diff --git a/.cirrus.yml b/.cirrus.yml index 585c265c3e5e5..cc6796b3faf69 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -11,10 +11,17 @@ gke_container: task: env: CIRRUS_WORKING_DIR: "/tmp/github_repo" - - replace_engine_script: | + ENGINE_PATH: "/tmp/clean_engine" + DEPOT_TOOLS: "/tmp/depot_tools" + PATH: "$DEPOT_TOOLS:$PATH" + depot_tools_script: + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS + gclient_sync_script: | + mkdir -p $ENGINE_PATH/src + echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_android_deps" : False, "download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient cd $ENGINE_PATH/src - rm -r flutter + rm -rf flutter + rm -rf out mv $CIRRUS_WORKING_DIR flutter gclient sync @@ -24,12 +31,34 @@ task: cd $ENGINE_PATH/src ./flutter/tools/gn --unoptimized ninja -C out/host_debug_unopt - test_host_script: cd $ENGINE_PATH/src && ./flutter/testing/run_tests.sh + test_host_script: cd $ENGINE_PATH/src && ./flutter/testing/run_tests.sh host_debug_unopt + - name: build_and_test_host_profile + compile_host_script: | + cd $ENGINE_PATH/src + ./flutter/tools/gn --runtime-mode profile --no-lto + ninja -C out/host_profile + test_host_script: cd $ENGINE_PATH/src && ./flutter/testing/run_tests.sh host_profile + - name: build_and_test_host_release + compile_host_script: | + cd $ENGINE_PATH/src + ./flutter/tools/gn --runtime-mode release --no-lto + ninja -C out/host_release + test_host_script: cd $ENGINE_PATH/src && ./flutter/testing/run_tests.sh host_release - name: build_android + get_android_sdk_script: | + echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient + cd $ENGINE_PATH/src + gclient sync + lint_host_script: | + cd $ENGINE_PATH/src/flutter/tools/android_lint + $ENGINE_PATH/src/third_party/dart/tools/sdks/dart-sdk/bin/pub get + $ENGINE_PATH/src/third_party/dart/tools/sdks/dart-sdk/bin/dart bin/main.dart compile_host_script: | cd $ENGINE_PATH/src ./flutter/tools/gn --android --unoptimized ninja -C out/android_debug_unopt + mkdir javadoc_tmp + ./flutter/tools/gen_javadoc.py --out-dir javadoc_tmp format_and_dart_test_task: container: @@ -37,12 +66,18 @@ format_and_dart_test_task: env: CIRRUS_WORKING_DIR: "/tmp/github_repo" - - replace_engine_script: | + DEPOT_TOOLS: "/tmp/depot_tools" + ENGINE_PATH: "/tmp/clean_engine" + PATH: "$DEPOT_TOOLS:$PATH" + depot_tools_script: + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git $DEPOT_TOOLS + gclient_sync_script: | + mkdir -p $ENGINE_PATH/src + echo 'solutions = [{"managed": False,"name": "src/flutter","url": "git@github.com:flutter/engine.git","deps_file": "DEPS", "custom_vars": {"download_android_deps" : False, "download_windows_deps" : False,},},]' > $ENGINE_PATH/.gclient cd $ENGINE_PATH/src - rm -r flutter - cp $CIRRUS_WORKING_DIR -r ./flutter + rm -rf flutter + rm -rf out + mv $CIRRUS_WORKING_DIR flutter gclient sync - format_script: cd $ENGINE_PATH/src/flutter && ./ci/format.sh build_script: cd $ENGINE_PATH/src/flutter && ./ci/build.sh diff --git a/.gitignore b/.gitignore index 730c7e4ce18fb..870f951370586 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ tags Thumbs.db .idea pubspec.lock +.vscode/ diff --git a/AUTHORS b/AUTHORS index ddcf4d7bd4e18..53ad592d738bf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,6 +4,8 @@ # Name/Organization Google Inc. +The Chromium Authors +The Fuchsia Authors Jim Simon Ali Bitek Jacob Greenfield diff --git a/BUILD.gn b/BUILD.gn index 8ea324031833f..b57d63f91fde2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,9 +1,15 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_root/common/config.gni") +# Whether to build the dartdevc sdk, libraries, and source files +# required for the flutter web sdk. +declare_args() { + full_dart_sdk = false +} + group("flutter") { testonly = true @@ -24,6 +30,12 @@ group("flutter") { "$flutter_root/frontend_server", "//third_party/dart:create_sdk", ] + + if (full_dart_sdk) { + public_deps += [ + "$flutter_root/web_sdk", + ] + } } } @@ -40,6 +52,7 @@ group("flutter") { "$flutter_root/runtime:runtime_unittests", "$flutter_root/shell/common:shell_unittests", "$flutter_root/shell/platform/embedder:embedder_unittests", + "$flutter_root/shell/platform/embedder:embedder_a11y_unittests", # TODO(cbracken) build these into a different kernel blob in the embedder tests and load that in a test in embedder_unittests "$flutter_root/synchronization:synchronization_unittests", "$flutter_root/third_party/txt:txt_unittests", ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6fe6a92aafd73..c5d94d44120ef 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,276 +1,22 @@ Contributing to the Flutter engine ================================== -[![Build Status](https://cirrus-ci.com/flutter/engine.svg)](https://cirrus-ci.com/flutter/engine) +[![Build Status](https://api.cirrus-ci.com/github/flutter/engine.svg)][build_status] -_See also: [Flutter's code of conduct](https://flutter.io/design-principles/#code-of-conduct)_ +_See also: [Flutter's code of conduct][code_of_conduct]_ Welcome ------- -This guide introduces you to building and contributing to the Flutter engine. -For an introduction to contributing to the Flutter framework, see [the equivalent -document in the framework's repository](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md). +For an introduction to contributing to Flutter, see [our contributor +guide][contrib_guide]. +For specific instructions regarding building Flutter's engine, see [Setting up +the Engine development environment][engine_dev_setup] on our wiki. Those +instructions are part of the broader onboarding instructions described in the +contributing guide. -I built it before. Remind me, how do I do it again? ---------------------------------------------------- - -If you have previously built the engine (i.e. your environment is already setup) and just want a refresher, then feel free to skip to one of the following sections: - -- [Build for Android](#android-cross-compiling-from-mac-or-linux) -- [Build for iOS](#ios-cross-compiling-from-mac) -- [Build flutter_tester](#desktop-mac-and-linux-for-tests) - -Otherwise, begin from the next section, which will help you prepare your environment. - -Things you will need --------------------- - - * Linux, Mac OS X, or Windows - * Windows doesn't support cross-compiling artifacts for Android or iOS - * Linux doesn't support cross-compiling artifacts for iOS - * git (used for source version control). - * An IDE. We recommend [IntelliJ with the Flutter plugin](https://flutter.io/intellij-ide/) or Xcode. - * An ssh client (used to authenticate with GitHub). - * Chromium's [depot_tools](http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up) (make sure it's in your path). We use the `gclient` tool from depot_tools. - * Python (used by many of our tools, including 'gclient'). - * On Mac OS X and Linux: curl and unzip (used by `gclient sync`). - * On Windows: Visual Studio (required for non-Googlers only). - * Recommended for Googlers: Goma for distributed builds. - -You do not need [Dart](https://www.dartlang.org/downloads/linux.html) installed, as a Dart tool chain is automatically downloaded as part of the "getting the code" step. Similarly for the Android SDK, it's downloaded by the `gclient sync` step below. - -Getting the code and configuring your environment -------------------------------------------------- - - * Ensure all the dependencies described in the previous section, in particular git, ssh, depot_tools, python, and curl, are installed. - * Fork `https://github.com/flutter/engine` into your own GitHub account. If you already have a fork, and are now installing a development environment on a new machine, make sure you've updated your fork so that you don't use stale configuration options from long ago. Do not use `git clone` to check out this repository; `gclient` will do it for you. - * If you haven't configured your machine with an SSH key that's known to github then - follow the directions here: https://help.github.com/articles/generating-ssh-keys/. - * Create an empty directory for your copy of the repository. For best results, call it `engine`: some of the tools assume this name when working across repositories. (They can be configured to use other names too, so this isn't a strict requirement.) - * Create a `.gclient` file in the `engine` directory with the following contents, replacing - `` with your GitHub account name: - -``` -solutions = [ - { - "managed": False, - "name": "src/flutter", - "url": "git@github.com:/engine.git", - "custom_deps": {}, - "deps_file": "DEPS", - "safesync_url": "", - }, -] -``` - - * `cd engine` (Change to the directory in which you put the `.gclient` file.) - * `gclient sync` This will fetch all the source code that Flutter depends on. Avoid interrupting this script, it can leave your repository in an inconsistent state that is tedious to clean up. (This step automatically runs `git clone`, among other things.) - * `cd src/flutter` (Change to the `flutter` directory of the `src` directory that `gclient sync` created in your `engine` directory.) - * `git remote add upstream git@github.com:flutter/engine.git` (So that you fetch from the master `flutter/engine` repository, not your clone, when running `git fetch` et al.) - * `cd ..` (Return to the `src` directory that `gclient sync` created in your `engine` directory.) - * If you're on Linux, run `sudo ./build/install-build-deps-android.sh` - * If you're on Linux, run `sudo ./build/install-build-deps.sh` - * If you're on Mac, install Oracle's Java JDK, version 1.7 or later. - * If you're on Mac, install `ant`: `brew install ant` - * If you're on Windows, install Visual Studio (non-Google developers only) - * If you're planning on working on the [buildroot](https://github.com/flutter/buildroot) repository as well, and have a local checkout of that repository, run the following commands in the `src` directory to update your git remotes accordingly: - - ```bash - git remote rename origin upstream - git remote add origin git@github.com:/buildroot.git - ``` - -Building and running the code ------------------------------ - -### General - -Most developers will use the `flutter` tool in [the main Flutter repository](https://github.com/flutter/flutter) for interacting with their built flutter/engine. To do so, the `flutter` tool accepts two global parameters `local-engine-src-path` and `local-engine`, a typical invocation would be: `--local-engine-src-path /path/to/engine/src --local-engine=android_debug_unopt`. - -Additionally if you've modified dart sources in `flutter/engine`, you'll need to add a `dependency_overrides` section to point to your modified `package:sky_engine` and `package:sky_services` to the `pubspec.yaml` for the flutter app you're using the custom engine with. A typical example would be: -``` -dependency_overrides: - sky_engine: - path: /path/to/flutter/engine/out/host_debug/gen/dart-pkg/sky_engine - sky_services: - path: /path/to/flutter/engine/out/host_debug/gen/dart-pkg/sky_services -``` -Depending on the platform you choose below, you will need to replace `host_debug` with the appropriate directory. - -### Android (cross-compiling from Mac or Linux) - -Run the following steps, from the `src` directory created in the steps above: - -* Update the Flutter Engine repo. - * `git pull upstream master` in `src/flutter` -* Update your dependencies - * `gclient sync` -* Prepare your build files - * `./flutter/tools/gn --android --unoptimized` for device-side executables - * `./flutter/tools/gn --android --android-cpu x86 --unoptimized` for x86 emulators - * `./flutter/tools/gn --android --android-cpu x64 --unoptimized` for x64 emulators - * `./flutter/tools/gn --unoptimized` for host-side executables -* Build your executables - * `ninja -C out/android_debug_unopt` for device-side executables - * `ninja -C out/android_debug_unopt_x86` for x86 emulators - * `ninja -C out/android_debug_unopt_x64` for x64 emulators - * `ninja -C out/host_debug_unopt` for host-side executables - * These commands can be combined. Ex: `ninja -C out/android_debug_unopt && ninja -C out/host_debug_unopt` - * For Googlers, consider also using the option `-j 1000` to parallelize the build using Goma. - -This builds a debug-enabled ("unoptimized") binary configured to run Dart in -checked mode ("debug"). There are other versions, [discussed on the wiki](https://github.com/flutter/flutter/wiki/Flutter's-modes). - -To run an example with your locally built binary, you'll also need to clone -[the main Flutter repository](https://github.com/flutter/flutter). See -[the instructions for contributing](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md) -to the main Flutter repository for detailed instructions. For your convenience, -the `engine` and `flutter` directories should be in the same parent directory. - -Once you've got everything set up, you can run an example using your locally -built engine by switching to that example's directory, running `pub get` to make -sure its dependencies have been downloaded, and using `flutter run` with an -explicit `--local-engine-src-path` pointing at the `engine/src` directory. Make -sure you have a device connected over USB and debugging enabled on that device: - - * `cd /path/to/flutter/examples/hello_world` - * `../../bin/flutter run --local-engine-src-path /path/to/engine/src --local-engine=android_debug_unopt` or `--local-engine=android_debug_unopt_x64` - -If you put the `engine` and `flutter` directories side-by-side, you can skip the -tedious `--local-engine-src-path` option and the `flutter` tool will -automatically determine the path. - -You can also specify a particular Dart file to run if you want to run an example -that doesn't have a `lib/main.dart` file using the `-t` command-line option. For -example, to run the `tabs.dart` example in the `examples/widgets` directory on a -connected Android device, from that directory you would run: - - * `flutter run --local-engine=android_debug_unopt -t tabs.dart` - -If you're going to be debugging crashes in the engine, make sure you add -`android:debuggable="true"` to the `` element in the -`android/AndroidManifest.xml` file for the Flutter app you are using -to test the engine. - -### iOS (cross-compiling from Mac) - -* Make sure you have Xcode 9.0+ installed. -* `git pull upstream master` in `src/flutter` to update the Flutter Engine repo. -* `gclient sync` to update dependencies. -* `./flutter/tools/gn --ios --unoptimized` to prepare build files for device-side executables (or `--ios --simulator --unoptimized` for simulator). - * For a discussion on the various flags and modes, [read this discussion](https://github.com/flutter/flutter/wiki/Flutter's-modes). - * This also produces an Xcode project for working with the engine source code at `out/ios_debug_unopt` -* `./flutter/tools/gn --unoptimized` to prepare the build files for host-side executables. -* `ninja -C out/ios_debug_unopt && ninja -C out/host_debug_unopt` to build all artifacts (use `out/ios_debug_sim_unopt` for Simulator). - * For Googlers, consider also using the option `-j 1000` to parallelize the build using Goma. - -Once the artifacts are built, you can start using them in your application by following these steps: -* `cd /path/to/flutter/examples/hello_world` -* `../../bin/flutter run --local-engine-src-path /path/to/engine/src --local-engine=ios_debug_unopt` or `--local-engine=ios_debug_sim_unopt` for simulator - * If you are debugging crashes in the engine, you can connect the `LLDB` debugger from `Xcode` by opening `ios/Runner.xcworkspace` and starting the application by clicking the Run button (CMD + R). - * To debug non crashing code, open Xcode with `ios/Runner.xcworkspace`, expand Flutter->Runner->Supporting Files->main.m in the Runner project. Put a breakpoint in main() then set your desired breakpoint in the engine in [lldb](https://lldb.llvm.org/tutorial.html) via `breakpoint set -...`. - - -### Desktop (Mac and Linux), for tests - -To run the tests, you should first clone [the main Flutter repository](https://github.com/flutter/flutter). -See [the instructions for contributing](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md) -to the main Flutter repository for detailed instructions. By default, Flutter will use the bundled version -of the engine. Follow the next steps to run tests using the locally-built engine: - -* `git pull upstream master` in `src/flutter` to update the Flutter Engine repo. -* `gclient sync` to update your dependencies. -* `./flutter/tools/gn --unoptimized` to prepare your build files. -* `ninja -C out/host_debug_unopt` to build a desktop unoptimized binary. - * For Googlers, consider also using the option `-j 1000` to parallelize the build using Goma. -* `--unoptimized` disables C++ compiler optimizations and does not strip debug symbols. You may skip the flag and invoke `ninja -C out/host_debug` if you would rather have the native components optimized. -* `flutter test --local-engine=host_debug_unopt` will run tests using the locally-built `flutter_tester`. - -### Desktop (gen_snapshot for Windows) - -You can only build selected binaries on Windows (mainly `gen_snapshot`). - -* Make sure you have Visual Studio installed (non-Googlers only). -* `git pull upstream master` in `src/flutter` to update the Flutter Engine repo. -* `gclient sync` to update your dependencies. -* `python .\flutter\tools\gn [--unoptimized] --runtime-mode=[debug|profile|release] [--android]` to prepare your build files. -* `ninja -C .\out\ gen_snapshot` to build. - -### Building all the builds that matter on Linux and Android - -The following script will update all the builds that matter if you're developing on Linux and testing on Android and created the `.gclient` file in `~/dev/engine`: - -```bash -set -ex - -cd ~/dev/engine/src/flutter -git fetch upstream -git rebase upstream/master -gclient sync -cd .. - -flutter/tools/gn --unoptimized --runtime-mode=debug -flutter/tools/gn --android --unoptimized --runtime-mode=debug -flutter/tools/gn --android --unoptimized --runtime-mode=profile -flutter/tools/gn --android --unoptimized --runtime-mode=release -flutter/tools/gn --android --runtime-mode=debug -flutter/tools/gn --android --runtime-mode=profile -flutter/tools/gn --android --runtime-mode=release - -cd out -find . -mindepth 1 -maxdepth 1 -type d | xargs -n 1 sh -c 'ninja -C $0 || exit 255' -``` - - -Contributing code ------------------ - -We gladly accept contributions via GitHub pull requests. See [the wiki](https://github.com/flutter/engine/wiki) for -information about the engine's architecture. - -To start working on a patch: - - * Make sure you are in the `engine/src/flutter` directory. - * `git fetch upstream` - * `git checkout upstream/master -b name_of_your_branch` - * Hack away. - * Please peruse our [style guides](https://flutter.io/style-guide/) and - [design principles](https://flutter.io/design-principles/) before - working on anything non-trivial. These guidelines are intended to - keep the code consistent and avoid common pitfalls. - * C, C++, and Objective-C code should be formatted with `clang-format` before - submission (use `buildtools//clang/bin/clang-format --style=file -i`). - * `git commit -a -m ""` - * `git push origin name_of_your_branch` - -To send us a pull request: - - * `git pull-request` (if you are using [Hub](http://github.com/github/hub/)) or - go to `https://github.com/flutter/engine` and click the - "Compare & pull request" button - -Once you've gotten an LGTM from a project maintainer, submit your changes to the -`master` branch using one of the following methods: - -* Wait for one of the project maintainers to submit it for you -* Click the green "Merge pull request" button on the GitHub UI of your pull - request (requires commit access) -* `git push upstream name_of_your_branch:master` (requires commit access) - -Then, make sure it doesn't make our tree catch fire by watching [the waterfall](https://build.chromium.org/p/client.flutter/waterfall). The waterfall runs -slightly different tests than Travis, so it's possible for the tree to go red even if -Travis did not. If that happens, please immediately revert your change. Do not check -anything in while the tree is red unless you are trying to resolve the problem. - -Please make sure all your checkins have detailed commit messages explaining the patch. -If you made multiple commits for a single pull request, either make sure each one has a detailed -message explaining that specific commit, or squash your commits into one single checkin with a -detailed message before sending the pull request. - -You must complete the -[Contributor License Agreement](https://cla.developers.google.com/clas). -You can do this online, and it only takes a minute. -If you've never submitted code before, you must add your (or your -organization's) name and contact info to the [AUTHORS](AUTHORS) file. +[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/wiki/Setting-up-the-Engine-development-environment +[engine_dev_setup]: https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment diff --git a/DEPS b/DEPS index 76015248c0e57..213da9773e38f 100644 --- a/DEPS +++ b/DEPS @@ -9,7 +9,7 @@ # .gclient file (the parent of 'src'). # # Then commit .DEPS.git locally (gclient doesn't like dirty trees) and run -# gclient sync +# gclient sync.. # Verify the thing happened you wanted. Then revert your .DEPS.git change # DO NOT CHECK IN CHANGES TO .DEPS.git upstream. It will be automatically # updated by a bot when you modify this one. @@ -23,7 +23,7 @@ vars = { 'fuchsia_git': 'https://fuchsia.googlesource.com', 'github_git': 'https://github.com', 'skia_git': 'https://skia.googlesource.com', - 'skia_revision': '4b7b2ceb4ad93861c2ae6b4fabce13f187ec4b93', + 'skia_revision': 'e51181387f763864acf5be828831b2d7c2b29065', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the @@ -31,51 +31,50 @@ 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': 'ac6d4f7e653deba11d4836768376537893a9e9d6', + 'dart_revision': '05f3b12960fbe1808d367df18b86095b1baed369', # 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.4.4', 'dart_async_tag': '2.0.8', - 'dart_bazel_worker_tag': '0.1.14', + 'dart_bazel_worker_tag': 'bazel_worker-v0.1.20', 'dart_boolean_selector_tag': '1.0.4', - 'dart_boringssl_gen_rev': 'fc47eaa1a245d858bae462cd64d4155605b850ea', - 'dart_boringssl_rev': '189270cd190267f5bd60cfe8f8ce7a61d07ba6f4', + 'dart_boringssl_gen_rev': 'bbf52f18f425e29b1185f2f6753bec02ed8c5880', + 'dart_boringssl_rev': '702e2b6d3831486535e958f262a05c75a5cb312e', 'dart_charcode_tag': 'v1.1.2', 'dart_cli_util_rev': '4ad7ccbe3195fd2583b30f86a86697ef61e80f41', 'dart_collection_tag': '1.14.11', 'dart_convert_tag': '2.0.2', 'dart_crypto_tag': '2.0.6', 'dart_csslib_tag': '0.14.4+1', - 'dart_dart2js_info_tag': '0.5.6+4', - 'dart_dart_style_tag': '1.2.0', - 'dart_dartdoc_tag': 'v0.23.1', - 'dart_fixnum_tag': '0.10.8', + 'dart_dart2js_info_tag': '0.6.0', + 'dart_dart_style_tag': '1.2.4', + 'dart_dartdoc_tag': 'v0.28.2', + 'dart_fixnum_tag': '0.10.9', 'dart_glob_tag': '1.1.7', - 'dart_html_tag': '0.13.3+2', + 'dart_html_tag': '0.13.4+1', 'dart_http_multi_server_tag': '2.0.5', - 'dart_http_parser_tag': '3.1.1', + 'dart_http_parser_tag': '3.1.3', 'dart_http_retry_tag': '0.1.1', - 'dart_http_tag': '0.11.3+17', + 'dart_http_tag': '0.12.0', 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.68', + 'dart_linter_tag': '0.1.83', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.0.2', 'dart_matcher_tag': '0.12.3', 'dart_mime_tag': '0.9.6+2', 'dart_mockito_tag': 'd39ac507483b9891165e422ec98d9fb480037c8b', - 'dart_mustache4dart_tag': 'v2.1.2', + 'dart_mustache_tag': '5e81b12215566dbe2473b2afd01a8a8aedd56ad9', 'dart_oauth2_tag': '1.2.1', 'dart_observatory_pub_packages_rev': '0894122173b0f98eb08863a7712e78407d4477bc', 'dart_package_config_tag': '1.0.5', - 'dart_package_resolver_tag': '1.0.4', + 'dart_package_resolver_tag': '1.0.10', 'dart_path_tag': '1.6.2', - 'dart_plugin_tag': 'f5b4b0e32d1406d62daccea030ba6457d14b1c47', 'dart_pool_tag': '1.3.6', - 'dart_protobuf_tag': '0.9.0', - 'dart_pub_rev': '9f00679ef47bc79cadc18e143720ade6c06c0100', + 'dart_protobuf_rev': '0c77167b16d00b561a6055bfe26690af7f26ae88', + 'dart_pub_rev': '8c363fe26f059c3063f1129adbb3c4e22a8ce954', 'dart_pub_semver_tag': '1.4.2', 'dart_quiver_tag': '2.0.0+1', 'dart_resource_rev': '2.1.5', @@ -86,25 +85,30 @@ vars = { 'dart_shelf_web_socket_tag': '0.2.2+3', 'dart_source_map_stack_trace_tag': '1.1.5', 'dart_source_maps_tag': '8af7cc1a1c3a193c1fba5993ce22a546a319c40e', - 'dart_source_span_tag': '1.4.1', + 'dart_source_span_tag': '1.5.5', 'dart_stack_trace_tag': '1.9.3', 'dart_stream_channel_tag': '1.6.8', 'dart_string_scanner_tag': '1.0.3', 'dart_term_glyph_tag': '1.0.1', 'dart_test_reflective_loader_tag': '0.1.8', - 'dart_test_tag': '1.0.0', - 'dart_tuple_tag': 'v1.0.1', + 'dart_test_tag': '1.3.4', 'dart_typed_data_tag': '1.1.6', 'dart_usage_tag': '3.4.0', 'dart_utf_tag': '0.9.0+5', - 'dart_watcher_rev': '0.9.7+10', + 'dart_watcher_rev': '0.9.7+12', 'dart_web_socket_channel_tag': '1.0.9', 'dart_yaml_tag': '2.1.15', # Build bot tooling for iOS 'ios_tools_revision': '69b7c1b160e7107a6a98d948363772dc9caea46f', - 'buildtools_revision': 'c1408453246f0475547b6fe634c2f3dad71c6457', + 'buildtools_revision': 'bac220c15490dcf7b7d8136f75100bbc77e8d217', + + # Checkout Android dependencies only on platforms where we build for Android targets. + 'download_android_deps': 'host_os == "mac" or host_os == "linux"', + + # Checkout Windows dependencies only if we are building on Windows. + 'download_windows_deps' : 'host_os == "win"', } # Only these hosts are allowed for dependencies in this DEPS file. @@ -117,7 +121,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '378efdc3507cef3204a164132cdeab04ea8e907e', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'ad098fcdb1b41fcf57c22e46e898d0dc04d3a178', # Fuchsia compatibility # @@ -126,19 +130,28 @@ deps = { # and not have to specific specific hashes. 'src/third_party/tonic': - Var('fuchsia_git') + '/tonic' + '@' + '077be256142ede39a271385907faadf4fcc62a4d', + Var('fuchsia_git') + '/tonic' + '@' + '02f9d8dd18dd259e3c5efe1fbe713819a730b6e0', 'src/third_party/benchmark': Var('fuchsia_git') + '/third_party/benchmark' + '@' + '21f1eb3fe269ea43eba862bf6b699cde46587ade', 'src/third_party/googletest': - Var('fuchsia_git') + '/third_party/googletest' + '@' + '2072b0053d3537fa5e8d222e34c759987aae1320', + Var('fuchsia_git') + '/third_party/googletest' + '@' + '50a5a788420bd44501a75562de8936fd7ac32117', 'src/third_party/rapidjson': Var('fuchsia_git') + '/third_party/rapidjson' + '@' + '32d07c55db1bb6c2ae17cba4033491a667647753', 'src/third_party/harfbuzz': - Var('fuchsia_git') + '/third_party/harfbuzz' + '@' + 'd837034f09a957faf2814002e8ebd81da6151d1b', + Var('fuchsia_git') + '/third_party/harfbuzz' + '@' + '02caec6c1c6ad996666788b8e920ccaec8b385e5', + + 'src/third_party/libcxx': + Var('fuchsia_git') + '/third_party/libcxx' + '@' + 'c5a5fa59789213c7dae68d2e51cb28ef681d8257', + + 'src/third_party/libcxxabi': + Var('fuchsia_git') + '/third_party/libcxxabi' + '@' + '1a9753522f1ae8d72848d365902f39e0d3d59a39', + + 'src/third_party/glfw': + Var('fuchsia_git') + '/third_party/glfw' + '@' + '999f3556fdd80983b10051746264489f2cb1ef16', # Chromium-style # @@ -253,8 +266,8 @@ deps = { 'src/third_party/dart/third_party/pkg/mockito': Var('dart_git') + '/mockito.git' + '@' + Var('dart_mockito_tag'), - 'src/third_party/dart/third_party/pkg/mustache4dart': - Var('chromium_git') + '/external/github.com/valotas/mustache4dart' + '@' + Var('dart_mustache4dart_tag'), + 'src/third_party/dart/third_party/pkg/mustache': + Var('dart_git') + '/external/github.com/xxgreg/mustache' + '@' + Var('dart_mustache_tag'), 'src/third_party/dart/third_party/pkg_tested/package_config': Var('dart_git') + '/package_config.git' + '@' + Var('dart_package_config_tag'), @@ -268,11 +281,8 @@ deps = { 'src/third_party/dart/third_party/pkg/pool': Var('dart_git') + '/pool.git' + '@' + Var('dart_pool_tag'), - 'src/third_party/dart/third_party/pkg/plugin': - Var('dart_git') + '/plugin.git' + '@' + Var('dart_plugin_tag'), - 'src/third_party/dart/third_party/pkg/protobuf': - Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_tag'), + Var('dart_git') + '/protobuf.git' + '@' + Var('dart_protobuf_rev'), 'src/third_party/dart/third_party/pkg/pub_semver': Var('dart_git') + '/pub_semver.git' + '@' + Var('dart_pub_semver_tag'), @@ -331,9 +341,6 @@ deps = { 'src/third_party/dart/third_party/pkg/test': Var('dart_git') + '/test.git' + '@' + Var('dart_test_tag'), - 'src/third_party/dart/third_party/pkg/tuple': - Var('dart_git') + '/tuple.git' + '@' + Var('dart_tuple_tag'), - 'src/third_party/dart/third_party/pkg/utf': Var('dart_git') + '/utf.git' + '@' + Var('dart_utf_tag'), @@ -353,7 +360,7 @@ deps = { Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', 'src/third_party/freetype2': - Var('fuchsia_git') + '/third_party/freetype2' + '@' + '6581fd3e9c8645f01c0d51e4f53893f5391f2bf3', + Var('fuchsia_git') + '/third_party/freetype2' + '@' + 'a10b062df0c8958d69377aa04ea6554a9961a111', 'src/third_party/root_certificates': Var('dart_git') + '/root_certificates.git' + '@' + Var('dart_root_certificates_rev'), @@ -367,20 +374,74 @@ deps = { 'src/third_party/libwebp': Var('chromium_git') + '/webm/libwebp.git' + '@' + '0.6.0', + 'src/third_party/wuffs': + Var('fuchsia_git') + '/third_party/wuffs' + '@' + 'a71538baa8f1f4053176c0d9f31bc12fd4e8e71b', + 'src/third_party/gyp': Var('chromium_git') + '/external/gyp.git' + '@' + '4801a5331ae62da9769a327f11c4213d32fb0dad', - # Headers for Vulkan 1.0 + # Headers for Vulkan 1.1 'src/third_party/vulkan': - Var('github_git') + '/KhronosGroup/Vulkan-Docs.git' + '@' + 'e29c2489e238509c41aeb8c7bce9d669a496344b', + Var('github_git') + '/KhronosGroup/Vulkan-Docs.git' + '@' + 'v1.1.91', 'src/third_party/pkg/when': Var('dart_git') + '/when.git' + '@' + '0.2.0', -} -recursedeps = [ - 'src/buildtools', -] + 'src/third_party/android_tools/ndk': { + 'packages': [ + { + 'package': 'flutter/android/ndk/${{platform}}', + 'version': 'version:r19b' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_tools/sdk/build-tools': { + 'packages': [ + { + 'package': 'flutter/android/sdk/build-tools/${{platform}}', + 'version': 'version:28.0.3' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_tools/sdk/platform-tools': { + 'packages': [ + { + 'package': 'flutter/android/sdk/platform-tools/${{platform}}', + 'version': 'version:28.0.1' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_tools/sdk/platforms': { + 'packages': [ + { + 'package': 'flutter/android/sdk/platforms', + 'version': 'version:28r6' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_tools/sdk/tools': { + 'packages': [ + { + 'package': 'flutter/android/sdk/tools/${{platform}}', + 'version': 'version:26.1.1' + } + ], + 'condition': 'download_android_deps', + 'dep_type': 'cipd', + }, +} hooks = [ { @@ -397,20 +458,20 @@ hooks = [ { # Update the Windows toolchain if necessary. 'name': 'win_toolchain', + 'condition': 'download_windows_deps', 'pattern': '.', 'action': ['python', 'src/build/vs_toolchain.py', 'update'], }, { - 'name': 'download_android_tools', + # Pull prebuilt dart sdk. + 'name': 'dart', 'pattern': '.', - 'action': [ - 'python', - 'src/tools/android/download_android_tools.py', - ], + 'action': ['python', 'src/tools/dart/update.py'], }, { 'name': 'download_android_support', 'pattern': '.', + 'condition': 'download_android_deps', 'action': [ 'python', 'src/flutter/tools/android_support/download_android_support.py', @@ -424,12 +485,6 @@ hooks = [ 'src/tools/buildtools/update.py', ], }, - { - # Pull dart sdk if needed - 'name': 'dart', - 'pattern': '.', - 'action': ['python', 'src/tools/dart/update.py'], - }, { 'name': 'generate_package_files', 'pattern': '.', @@ -448,18 +503,19 @@ hooks = [ ], }, { - "name": "7zip", - "pattern": ".", - "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': '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', ], }, ] diff --git a/LICENSE b/LICENSE index 972bb2edb099e..c6823b81eb845 100644 --- a/LICENSE +++ b/LICENSE @@ -1,27 +1,25 @@ -// 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. +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 7f2d8a53c4850..8eedc223a79fb 100644 --- a/README.md +++ b/README.md @@ -14,26 +14,13 @@ toolchain. Most developers will interact with Flutter via the [Flutter Framework](https://github.com/flutter/flutter), which provides a modern, reactive framework, and a rich set of platform, layout and foundation widgets. - -_Flutter is still under development and we continue to add -features._ However, it is ready for use by early adopters who are willing to deal -with the odd wrinkle or two along the way. We hope you try it out and send -us [feedback](mailto:flutter-dev@googlegroups.com). - - - For information about using Flutter to build apps, please see - the [getting started guide](https://flutter.io/getting-started/). - - - For information about contributing to the Flutter framework, please see - [the main Flutter repository](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md). - - - For information about contributing code to the engine itself, please see - [CONTRIBUTING.md](CONTRIBUTING.md). - - - For information about the engine's architecture, please see - [the wiki](https://github.com/flutter/engine/wiki). - -Community ---------- - -Join us in our [Gitter chat room](https://gitter.im/flutter/flutter) or join our mailing list, -[flutter-dev@googlegroups.com](https://groups.google.com/forum/#!forum/flutter-dev). +If you are new to Flutter, then you will find more general information +on the Flutter project, including tutorials and samples, on our Web +site at [flutter.io](https://flutter.io). For specific information +about Flutter's APIs, consider our API reference which can be found at +the [docs.flutter.io](https://docs.flutter.io/). + +If you intend to contribute to Flutter, welcome! You are encouraged to +start with [our contributor +guide](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md), +which helps onboard new team members. diff --git a/analysis_options.yaml b/analysis_options.yaml index 6420a5457adca..699ca970ef474 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,9 +10,6 @@ # private fields, especially on the Window object): analyzer: - language: - enableStrictCallChecks: true - enableSuperMixins: true strong-mode: implicit-dynamic: false errors: @@ -20,6 +17,7 @@ analyzer: missing_required_param: warning # treat missing returns as a warning (not a hint) missing_return: warning + native_function_body_in_non_sdk_code: ignore # allow having TODOs in the code todo: ignore # `flutter analyze` (without `--watch`) just ignores directories @@ -94,7 +92,6 @@ linter: # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - - prefer_bool_in_asserts - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors diff --git a/assets/BUILD.gn b/assets/BUILD.gn index f28e9bdd0a6ea..f04fe40e1e90a 100644 --- a/assets/BUILD.gn +++ b/assets/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/assets/asset_manager.cc b/assets/asset_manager.cc index 2155d24a78a26..610bd10a14a3f 100644 --- a/assets/asset_manager.cc +++ b/assets/asset_manager.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,10 +7,6 @@ #include "flutter/assets/directory_asset_bundle.h" #include "flutter/fml/trace_event.h" -#ifdef ERROR -#undef ERROR -#endif - namespace blink { AssetManager::AssetManager() = default; @@ -39,7 +35,8 @@ std::unique_ptr AssetManager::GetAsMapping( if (asset_name.size() == 0) { return nullptr; } - TRACE_EVENT0("flutter", "AssetManager::GetAsMapping"); + TRACE_EVENT1("flutter", "AssetManager::GetAsMapping", "name", + asset_name.c_str()); for (const auto& resolver : resolvers_) { auto mapping = resolver->GetAsMapping(asset_name); if (mapping != nullptr) { diff --git a/assets/asset_manager.h b/assets/asset_manager.h index 4ab58ed0f2bf7..42ffa9b1053c4 100644 --- a/assets/asset_manager.h +++ b/assets/asset_manager.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,9 +15,12 @@ namespace blink { -class AssetManager final : public AssetResolver, - public fml::RefCountedThreadSafe { +class AssetManager final : public AssetResolver { public: + AssetManager(); + + ~AssetManager() override; + void PushFront(std::unique_ptr resolver); void PushBack(std::unique_ptr resolver); @@ -32,13 +35,7 @@ class AssetManager final : public AssetResolver, private: std::deque> resolvers_; - AssetManager(); - - ~AssetManager(); - FML_DISALLOW_COPY_AND_ASSIGN(AssetManager); - FML_FRIEND_MAKE_REF_COUNTED(AssetManager); - FML_FRIEND_REF_COUNTED_THREAD_SAFE(AssetManager); }; } // namespace blink diff --git a/assets/asset_resolver.h b/assets/asset_resolver.h index d0a68f0bf2cd8..c49b8fb99a868 100644 --- a/assets/asset_resolver.h +++ b/assets/asset_resolver.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/assets/directory_asset_bundle.cc b/assets/directory_asset_bundle.cc index e801e04ad76e9..2f39535314d91 100644 --- a/assets/directory_asset_bundle.cc +++ b/assets/directory_asset_bundle.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/assets/directory_asset_bundle.h b/assets/directory_asset_bundle.h index f61ac147ff1fb..041c301e8730a 100644 --- a/assets/directory_asset_bundle.h +++ b/assets/directory_asset_bundle.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/assets/zip_asset_store.cc b/assets/zip_asset_store.cc index 7823f6e594396..97228f96087a8 100644 --- a/assets/zip_asset_store.cc +++ b/assets/zip_asset_store.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,8 +22,8 @@ void UniqueUnzipperTraits::Free(void* file) { unzClose(file); } -ZipAssetStore::ZipAssetStore(std::string file_path) - : file_path_(std::move(file_path)) { +ZipAssetStore::ZipAssetStore(std::string file_path, std::string directory) + : file_path_(std::move(file_path)), directory_(std::move(directory)) { BuildStatCache(); } @@ -41,9 +41,10 @@ bool ZipAssetStore::IsValid() const { // |blink::AssetResolver| std::unique_ptr ZipAssetStore::GetAsMapping( const std::string& asset_name) const { - TRACE_EVENT0("flutter", "ZipAssetStore::GetAsMapping"); - auto found = stat_cache_.find(asset_name); + TRACE_EVENT1("flutter", "ZipAssetStore::GetAsMapping", "name", + asset_name.c_str()); + auto found = stat_cache_.find(directory_ + "/" + asset_name); if (found == stat_cache_.end()) { return nullptr; } diff --git a/assets/zip_asset_store.h b/assets/zip_asset_store.h index f801e0b0cca25..b8b66a7d4f5ea 100644 --- a/assets/zip_asset_store.h +++ b/assets/zip_asset_store.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -23,7 +23,7 @@ using UniqueUnzipper = fml::UniqueObject; class ZipAssetStore final : public AssetResolver { public: - ZipAssetStore(std::string file_path); + ZipAssetStore(std::string file_path, std::string directory); ~ZipAssetStore() override; @@ -35,7 +35,9 @@ class ZipAssetStore final : public AssetResolver { : file_pos(p_file_pos), uncompressed_size(p_uncompressed_size) {} }; - std::string file_path_; + const std::string file_path_; + const std::string directory_; + mutable std::map stat_cache_; // |blink::AssetResolver| diff --git a/benchmarking/BUILD.gn b/benchmarking/BUILD.gn index 7fa479c957daf..7d49d06c4e7d6 100644 --- a/benchmarking/BUILD.gn +++ b/benchmarking/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter 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/benchmarking/benchmarking.cc b/benchmarking/benchmarking.cc index 12588208c6579..c8e5af7ea3dd5 100644 --- a/benchmarking/benchmarking.cc +++ b/benchmarking/benchmarking.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/benchmarking/benchmarking.h b/benchmarking/benchmarking.h index 4bc8cd535ef54..d84004f4d8b01 100644 --- a/benchmarking/benchmarking.h +++ b/benchmarking/benchmarking.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/rules.gni b/build/dart/rules.gni index b4b8df6253680..d1b66255e987d 100644 --- a/build/dart/rules.gni +++ b/build/dart/rules.gni @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_package_name.py b/build/dart/tools/dart_package_name.py index 5817945e49cba..255ef5be7a13a 100755 --- a/build/dart/tools/dart_package_name.py +++ b/build/dart/tools/dart_package_name.py @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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 34396b827685d..18d2e6951faf8 100755 --- a/build/dart/tools/dart_pkg.py +++ b/build/dart/tools/dart_pkg.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/generate_coverage.py b/build/generate_coverage.py new file mode 100755 index 0000000000000..fb6ad9d74514c --- /dev/null +++ b/build/generate_coverage.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# Copyright 2013 The Flutter 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 sys +import subprocess +import os +import argparse +import errno +import shutil + +def GetLLVMBinDirectory(): + buildtool_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../buildtools") + platform_dir = "" + if sys.platform.startswith('linux'): + platform_dir = "linux-x64" + elif sys.platform == 'darwin': + platform_dir = "mac-x64" + else: + raise Exception("Unknown/Unsupported platform.") + llvm_bin_dir = os.path.abspath(os.path.join(buildtool_dir, platform_dir, "clang/bin")) + if not os.path.exists(llvm_bin_dir): + raise Exception("LLVM directory %s double not be located." % llvm_bin_dir) + return llvm_bin_dir + + +def MakeDirs(new_dir): + """A wrapper around os.makedirs() that emulates "mkdir -p".""" + try: + os.makedirs(new_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + +def RemoveIfExists(path): + if os.path.isdir(path) and not os.path.islink(path): + shutil.rmtree(path) + elif os.path.exists(path): + os.remove(path) + +def main(): + parser = argparse.ArgumentParser(); + + parser.add_argument('-t', '--tests', nargs='+', dest='tests', + required=True, help='The unit tests to run and gather coverage data on.') + parser.add_argument('-o', '--output', dest='output', + 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.') + + args = parser.parse_args() + + output = os.path.abspath(args.output) + + MakeDirs(output) + + generate_all_reports = args.format == "all" + + raw_profiles = [] + binaries = [] + + # Run all unit tests and collect raw profiles. + for test in args.tests: + absolute_test_path = os.path.abspath(test) + + if not os.path.exists(absolute_test_path): + print("Path %s does not exist." % absolute_test_path) + return -1 + + 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) + + subprocess.check_call([absolute_test_path], env={ + "LLVM_PROFILE_FILE": raw_profile + }) + + if not os.path.exists(raw_profile): + print("Could not find raw profile data for unit test run %s." % test) + print("Did you build with the --coverage flag?") + return -1 + + raw_profiles.append(raw_profile) + + if len(raw_profiles) == 0: + print("No raw profiles could be generated.") + return -1 + + binaries_flag = [] + for binary in binaries: + binaries_flag.append('-object') + binaries_flag.append(binary) + + llvm_bin_dir = GetLLVMBinDirectory() + + # Merge all raw profiles into a single profile. + profdata_binary = os.path.join(llvm_bin_dir, "llvm-profdata") + + print("Merging %d raw profile(s) into single profile." % len(raw_profiles)) + merged_profile_path = os.path.join(output, "all.profile") + RemoveIfExists(merged_profile_path) + merge_command = [profdata_binary, "merge", "-sparse"] + raw_profiles + ["-o", merged_profile_path] + subprocess.check_call(merge_command) + print("Done.") + + if not os.path.exists(merged_profile_path): + print("Could not generate or find merged profile %s." % merged_profile_path) + return -1 + + llvm_cov_binary = os.path.join(llvm_bin_dir, "llvm-cov") + instr_profile_flag = "-instr-profile=%s" % merged_profile_path + ignore_flags = "-ignore-filename-regex=third_party|unittest|fixture" + + # Generate the HTML report if specified. + if generate_all_reports or args.format == 'html': + print("Generating HTML report.") + show_command = [llvm_cov_binary, "show"] + binaries_flag + [ + instr_profile_flag, + "-format=html", + "-output-dir=%s" % output, + "-tab-size=2", + ignore_flags, + ] + subprocess.check_call(show_command) + print("Done.") + + # Generate a report summary if specified. + if generate_all_reports or args.format == 'summary': + print("Generating a summary report.") + report_command = [llvm_cov_binary, "report"] + binaries_flag + [ + instr_profile_flag, + ignore_flags, + ] + subprocess.check_call(report_command) + print("Done.") + + # Generate a lcov summary if specified. + if generate_all_reports or args.format == 'lcov': + print("Generating LCOV report.") + lcov_file = os.path.join(output, 'coverage.lcov') + RemoveIfExists(lcov_file) + lcov_command = [llvm_cov_binary, "export"] + binaries_flag + [ + instr_profile_flag, + ignore_flags, + "-format=lcov", + ] + with open(lcov_file, 'w') as lcov_redirect: + subprocess.check_call(lcov_command, stdout=lcov_redirect) + print("Done.") + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/build/git_revision.py b/build/git_revision.py index bc58320306d14..29d62e796a347 100755 --- a/build/git_revision.py +++ b/build/git_revision.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -31,7 +31,6 @@ def main(): '-C', repository, 'rev-parse', - '--short', 'HEAD', ]) diff --git a/build/install-build-deps-linux-desktop.sh b/build/install-build-deps-linux-desktop.sh new file mode 100755 index 0000000000000..ef24ad5d95cf5 --- /dev/null +++ b/build/install-build-deps-linux-desktop.sh @@ -0,0 +1,11 @@ +#!/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. + +# Installs the dependencies necessary to build the Linux Flutter shell, beyond +# those installed by install-build-deps.sh. + +set -e + +sudo apt-get -y install libgtk-3-dev libx11-dev diff --git a/ci/analyze.sh b/ci/analyze.sh index c86d506858e0c..6cf2206b99e40 100755 --- a/ci/analyze.sh +++ b/ci/analyze.sh @@ -2,15 +2,9 @@ echo "Analyzing dart:ui library..." RESULTS=`dartanalyzer \ --options flutter/analysis_options.yaml \ - out/host_debug_unopt/gen/sky/bindings/dart_ui/ui.dart \ + "$1out/host_debug_unopt/gen/sky/bindings/dart_ui/ui.dart" \ 2>&1 \ - | grep -v "Native functions can only be declared in the SDK and code that is loaded through native extensions" \ - | grep -Ev "The function '.+' (is not|isn't) used" \ - | grep -Ev "The top level variable '.+' isn't used" \ - | grep -Ev "Undefined name 'main'" \ - | grep -v "The library 'dart:_internal' is internal" \ - | grep -Ev "Unused import.+ui\.dart" \ - | grep -Ev "[0-9]+ errors.*found\." \ + | grep -Ev "No issues found!" \ | grep -Ev "Analyzing.+out/host_debug_unopt/gen/sky/bindings/dart_ui/ui\.dart"` echo "$RESULTS" diff --git a/ci/build.sh b/ci/build.sh index 57699445121c1..cd0024147ba54 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -12,3 +12,9 @@ ninja -C out/host_debug_unopt generate_dart_ui # Analyze the dart UI flutter/ci/analyze.sh flutter/ci/licenses.sh + +# Check that dart libraries conform +cd flutter/web_sdk +pub get +cd .. +dart web_sdk/test/api_conform_test.dart diff --git a/ci/docker/build/Dockerfile b/ci/docker/build/Dockerfile index 81655f7adf228..7307ac0a33470 100644 --- a/ci/docker/build/Dockerfile +++ b/ci/docker/build/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:14.04 +FROM ubuntu:16.04 ENV DEPOT_TOOLS_PATH $HOME/depot_tools ENV ENGINE_PATH $HOME/engine @@ -17,9 +17,10 @@ RUN gclient sync WORKDIR $ENGINE_PATH/src RUN ./build/install-build-deps.sh --no-prompt RUN ./build/install-build-deps-android.sh --no-prompt +RUN ./flutter/build/install-build-deps-linux-desktop.sh WORKDIR $HOME/dart -RUN wget https://storage.googleapis.com/dart-archive/channels/dev/release/2.0.0-dev.63.0/sdk/dartsdk-linux-x64-release.zip +RUN wget https://storage.googleapis.com/dart-archive/channels/dev/release/2.1.0-dev.7.1/sdk/dartsdk-linux-x64-release.zip RUN unzip dartsdk-linux-x64-release.zip ENV PATH $PATH:$HOME/dart/dart-sdk/bin diff --git a/ci/format.sh b/ci/format.sh index de00b6d3a67b7..93f0be6c47618 100755 --- a/ci/format.sh +++ b/ci/format.sh @@ -57,7 +57,7 @@ done if [[ $FAILED_CHECKS -ne 0 ]]; then echo "" - echo "ERROR: Some files are formatted incorrectly. To fix, apply diffs above via patch -p0." + echo "ERROR: Some files are formatted incorrectly. To fix, run \`./ci/format.sh | patch -p0\` from the flutter/engine/src/flutter directory." exit 1 fi diff --git a/ci/licenses.sh b/ci/licenses.sh index 472285b5b88c3..fe56fc33148b9 100755 --- a/ci/licenses.sh +++ b/ci/licenses.sh @@ -8,6 +8,7 @@ echo "Verifying license script is still happy..." for f in out/license_script_output/licenses_*; do if ! cmp -s flutter/ci/licenses_golden/$(basename $f) $f then + echo "============================= ERROR =============================" echo "License script got different results than expected for $f." echo "Please rerun the licenses script locally to verify that it is" echo "correctly catching any new licenses for anything you may have" @@ -21,5 +22,44 @@ for f in out/license_script_output/licenses_*; do fi done +echo "Verifying license tool signature..." +if ! cmp -s flutter/ci/licenses_golden/tool_signature out/license_script_output/tool_signature +then + echo "============================= ERROR =============================" + echo "The license tool signature has changed. This is expected when" + echo "there have been changes to the license tool itself. Licenses have" + echo "been re-computed for all components. If only the license script has" + echo "changed, no diffs are typically expected in the output of the" + echo "script. Verify the output, and if it looks correct, update the" + echo "license tool signature golden file:" + echo " ci/licences_golden/tool_signature" + echo "For more information, see the script in:" + echo " https://github.com/flutter/engine/tree/master/tools/licenses" + echo "" + diff -U 6 flutter/ci/licenses_golden/tool_signature out/license_script_output/tool_signature + exit 1 +fi + +echo "Checking license count in licenses_flutter..." +actualLicenseCount=`tail -n 1 flutter/ci/licenses_golden/licenses_flutter | tr -dc '0-9'` +expectedLicenseCount=3 # When changing this number: Update the error message below as well describing all expected license types. + +if [ "$actualLicenseCount" -ne "$expectedLicenseCount" ] +then + echo "=============================== ERROR ===============================" + echo "The total license count in flutter/ci/licenses_golden/licenses_flutter" + echo "changed from $expectedLicenseCount to $actualLicenseCount." + echo "It's very likely that this is an unintentional change. Please" + echo "double-check that all newly added files have a BSD-style license" + echo "header with the following copyright:" + echo " Copyright 2013 The Flutter Authors. All rights reserved." + echo "Files in 'third_party/bsdiff' may have the following copyright instead:" + echo " Copyright 2003-2005 Colin Percival. All rights reserved." + echo "Files in 'third_party/txt' may have an Apache license header instead." + echo "If you're absolutely sure that the change in license count is" + echo "intentional, update 'flutter/ci/licenses.sh' with the new count." + exit 1 +fi + echo "Licenses are as expected." exit 0 diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 573f866548a21..f5259b505f734 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -4,37 +4,168 @@ UNUSED LICENSES: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USED LICENSES: +==================================================================================================== +LIBRARY: bsdiff +ORIGIN: ../../../flutter/third_party/bsdiff/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../flutter/third_party/bsdiff/io/flutter/util/BSDiff.java +---------------------------------------------------------------------------------------------------- +Copyright 2003-2005 Colin Percival. 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 THE AUTHOR ``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 BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== + ==================================================================================================== LIBRARY: engine LIBRARY: txt -ORIGIN: ../../../flutter/flow/layers/physical_shape_layer.cc + ../../../LICENSE +ORIGIN: ../../../flutter/LICENSE TYPE: LicenseType.bsd +FILE: ../../../flutter/DEPS +FILE: ../../../flutter/assets/asset_manager.cc +FILE: ../../../flutter/assets/asset_manager.h +FILE: ../../../flutter/assets/asset_resolver.h +FILE: ../../../flutter/assets/directory_asset_bundle.cc +FILE: ../../../flutter/assets/directory_asset_bundle.h +FILE: ../../../flutter/assets/zip_asset_store.cc +FILE: ../../../flutter/assets/zip_asset_store.h +FILE: ../../../flutter/benchmarking/benchmarking.cc +FILE: ../../../flutter/benchmarking/benchmarking.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/common/version/version.cc +FILE: ../../../flutter/common/version/version.h +FILE: ../../../flutter/flow/compositor_context.cc +FILE: ../../../flutter/flow/compositor_context.h FILE: ../../../flutter/flow/debug_print.cc FILE: ../../../flutter/flow/debug_print.h +FILE: ../../../flutter/flow/embedded_views.cc +FILE: ../../../flutter/flow/embedded_views.h +FILE: ../../../flutter/flow/export_node.cc FILE: ../../../flutter/flow/export_node.h +FILE: ../../../flutter/flow/instrumentation.cc +FILE: ../../../flutter/flow/instrumentation.h +FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc +FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h +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_rect_layer.cc +FILE: ../../../flutter/flow/layers/clip_rect_layer.h +FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc +FILE: ../../../flutter/flow/layers/clip_rrect_layer.h +FILE: ../../../flutter/flow/layers/color_filter_layer.cc +FILE: ../../../flutter/flow/layers/color_filter_layer.h +FILE: ../../../flutter/flow/layers/container_layer.cc +FILE: ../../../flutter/flow/layers/container_layer.h +FILE: ../../../flutter/flow/layers/layer.cc +FILE: ../../../flutter/flow/layers/layer.h +FILE: ../../../flutter/flow/layers/layer_tree.cc +FILE: ../../../flutter/flow/layers/layer_tree.h +FILE: ../../../flutter/flow/layers/opacity_layer.cc +FILE: ../../../flutter/flow/layers/opacity_layer.h +FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc +FILE: ../../../flutter/flow/layers/performance_overlay_layer.h +FILE: ../../../flutter/flow/layers/performance_overlay_layer_unittests.cc FILE: ../../../flutter/flow/layers/physical_shape_layer.cc FILE: ../../../flutter/flow/layers/physical_shape_layer.h +FILE: ../../../flutter/flow/layers/picture_layer.cc +FILE: ../../../flutter/flow/layers/picture_layer.h +FILE: ../../../flutter/flow/layers/platform_view_layer.cc +FILE: ../../../flutter/flow/layers/platform_view_layer.h +FILE: ../../../flutter/flow/layers/shader_mask_layer.cc +FILE: ../../../flutter/flow/layers/shader_mask_layer.h FILE: ../../../flutter/flow/layers/texture_layer.cc FILE: ../../../flutter/flow/layers/texture_layer.h +FILE: ../../../flutter/flow/layers/transform_layer.cc +FILE: ../../../flutter/flow/layers/transform_layer.h FILE: ../../../flutter/flow/matrix_decomposition.cc FILE: ../../../flutter/flow/matrix_decomposition.h FILE: ../../../flutter/flow/matrix_decomposition_unittests.cc FILE: ../../../flutter/flow/paint_utils.cc FILE: ../../../flutter/flow/paint_utils.h +FILE: ../../../flutter/flow/raster_cache.cc +FILE: ../../../flutter/flow/raster_cache.h FILE: ../../../flutter/flow/raster_cache_key.cc FILE: ../../../flutter/flow/raster_cache_key.h FILE: ../../../flutter/flow/raster_cache_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/texture.cc FILE: ../../../flutter/flow/texture.h +FILE: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart +FILE: ../../../flutter/fml/arraysize.h +FILE: ../../../flutter/fml/base32.cc +FILE: ../../../flutter/fml/base32.h +FILE: ../../../flutter/fml/base32_unittest.cc +FILE: ../../../flutter/fml/build_config.h +FILE: ../../../flutter/fml/closure.h +FILE: ../../../flutter/fml/command_line.cc +FILE: ../../../flutter/fml/command_line.h +FILE: ../../../flutter/fml/command_line_unittest.cc +FILE: ../../../flutter/fml/compiler_specific.h +FILE: ../../../flutter/fml/eintr_wrapper.h +FILE: ../../../flutter/fml/file.cc +FILE: ../../../flutter/fml/file.h +FILE: ../../../flutter/fml/file_unittest.cc FILE: ../../../flutter/fml/icu_util.cc FILE: ../../../flutter/fml/icu_util.h +FILE: ../../../flutter/fml/log_level.h +FILE: ../../../flutter/fml/log_settings.cc +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/macros.h +FILE: ../../../flutter/fml/make_copyable.h +FILE: ../../../flutter/fml/mapping.cc FILE: ../../../flutter/fml/mapping.h +FILE: ../../../flutter/fml/memory/ref_counted.h +FILE: ../../../flutter/fml/memory/ref_counted_internal.h +FILE: ../../../flutter/fml/memory/ref_counted_unittest.cc +FILE: ../../../flutter/fml/memory/ref_ptr.h +FILE: ../../../flutter/fml/memory/ref_ptr_internal.h +FILE: ../../../flutter/fml/memory/thread_checker.h +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 FILE: ../../../flutter/fml/message_loop_impl.h 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 +FILE: ../../../flutter/fml/paths_unittests.cc FILE: ../../../flutter/fml/platform/android/jni_util.cc FILE: ../../../flutter/fml/platform/android/jni_util.h FILE: ../../../flutter/fml/platform/android/jni_weak_ref.cc @@ -42,6 +173,7 @@ FILE: ../../../flutter/fml/platform/android/jni_weak_ref.h FILE: ../../../flutter/fml/platform/android/message_loop_android.cc FILE: ../../../flutter/fml/platform/android/message_loop_android.h FILE: ../../../flutter/fml/platform/android/paths_android.cc +FILE: ../../../flutter/fml/platform/android/paths_android.h FILE: ../../../flutter/fml/platform/android/scoped_java_ref.cc FILE: ../../../flutter/fml/platform/android/scoped_java_ref.h FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc @@ -51,207 +183,200 @@ FILE: ../../../flutter/fml/platform/darwin/message_loop_darwin.mm FILE: ../../../flutter/fml/platform/darwin/paths_darwin.mm FILE: ../../../flutter/fml/platform/darwin/platform_version.h FILE: ../../../flutter/fml/platform/darwin/platform_version.mm +FILE: ../../../flutter/fml/platform/darwin/scoped_block.h FILE: ../../../flutter/fml/platform/darwin/scoped_block.mm FILE: ../../../flutter/fml/platform/darwin/scoped_nsobject.h FILE: ../../../flutter/fml/platform/darwin/scoped_nsobject.mm +FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc 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 FILE: ../../../flutter/fml/platform/linux/timerfd.cc FILE: ../../../flutter/fml/platform/linux/timerfd.h +FILE: ../../../flutter/fml/platform/posix/file_posix.cc FILE: ../../../flutter/fml/platform/posix/mapping_posix.cc +FILE: ../../../flutter/fml/platform/posix/native_library_posix.cc +FILE: ../../../flutter/fml/platform/posix/paths_posix.cc +FILE: ../../../flutter/fml/platform/posix/shared_mutex_posix.cc +FILE: ../../../flutter/fml/platform/posix/shared_mutex_posix.h +FILE: ../../../flutter/fml/platform/win/errors_win.cc +FILE: ../../../flutter/fml/platform/win/errors_win.h +FILE: ../../../flutter/fml/platform/win/file_win.cc FILE: ../../../flutter/fml/platform/win/mapping_win.cc FILE: ../../../flutter/fml/platform/win/message_loop_win.cc FILE: ../../../flutter/fml/platform/win/message_loop_win.h +FILE: ../../../flutter/fml/platform/win/native_library_win.cc FILE: ../../../flutter/fml/platform/win/paths_win.cc +FILE: ../../../flutter/fml/platform/win/wstring_conversion.h +FILE: ../../../flutter/fml/string_view.cc +FILE: ../../../flutter/fml/string_view.h +FILE: ../../../flutter/fml/string_view_unittest.cc +FILE: ../../../flutter/fml/synchronization/atomic_object.h +FILE: ../../../flutter/fml/synchronization/count_down_latch.cc +FILE: ../../../flutter/fml/synchronization/count_down_latch.h +FILE: ../../../flutter/fml/synchronization/count_down_latch_unittests.cc +FILE: ../../../flutter/fml/synchronization/shared_mutex.h +FILE: ../../../flutter/fml/synchronization/shared_mutex_std.cc +FILE: ../../../flutter/fml/synchronization/shared_mutex_std.h +FILE: ../../../flutter/fml/synchronization/thread_annotations.h +FILE: ../../../flutter/fml/synchronization/thread_annotations_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_runner.cc FILE: ../../../flutter/fml/task_runner.h 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/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/trace_event.cc FILE: ../../../flutter/fml/trace_event.h +FILE: ../../../flutter/fml/unique_fd.cc +FILE: ../../../flutter/fml/unique_fd.h +FILE: ../../../flutter/fml/unique_object.h +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/stub_ui/compositing.dart +FILE: ../../../flutter/lib/stub_ui/geometry.dart +FILE: ../../../flutter/lib/stub_ui/hash_codes.dart +FILE: ../../../flutter/lib/stub_ui/hooks.dart +FILE: ../../../flutter/lib/stub_ui/isolate_name_server.dart +FILE: ../../../flutter/lib/stub_ui/lerp.dart +FILE: ../../../flutter/lib/stub_ui/natives.dart +FILE: ../../../flutter/lib/stub_ui/painting.dart +FILE: ../../../flutter/lib/stub_ui/plugins.dart +FILE: ../../../flutter/lib/stub_ui/pointer.dart +FILE: ../../../flutter/lib/stub_ui/semantics.dart +FILE: ../../../flutter/lib/stub_ui/text.dart +FILE: ../../../flutter/lib/stub_ui/ui.dart +FILE: ../../../flutter/lib/stub_ui/versions.dart +FILE: ../../../flutter/lib/stub_ui/window.dart +FILE: ../../../flutter/lib/ui/compositing.dart +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/dart_runtime_hooks.cc +FILE: ../../../flutter/lib/ui/dart_runtime_hooks.h +FILE: ../../../flutter/lib/ui/dart_ui.cc +FILE: ../../../flutter/lib/ui/dart_ui.h +FILE: ../../../flutter/lib/ui/dart_wrapper.h +FILE: ../../../flutter/lib/ui/geometry.dart +FILE: ../../../flutter/lib/ui/hash_codes.dart +FILE: ../../../flutter/lib/ui/hooks.dart +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/lerp.dart +FILE: ../../../flutter/lib/ui/natives.dart +FILE: ../../../flutter/lib/ui/painting.dart +FILE: ../../../flutter/lib/ui/painting/canvas.cc +FILE: ../../../flutter/lib/ui/painting/canvas.h FILE: ../../../flutter/lib/ui/painting/codec.cc FILE: ../../../flutter/lib/ui/painting/codec.h +FILE: ../../../flutter/lib/ui/painting/engine_layer.cc +FILE: ../../../flutter/lib/ui/painting/engine_layer.h FILE: ../../../flutter/lib/ui/painting/frame_info.cc FILE: ../../../flutter/lib/ui/painting/frame_info.h +FILE: ../../../flutter/lib/ui/painting/gradient.cc +FILE: ../../../flutter/lib/ui/painting/gradient.h +FILE: ../../../flutter/lib/ui/painting/image.cc +FILE: ../../../flutter/lib/ui/painting/image.h +FILE: ../../../flutter/lib/ui/painting/image_encoding.cc +FILE: ../../../flutter/lib/ui/painting/image_encoding.h +FILE: ../../../flutter/lib/ui/painting/image_filter.cc +FILE: ../../../flutter/lib/ui/painting/image_filter.h +FILE: ../../../flutter/lib/ui/painting/image_shader.cc +FILE: ../../../flutter/lib/ui/painting/image_shader.h +FILE: ../../../flutter/lib/ui/painting/matrix.cc +FILE: ../../../flutter/lib/ui/painting/matrix.h +FILE: ../../../flutter/lib/ui/painting/paint.cc +FILE: ../../../flutter/lib/ui/painting/paint.h +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/picture.cc +FILE: ../../../flutter/lib/ui/painting/picture.h +FILE: ../../../flutter/lib/ui/painting/picture_recorder.cc +FILE: ../../../flutter/lib/ui/painting/picture_recorder.h +FILE: ../../../flutter/lib/ui/painting/rrect.cc +FILE: ../../../flutter/lib/ui/painting/rrect.h +FILE: ../../../flutter/lib/ui/painting/shader.cc +FILE: ../../../flutter/lib/ui/painting/shader.h FILE: ../../../flutter/lib/ui/painting/vertices.cc FILE: ../../../flutter/lib/ui/painting/vertices.h +FILE: ../../../flutter/lib/ui/plugins.dart +FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc +FILE: ../../../flutter/lib/ui/plugins/callback_cache.h +FILE: ../../../flutter/lib/ui/pointer.dart +FILE: ../../../flutter/lib/ui/semantics.dart +FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.cc +FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.h +FILE: ../../../flutter/lib/ui/semantics/semantics_node.cc +FILE: ../../../flutter/lib/ui/semantics/semantics_node.h +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/snapshot_delegate.h +FILE: ../../../flutter/lib/ui/text.dart +FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc +FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.h FILE: ../../../flutter/lib/ui/text/font_collection.cc FILE: ../../../flutter/lib/ui/text/font_collection.h -FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc -FILE: ../../../flutter/shell/gpu/gpu_surface_software.h -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_surface_software.cc -FILE: ../../../flutter/shell/platform/android/android_surface_software.h -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityEvents.java -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/EventChannel.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterException.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MessageCodec.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCall.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodChannel.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCodec.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java -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/util/PathUtils.java -FILE: ../../../flutter/shell/platform/android/io/flutter/util/Preconditions.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/TextureRegistry.java -FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.cc -FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.mm -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.h -FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.mm -FILE: ../../../flutter/shell/platform/embedder/embedder.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_include.c -FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc -FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h -FILE: ../../../flutter/third_party/txt/src/txt/platform.cc -FILE: ../../../flutter/third_party/txt/src/txt/platform.h -FILE: ../../../flutter/third_party/txt/src/txt/platform_android.cc -FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.mm -FILE: ../../../flutter/vulkan/skia_vulkan_header.h -FILE: ../../../flutter/vulkan/vulkan_native_surface_magma.cc -FILE: ../../../flutter/vulkan/vulkan_native_surface_magma.h -FILE: ../../../flutter/vulkan/vulkan_provider.cc -FILE: ../../../flutter/vulkan/vulkan_provider.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: engine -ORIGIN: ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/DEPS -FILE: ../../../flutter/lib/io/dart_io.cc -FILE: ../../../flutter/lib/io/dart_io.h -FILE: ../../../flutter/lib/snapshot/libraries.json -FILE: ../../../flutter/lib/ui/dart_runtime_hooks.cc -FILE: ../../../flutter/lib/ui/dart_runtime_hooks.h -FILE: ../../../flutter/lib/ui/dart_ui.cc -FILE: ../../../flutter/lib/ui/dart_ui.h -FILE: ../../../flutter/lib/ui/natives.dart +FILE: ../../../flutter/lib/ui/text/paragraph.cc +FILE: ../../../flutter/lib/ui/text/paragraph.h +FILE: ../../../flutter/lib/ui/text/paragraph_builder.cc +FILE: ../../../flutter/lib/ui/text/paragraph_builder.h +FILE: ../../../flutter/lib/ui/text/paragraph_impl.cc +FILE: ../../../flutter/lib/ui/text/paragraph_impl.h +FILE: ../../../flutter/lib/ui/text/paragraph_impl_txt.cc +FILE: ../../../flutter/lib/ui/text/paragraph_impl_txt.h +FILE: ../../../flutter/lib/ui/text/text_box.cc +FILE: ../../../flutter/lib/ui/text/text_box.h +FILE: ../../../flutter/lib/ui/ui.dart +FILE: ../../../flutter/lib/ui/ui_dart_state.cc +FILE: ../../../flutter/lib/ui/ui_dart_state.h +FILE: ../../../flutter/lib/ui/versions.cc +FILE: ../../../flutter/lib/ui/versions.dart +FILE: ../../../flutter/lib/ui/versions.h +FILE: ../../../flutter/lib/ui/window.dart +FILE: ../../../flutter/lib/ui/window/platform_message.cc +FILE: ../../../flutter/lib/ui/window/platform_message.h +FILE: ../../../flutter/lib/ui/window/platform_message_response.cc +FILE: ../../../flutter/lib/ui/window/platform_message_response.h +FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.cc +FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.h +FILE: ../../../flutter/lib/ui/window/pointer_data.cc +FILE: ../../../flutter/lib/ui/window/pointer_data.h +FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc +FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h +FILE: ../../../flutter/lib/ui/window/viewport_metrics.cc +FILE: ../../../flutter/lib/ui/window/viewport_metrics.h +FILE: ../../../flutter/lib/ui/window/window.cc FILE: ../../../flutter/lib/ui/window/window.h -FILE: ../../../flutter/shell/common/skia_event_tracer_impl.cc -FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONUtil.java -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Info.plist -FILE: ../../../flutter/shell/platform/darwin/ios/framework/module.modulemap -FILE: ../../../flutter/shell/platform/darwin/ios/headless_platform_view_ios.mm -FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist -FILE: ../../../flutter/shell/platform/embedder/assets/embedder.modulemap -FILE: ../../../flutter/shell/platform/embedder/fixtures/simple_main.dart -FILE: ../../../flutter/sky/tools/roll/patches/chromium/android_build.patch ----------------------------------------------------------------------------------------------------- -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: engine -ORIGIN: ../../../flutter/assets/asset_manager.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/assets/asset_manager.cc -FILE: ../../../flutter/assets/asset_manager.h -FILE: ../../../flutter/assets/asset_resolver.h -FILE: ../../../flutter/common/task_runners.cc -FILE: ../../../flutter/common/task_runners.h -FILE: ../../../flutter/flow/skia_gpu_object.cc -FILE: ../../../flutter/flow/skia_gpu_object.h FILE: ../../../flutter/runtime/dart_isolate.cc FILE: ../../../flutter/runtime/dart_isolate.h FILE: ../../../flutter/runtime/dart_isolate_unittests.cc +FILE: ../../../flutter/runtime/dart_service_isolate.cc +FILE: ../../../flutter/runtime/dart_service_isolate.h +FILE: ../../../flutter/runtime/dart_service_isolate_unittests.cc FILE: ../../../flutter/runtime/dart_snapshot.cc FILE: ../../../flutter/runtime/dart_snapshot.h FILE: ../../../flutter/runtime/dart_snapshot_buffer.cc @@ -259,153 +384,336 @@ FILE: ../../../flutter/runtime/dart_snapshot_buffer.h FILE: ../../../flutter/runtime/dart_vm.cc FILE: ../../../flutter/runtime/dart_vm.h FILE: ../../../flutter/runtime/dart_vm_unittests.cc -FILE: ../../../flutter/runtime/service_protocol.cc -FILE: ../../../flutter/runtime/service_protocol.h -FILE: ../../../flutter/shell/common/io_manager.cc -FILE: ../../../flutter/shell/common/io_manager.h -FILE: ../../../flutter/shell/common/run_configuration.cc -FILE: ../../../flutter/shell/common/run_configuration.h -FILE: ../../../flutter/shell/common/shell_unittests.cc -FILE: ../../../flutter/shell/common/thread_host.cc -FILE: ../../../flutter/shell/common/thread_host.h -FILE: ../../../flutter/shell/platform/darwin/common/command_line.h -FILE: ../../../flutter/shell/platform/darwin/common/command_line.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h -FILE: ../../../flutter/shell/platform/embedder/embedder.h -FILE: ../../../flutter/shell/platform/embedder/embedder_engine.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_engine.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 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: engine -ORIGIN: ../../../flutter/assets/directory_asset_bundle.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/assets/directory_asset_bundle.cc -FILE: ../../../flutter/assets/directory_asset_bundle.h -FILE: ../../../flutter/assets/zip_asset_store.cc -FILE: ../../../flutter/assets/zip_asset_store.h -FILE: ../../../flutter/common/settings.cc -FILE: ../../../flutter/common/settings.h -FILE: ../../../flutter/flow/export_node.cc -FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc -FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h -FILE: ../../../flutter/flow/layers/child_scene_layer.cc -FILE: ../../../flutter/flow/layers/child_scene_layer.h -FILE: ../../../flutter/flow/layers/shader_mask_layer.cc -FILE: ../../../flutter/flow/layers/shader_mask_layer.h -FILE: ../../../flutter/flow/raster_cache.cc -FILE: ../../../flutter/flow/raster_cache.h -FILE: ../../../flutter/flow/scene_update_context.cc -FILE: ../../../flutter/flow/scene_update_context.h -FILE: ../../../flutter/lib/ui/painting/image_filter.cc -FILE: ../../../flutter/lib/ui/painting/image_filter.h -FILE: ../../../flutter/lib/ui/semantics.dart -FILE: ../../../flutter/lib/ui/semantics/semantics_node.cc -FILE: ../../../flutter/lib/ui/semantics/semantics_node.h -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/text/text_box.h -FILE: ../../../flutter/lib/ui/window/platform_message.cc -FILE: ../../../flutter/lib/ui/window/platform_message.h -FILE: ../../../flutter/lib/ui/window/platform_message_response.cc -FILE: ../../../flutter/lib/ui/window/platform_message_response.h -FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.cc -FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.h -FILE: ../../../flutter/lib/ui/window/pointer_data.h -FILE: ../../../flutter/lib/ui/window/pointer_data_packet.h -FILE: ../../../flutter/lib/ui/window/viewport_metrics.h FILE: ../../../flutter/runtime/embedder_resources.cc FILE: ../../../flutter/runtime/embedder_resources.h FILE: ../../../flutter/runtime/fixtures/simple_main.dart +FILE: ../../../flutter/runtime/runtime_controller.cc +FILE: ../../../flutter/runtime/runtime_controller.h +FILE: ../../../flutter/runtime/runtime_delegate.cc +FILE: ../../../flutter/runtime/runtime_delegate.h +FILE: ../../../flutter/runtime/service_protocol.cc +FILE: ../../../flutter/runtime/service_protocol.h FILE: ../../../flutter/runtime/start_up.cc FILE: ../../../flutter/runtime/start_up.h FILE: ../../../flutter/runtime/test_font_data.cc FILE: ../../../flutter/runtime/test_font_data.h +FILE: ../../../flutter/shell/common/animator.cc +FILE: ../../../flutter/shell/common/animator.h +FILE: ../../../flutter/shell/common/engine.cc +FILE: ../../../flutter/shell/common/engine.h +FILE: ../../../flutter/shell/common/io_manager.cc +FILE: ../../../flutter/shell/common/io_manager.h +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/platform_view.cc +FILE: ../../../flutter/shell/common/platform_view.h +FILE: ../../../flutter/shell/common/rasterizer.cc +FILE: ../../../flutter/shell/common/rasterizer.h +FILE: ../../../flutter/shell/common/run_configuration.cc +FILE: ../../../flutter/shell/common/run_configuration.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_unittests.cc +FILE: ../../../flutter/shell/common/skia_event_tracer_impl.cc FILE: ../../../flutter/shell/common/skia_event_tracer_impl.h FILE: ../../../flutter/shell/common/surface.cc FILE: ../../../flutter/shell/common/surface.h +FILE: ../../../flutter/shell/common/switches.cc +FILE: ../../../flutter/shell/common/switches.h +FILE: ../../../flutter/shell/common/thread_host.cc +FILE: ../../../flutter/shell/common/thread_host.h +FILE: ../../../flutter/shell/common/vsync_waiter.cc +FILE: ../../../flutter/shell/common/vsync_waiter.h FILE: ../../../flutter/shell/common/vsync_waiter_fallback.cc +FILE: ../../../flutter/shell/common/vsync_waiter_fallback.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_software.cc +FILE: ../../../flutter/shell/gpu/gpu_surface_software.h FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan.cc FILE: ../../../flutter/shell/gpu/gpu_surface_vulkan.h +FILE: ../../../flutter/shell/platform/android/AndroidManifest.xml FILE: ../../../flutter/shell/platform/android/android_context_gl.cc FILE: ../../../flutter/shell/platform/android/android_context_gl.h FILE: ../../../flutter/shell/platform/android/android_environment_gl.cc 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_native_window.cc FILE: ../../../flutter/shell/platform/android/android_native_window.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.cc FILE: ../../../flutter/shell/platform/android/android_surface.h FILE: ../../../flutter/shell/platform/android/android_surface_gl.cc FILE: ../../../flutter/shell/platform/android/android_surface_gl.h +FILE: ../../../flutter/shell/platform/android/android_surface_software.cc +FILE: ../../../flutter/shell/platform/android/android_surface_software.h FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.cc FILE: ../../../flutter/shell/platform/android/android_surface_vulkan.h +FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc +FILE: ../../../flutter/shell/platform/android/apk_asset_provider.h +FILE: ../../../flutter/shell/platform/android/flutter_main.cc +FILE: ../../../flutter/shell/platform/android/flutter_main.h +FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java +FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +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/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/FlutterActivity.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +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/engine/FlutterEngine.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.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/renderer/FlutterRenderer.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.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 +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/ErrorLogResult.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/EventChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterException.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/JSONUtil.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MessageCodec.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCall.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodChannel.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCodec.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java +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/InputConnectionAdaptor.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java +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/view/AccessibilityBridge.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterMain.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArguments.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourceCleaner.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourceExtractor.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourcePaths.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourceUpdater.java +FILE: ../../../flutter/shell/platform/android/io/flutter/view/TextureRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java +FILE: ../../../flutter/shell/platform/android/library_loader.cc +FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc +FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h +FILE: ../../../flutter/shell/platform/android/platform_view_android.cc +FILE: ../../../flutter/shell/platform/android/platform_view_android.h +FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.cc +FILE: ../../../flutter/shell/platform/android/platform_view_android_jni.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/engine_method_result.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/engine_method_result.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h +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/plugin_registrar.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_message_codec.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_method_codec.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar_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/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/darwin/common/buffer_conversions.h FILE: ../../../flutter/shell/platform/darwin/common/buffer_conversions.mm +FILE: ../../../flutter/shell/platform/darwin/common/command_line.h +FILE: ../../../flutter/shell/platform/darwin/common/command_line.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/FlutterBinaryMessenger.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.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/FlutterHeadlessDartRunner.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.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/FlutterAppDelegate_Internal.h +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/FlutterChannels.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.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 +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +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/FlutterStandardCodec.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h 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/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/FlutterViewController_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/module.modulemap +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_gl_context.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_context.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_gl_render_target.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_gl.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.mm FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.h FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEOpenGLContextHandling.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEPlugin.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEPluginRegistrar.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEView.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEViewController.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Info.plist +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputModel.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLEView.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLEViewController.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap +FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist +FILE: ../../../flutter/shell/platform/embedder/assets/embedder.modulemap +FILE: ../../../flutter/shell/platform/embedder/embedder.cc +FILE: ../../../flutter/shell/platform/embedder/embedder.h +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_include.c +FILE: ../../../flutter/shell/platform/embedder/embedder_safe_access.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_software.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h +FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_task_runner.h +FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_thread_host.h +FILE: ../../../flutter/shell/platform/embedder/fixtures/a11y_main.dart +FILE: ../../../flutter/shell/platform/embedder/fixtures/simple_main.dart +FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc +FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h +FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.cc +FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.h +FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller.cc +FILE: ../../../flutter/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc +FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h +FILE: ../../../flutter/shell/platform/glfw/flutter_glfw.cc +FILE: ../../../flutter/shell/platform/glfw/key_event_handler.cc +FILE: ../../../flutter/shell/platform/glfw/key_event_handler.h +FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h +FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h +FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc +FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart +FILE: ../../../flutter/sky/tools/roll/patches/chromium/android_build.patch FILE: ../../../flutter/synchronization/pipeline.cc FILE: ../../../flutter/synchronization/pipeline.h FILE: ../../../flutter/synchronization/semaphore.cc FILE: ../../../flutter/synchronization/semaphore.h FILE: ../../../flutter/synchronization/semaphore_unittest.cc +FILE: ../../../flutter/third_party/txt/src/txt/platform.cc +FILE: ../../../flutter/third_party/txt/src/txt/platform.h +FILE: ../../../flutter/third_party/txt/src/txt/platform_android.cc +FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.mm +FILE: ../../../flutter/vulkan/skia_vulkan_header.h FILE: ../../../flutter/vulkan/vulkan_application.cc FILE: ../../../flutter/vulkan/vulkan_application.h FILE: ../../../flutter/vulkan/vulkan_backbuffer.cc @@ -428,539 +736,55 @@ FILE: ../../../flutter/vulkan/vulkan_native_surface_android.cc FILE: ../../../flutter/vulkan/vulkan_native_surface_android.h FILE: ../../../flutter/vulkan/vulkan_proc_table.cc FILE: ../../../flutter/vulkan/vulkan_proc_table.h +FILE: ../../../flutter/vulkan/vulkan_provider.cc +FILE: ../../../flutter/vulkan/vulkan_provider.h FILE: ../../../flutter/vulkan/vulkan_surface.cc FILE: ../../../flutter/vulkan/vulkan_surface.h FILE: ../../../flutter/vulkan/vulkan_swapchain.cc FILE: ../../../flutter/vulkan/vulkan_swapchain.h +FILE: ../../../flutter/vulkan/vulkan_swapchain_stub.cc 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/flutter_kernel_sdk.dart +FILE: ../../../flutter/web_sdk/libraries.json ---------------------------------------------------------------------------------------------------- -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: engine -ORIGIN: ../../../flutter/benchmarking/benchmarking.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/benchmarking/benchmarking.cc -FILE: ../../../flutter/benchmarking/benchmarking.h -FILE: ../../../flutter/fml/base32.cc -FILE: ../../../flutter/fml/base32.h -FILE: ../../../flutter/fml/base32_unittest.cc -FILE: ../../../flutter/fml/file.cc -FILE: ../../../flutter/fml/file.h -FILE: ../../../flutter/fml/file_unittest.cc -FILE: ../../../flutter/fml/macros.h -FILE: ../../../flutter/fml/mapping.cc -FILE: ../../../flutter/fml/message.cc -FILE: ../../../flutter/fml/message.h -FILE: ../../../flutter/fml/message_unittests.cc -FILE: ../../../flutter/fml/native_library.h -FILE: ../../../flutter/fml/paths.cc -FILE: ../../../flutter/fml/platform/android/paths_android.h -FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc -FILE: ../../../flutter/fml/platform/posix/file_posix.cc -FILE: ../../../flutter/fml/platform/posix/native_library_posix.cc -FILE: ../../../flutter/fml/platform/posix/paths_posix.cc -FILE: ../../../flutter/fml/platform/win/errors_win.cc -FILE: ../../../flutter/fml/platform/win/errors_win.h -FILE: ../../../flutter/fml/platform/win/file_win.cc -FILE: ../../../flutter/fml/platform/win/native_library_win.cc -FILE: ../../../flutter/fml/platform/win/wstring_conversion.h -FILE: ../../../flutter/fml/unique_fd.cc -FILE: ../../../flutter/fml/unique_fd.h -FILE: ../../../flutter/fml/unique_object.h -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/plugins/callback_cache.cc -FILE: ../../../flutter/lib/ui/plugins/callback_cache.h -FILE: ../../../flutter/runtime/dart_service_isolate_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/shell_benchmarks.cc -FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc -FILE: ../../../flutter/shell/platform/android/android_shell_holder.h -FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc -FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h -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/platform_message_response_darwin.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm -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_software.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h -FILE: ../../../flutter/shell/version/version.cc -FILE: ../../../flutter/shell/version/version.h ----------------------------------------------------------------------------------------------------- -Copyright 2018 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: engine -ORIGIN: ../../../flutter/flow/layers/platform_view_layer.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/flow/layers/platform_view_layer.cc -FILE: ../../../flutter/flow/layers/platform_view_layer.h -FILE: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart -FILE: ../../../flutter/fml/paths_unittests.cc -FILE: ../../../flutter/lib/ui/isolate_name_server.dart -FILE: ../../../flutter/lib/ui/painting/engine_layer.cc -FILE: ../../../flutter/lib/ui/painting/engine_layer.h -FILE: ../../../flutter/lib/ui/painting/image_encoding.cc -FILE: ../../../flutter/lib/ui/painting/image_encoding.h -FILE: ../../../flutter/lib/ui/plugins.dart -FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.cc -FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.h -FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc -FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.h -FILE: ../../../flutter/shell/platform/android/apk_asset_provider.h -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformView.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java -FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArguments.java -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h -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/FlutterPluginAppLifeCycleDelegate.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm -FILE: ../../../flutter/shell/platform/darwin/ios/headless_platform_view_ios.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: engine -ORIGIN: ../../../flutter/fml/platform/darwin/scoped_block.h + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/fml/platform/darwin/scoped_block.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: engine -ORIGIN: ../../../flutter/fml/time/time_delta_unittest.cc + ../../../third_party/tonic/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/fml/export.h -FILE: ../../../flutter/fml/time/time_delta_unittest.cc -FILE: ../../../flutter/fml/time/time_point_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2017 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. - * 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: engine -ORIGIN: ../../../flutter/lib/ui/painting/image.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/lib/ui/painting/image.cc -FILE: ../../../flutter/lib/ui/painting/image.h -FILE: ../../../flutter/shell/common/switches.cc -FILE: ../../../flutter/shell/common/switches.h -FILE: ../../../flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterMain.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterView.java ----------------------------------------------------------------------------------------------------- -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. +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== LIBRARY: engine -ORIGIN: ../../../third_party/icu/scripts/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/flow/compositor_context.cc -FILE: ../../../flutter/flow/compositor_context.h -FILE: ../../../flutter/flow/instrumentation.cc -FILE: ../../../flutter/flow/instrumentation.h -FILE: ../../../flutter/flow/layers/clip_path_layer.cc -FILE: ../../../flutter/flow/layers/clip_path_layer.h -FILE: ../../../flutter/flow/layers/clip_rect_layer.cc -FILE: ../../../flutter/flow/layers/clip_rect_layer.h -FILE: ../../../flutter/flow/layers/clip_rrect_layer.cc -FILE: ../../../flutter/flow/layers/clip_rrect_layer.h -FILE: ../../../flutter/flow/layers/color_filter_layer.cc -FILE: ../../../flutter/flow/layers/color_filter_layer.h -FILE: ../../../flutter/flow/layers/container_layer.cc -FILE: ../../../flutter/flow/layers/container_layer.h -FILE: ../../../flutter/flow/layers/layer.cc -FILE: ../../../flutter/flow/layers/layer.h -FILE: ../../../flutter/flow/layers/layer_tree.cc -FILE: ../../../flutter/flow/layers/layer_tree.h -FILE: ../../../flutter/flow/layers/opacity_layer.cc -FILE: ../../../flutter/flow/layers/opacity_layer.h -FILE: ../../../flutter/flow/layers/performance_overlay_layer.cc -FILE: ../../../flutter/flow/layers/performance_overlay_layer.h -FILE: ../../../flutter/flow/layers/picture_layer.cc -FILE: ../../../flutter/flow/layers/picture_layer.h -FILE: ../../../flutter/flow/layers/transform_layer.cc -FILE: ../../../flutter/flow/layers/transform_layer.h -FILE: ../../../flutter/lib/snapshot/snapshot.dart -FILE: ../../../flutter/lib/snapshot/snapshot.h -FILE: ../../../flutter/lib/snapshot/snapshot_fuchsia.dart -FILE: ../../../flutter/lib/ui/compositing.dart -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/dart_wrapper.h -FILE: ../../../flutter/lib/ui/geometry.dart -FILE: ../../../flutter/lib/ui/hash_codes.dart -FILE: ../../../flutter/lib/ui/hooks.dart -FILE: ../../../flutter/lib/ui/lerp.dart -FILE: ../../../flutter/lib/ui/painting.dart -FILE: ../../../flutter/lib/ui/painting/canvas.cc -FILE: ../../../flutter/lib/ui/painting/canvas.h -FILE: ../../../flutter/lib/ui/painting/gradient.cc -FILE: ../../../flutter/lib/ui/painting/gradient.h -FILE: ../../../flutter/lib/ui/painting/image_shader.cc -FILE: ../../../flutter/lib/ui/painting/image_shader.h -FILE: ../../../flutter/lib/ui/painting/matrix.cc -FILE: ../../../flutter/lib/ui/painting/matrix.h -FILE: ../../../flutter/lib/ui/painting/paint.cc -FILE: ../../../flutter/lib/ui/painting/paint.h -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/picture.cc -FILE: ../../../flutter/lib/ui/painting/picture.h -FILE: ../../../flutter/lib/ui/painting/picture_recorder.cc -FILE: ../../../flutter/lib/ui/painting/picture_recorder.h -FILE: ../../../flutter/lib/ui/painting/rrect.cc -FILE: ../../../flutter/lib/ui/painting/rrect.h -FILE: ../../../flutter/lib/ui/painting/shader.cc -FILE: ../../../flutter/lib/ui/painting/shader.h -FILE: ../../../flutter/lib/ui/pointer.dart -FILE: ../../../flutter/lib/ui/text.dart -FILE: ../../../flutter/lib/ui/text/paragraph.cc -FILE: ../../../flutter/lib/ui/text/paragraph.h -FILE: ../../../flutter/lib/ui/text/paragraph_builder.cc -FILE: ../../../flutter/lib/ui/text/paragraph_builder.h -FILE: ../../../flutter/lib/ui/text/paragraph_impl.cc -FILE: ../../../flutter/lib/ui/text/paragraph_impl.h -FILE: ../../../flutter/lib/ui/text/paragraph_impl_txt.cc -FILE: ../../../flutter/lib/ui/text/paragraph_impl_txt.h -FILE: ../../../flutter/lib/ui/text/text_box.cc -FILE: ../../../flutter/lib/ui/ui.dart -FILE: ../../../flutter/lib/ui/ui_dart_state.cc -FILE: ../../../flutter/lib/ui/ui_dart_state.h -FILE: ../../../flutter/lib/ui/window.dart -FILE: ../../../flutter/lib/ui/window/pointer_data.cc -FILE: ../../../flutter/lib/ui/window/pointer_data_packet.cc -FILE: ../../../flutter/lib/ui/window/window.cc -FILE: ../../../flutter/runtime/dart_service_isolate.cc -FILE: ../../../flutter/runtime/dart_service_isolate.h -FILE: ../../../flutter/runtime/runtime_controller.cc -FILE: ../../../flutter/runtime/runtime_controller.h -FILE: ../../../flutter/runtime/runtime_delegate.cc -FILE: ../../../flutter/runtime/runtime_delegate.h -FILE: ../../../flutter/shell/common/animator.cc -FILE: ../../../flutter/shell/common/animator.h -FILE: ../../../flutter/shell/common/engine.cc -FILE: ../../../flutter/shell/common/engine.h -FILE: ../../../flutter/shell/common/platform_view.cc -FILE: ../../../flutter/shell/common/platform_view.h -FILE: ../../../flutter/shell/common/rasterizer.cc -FILE: ../../../flutter/shell/common/rasterizer.h -FILE: ../../../flutter/shell/common/shell.cc -FILE: ../../../flutter/shell/common/shell.h -FILE: ../../../flutter/shell/common/vsync_waiter.cc -FILE: ../../../flutter/shell/common/vsync_waiter.h -FILE: ../../../flutter/shell/common/vsync_waiter_fallback.h -FILE: ../../../flutter/shell/platform/android/AndroidManifest.xml -FILE: ../../../flutter/shell/platform/android/flutter_main.cc -FILE: ../../../flutter/shell/platform/android/flutter_main.h -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterActivity.java -FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterApplication.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourceCleaner.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourceExtractor.java -FILE: ../../../flutter/shell/platform/android/io/flutter/view/ResourcePaths.java -FILE: ../../../flutter/shell/platform/android/library_loader.cc -FILE: ../../../flutter/shell/platform/android/platform_view_android.cc -FILE: ../../../flutter/shell/platform/android/platform_view_android.h -FILE: ../../../flutter/shell/platform/darwin/common/buffer_conversions.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm ----------------------------------------------------------------------------------------------------- -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: engine -ORIGIN: ../../../third_party/tonic/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../flutter/fml/arraysize.h -FILE: ../../../flutter/fml/build_config.h -FILE: ../../../flutter/fml/closure.h -FILE: ../../../flutter/fml/command_line.cc -FILE: ../../../flutter/fml/command_line.h -FILE: ../../../flutter/fml/command_line_unittest.cc -FILE: ../../../flutter/fml/compiler_specific.h -FILE: ../../../flutter/fml/eintr_wrapper.h -FILE: ../../../flutter/fml/log_level.h -FILE: ../../../flutter/fml/log_settings.cc -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/make_copyable.h -FILE: ../../../flutter/fml/memory/ref_counted.h -FILE: ../../../flutter/fml/memory/ref_counted_internal.h -FILE: ../../../flutter/fml/memory/ref_counted_unittest.cc -FILE: ../../../flutter/fml/memory/ref_ptr.h -FILE: ../../../flutter/fml/memory/ref_ptr_internal.h -FILE: ../../../flutter/fml/memory/thread_checker.h -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/string_view.cc -FILE: ../../../flutter/fml/string_view.h -FILE: ../../../flutter/fml/string_view_unittest.cc -FILE: ../../../flutter/fml/synchronization/thread_annotations.h -FILE: ../../../flutter/fml/synchronization/thread_annotations_unittest.cc -FILE: ../../../flutter/fml/synchronization/thread_checker.h -FILE: ../../../flutter/fml/synchronization/thread_checker_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/time/time_delta.h -FILE: ../../../flutter/fml/time/time_point.cc -FILE: ../../../flutter/fml/time/time_point.h -FILE: ../../../flutter/fml/time/time_unittest.cc ----------------------------------------------------------------------------------------------------- -Copyright 2016 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. - * 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 TYPE: LicenseType.apache +FILE: ../../../flutter/flow/flow_run_all_unittests.cc +FILE: ../../../flutter/flow/flow_test_utils.cc +FILE: ../../../flutter/flow/flow_test_utils.h 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 @@ -1243,4 +1067,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: 12 +Total license count: 3 diff --git a/ci/licenses_golden/licenses_garnet b/ci/licenses_golden/licenses_garnet deleted file mode 100644 index 1792f6921fe63..0000000000000 --- a/ci/licenses_golden/licenses_garnet +++ /dev/null @@ -1,960 +0,0 @@ -Signature: e493a563f62df99aea315a223aa83d38 - -UNUSED LICENSES: - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -USED LICENSES: - -==================================================================================================== -LIBRARY: garnet -ORIGIN: ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/macros.h ----------------------------------------------------------------------------------------------------- -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: garnet -ORIGIN: ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/.cargo/config -FILE: ../../../garnet/.dir-locals.el -FILE: ../../../garnet/Cargo.lock -FILE: ../../../garnet/Cargo.toml -FILE: ../../../garnet/manifest/garnet -FILE: ../../../garnet/manifest/minimal -FILE: ../../../garnet/manifest/third_party -FILE: ../../../garnet/public/lib/amber/fidl/amber.fidl -FILE: ../../../garnet/public/lib/auth/fidl/auth_provider.fidl -FILE: ../../../garnet/public/lib/auth/fidl/auth_provider_factory.fidl -FILE: ../../../garnet/public/lib/auth/fidl/token_manager.fidl -FILE: ../../../garnet/public/lib/bluetooth/fidl/common.fidl -FILE: ../../../garnet/public/lib/bluetooth/fidl/control.fidl -FILE: ../../../garnet/public/lib/bluetooth/fidl/gatt.fidl -FILE: ../../../garnet/public/lib/bluetooth/fidl/low_energy.fidl -FILE: ../../../garnet/public/lib/cobalt/fidl/cobalt.fidl -FILE: ../../../garnet/public/lib/cobalt/fidl/cobalt_controller.fidl -FILE: ../../../garnet/public/lib/device_settings/fidl/device_settings.fidl -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings2/test_types.fidl2 -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/compiler/generated/fidl_files/fidl_files.core.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/compiler/generated/fidl_types/fidl_types.core.go -FILE: ../../../garnet/public/lib/fidl/rust/fidl/Cargo.toml -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/endpoints.rs -FILE: ../../../garnet/public/lib/fidl/tools/sublime/FIDL.sublime-syntax -FILE: ../../../garnet/public/lib/fidl/tools/vim/ftdetect/fidl.vim -FILE: ../../../garnet/public/lib/fidl/tools/vim/syntax/fidl.vim -FILE: ../../../garnet/public/lib/fsl/fidl/sized_vmo_transport.fidl -FILE: ../../../garnet/public/lib/fsl/io/fd.cc -FILE: ../../../garnet/public/lib/fsl/io/fd.h -FILE: ../../../garnet/public/lib/fsl/socket/blocking_drain_unittest.cc -FILE: ../../../garnet/public/lib/fsl/socket/strings_unittest.cc -FILE: ../../../garnet/public/lib/fsl/tasks/fd_waiter.cc -FILE: ../../../garnet/public/lib/fsl/tasks/fd_waiter.h -FILE: ../../../garnet/public/lib/fsl/tasks/fd_waiter_unittest.cc -FILE: ../../../garnet/public/lib/fsl/threading/thread.cc -FILE: ../../../garnet/public/lib/fsl/threading/thread.h -FILE: ../../../garnet/public/lib/fsl/threading/thread_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/file_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/sized_vmo.cc -FILE: ../../../garnet/public/lib/fsl/vmo/sized_vmo.h -FILE: ../../../garnet/public/lib/fxl/files/directory_unittest.cc -FILE: ../../../garnet/public/lib/fxl/files/file_descriptor_unittest.cc -FILE: ../../../garnet/public/lib/fxl/files/path_win.cc -FILE: ../../../garnet/public/lib/fxl/files/symlink_win.cc -FILE: ../../../garnet/public/lib/fxl/functional/apply.h -FILE: ../../../garnet/public/lib/fxl/functional/apply_unittest.cc -FILE: ../../../garnet/public/lib/fxl/functional/auto_call_unittest.cc -FILE: ../../../garnet/public/lib/fxl/functional/cancelable_callback.h -FILE: ../../../garnet/public/lib/fxl/functional/cancelable_callback_unittest.cc -FILE: ../../../garnet/public/lib/fxl/fxl_export.h -FILE: ../../../garnet/public/lib/fxl/inttypes.h -FILE: ../../../garnet/public/lib/fxl/log_settings_command_line.cc -FILE: ../../../garnet/public/lib/fxl/log_settings_command_line.h -FILE: ../../../garnet/public/lib/fxl/portable_unistd.h -FILE: ../../../garnet/public/lib/fxl/random/rand_unittest.cc -FILE: ../../../garnet/public/lib/fxl/random/uuid_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/join_strings.h -FILE: ../../../garnet/public/lib/fxl/strings/join_strings_unittest.cc -FILE: ../../../garnet/public/lib/fxl/threading/thread.cc -FILE: ../../../garnet/public/lib/fxl/threading/thread.h -FILE: ../../../garnet/public/lib/fxl/threading/thread_unittest.cc -FILE: ../../../garnet/public/lib/fxl/time/stopwatch_unittest.cc -FILE: ../../../garnet/public/lib/fxl/time/time_delta_unittest.cc -FILE: ../../../garnet/public/lib/fxl/time/time_point_unittest.cc -FILE: ../../../garnet/public/lib/images/fidl/image_info.fidl -FILE: ../../../garnet/public/lib/images/fidl/image_pipe.fidl -FILE: ../../../garnet/public/lib/images/fidl/memory_type.fidl -FILE: ../../../garnet/public/lib/mdns/fidl/mdns.fidl -FILE: ../../../garnet/public/lib/media/audio/lpcm_output_stream.cc -FILE: ../../../garnet/public/lib/media/audio/lpcm_output_stream.h -FILE: ../../../garnet/public/lib/media/audio/perceived_level.h -FILE: ../../../garnet/public/lib/media/audio/types.cc -FILE: ../../../garnet/public/lib/media/audio/types.h -FILE: ../../../garnet/public/lib/media/c/audio.h -FILE: ../../../garnet/public/lib/media/fidl/audio_capturer.fidl -FILE: ../../../garnet/public/lib/media/fidl/audio_policy_service.fidl -FILE: ../../../garnet/public/lib/media/fidl/net_media_player.fidl -FILE: ../../../garnet/public/lib/media/fidl/net_media_service.fidl -FILE: ../../../garnet/public/lib/netconnector/cpp/net_stub_responder.h -FILE: ../../../garnet/public/lib/netstack/c/netconfig.h -FILE: ../../../garnet/public/lib/power/fidl/power_manager.fidl -FILE: ../../../garnet/public/lib/svc/cpp/service_namespace.h -FILE: ../../../garnet/public/lib/svc/cpp/service_provider_bridge.cc -FILE: ../../../garnet/public/lib/svc/cpp/service_provider_bridge.h -FILE: ../../../garnet/public/lib/svc/cpp/services.cc -FILE: ../../../garnet/public/lib/svc/cpp/services.h -FILE: ../../../garnet/public/lib/svc/go/src/svc/svcfs/svcfs.go -FILE: ../../../garnet/public/lib/svc/go/src/svc/svcns/svcns.go -FILE: ../../../garnet/public/lib/test_runner/cpp/gtest_main.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/reporting/gtest_listener.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/reporting/gtest_listener.h -FILE: ../../../garnet/public/lib/test_runner/cpp/reporting/reporter.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/reporting/reporter.h -FILE: ../../../garnet/public/lib/test_runner/cpp/test_runner.h -FILE: ../../../garnet/public/lib/test_runner/cpp/test_runner_store_impl.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/test_runner_store_impl.h -FILE: ../../../garnet/public/lib/time_service/fidl/time_service.fidl -FILE: ../../../garnet/public/lib/ui/fun/sketchy/fidl/canvas.fidl -FILE: ../../../garnet/public/lib/ui/fun/sketchy/fidl/ops.fidl -FILE: ../../../garnet/public/lib/ui/fun/sketchy/fidl/resources.fidl -FILE: ../../../garnet/public/lib/ui/fun/sketchy/fidl/types.fidl -FILE: ../../../garnet/public/lib/ui/input/device_state.cc -FILE: ../../../garnet/public/lib/ui/input/device_state.h -FILE: ../../../garnet/public/lib/ui/input/fidl/ime_service.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/input_device_registry.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/input_reports.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/text_editing.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/text_input.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/usages.fidl -FILE: ../../../garnet/public/lib/ui/input/input_device_impl.cc -FILE: ../../../garnet/public/lib/ui/input/input_device_impl.h -FILE: ../../../garnet/public/lib/ui/mozart/fidl/presentation_info.fidl -FILE: ../../../garnet/public/lib/ui/presentation/fidl/presentation.fidl -FILE: ../../../garnet/public/lib/ui/scenic/client/host_image_cycler.cc -FILE: ../../../garnet/public/lib/ui/scenic/client/host_image_cycler.h -FILE: ../../../garnet/public/lib/ui/scenic/client/host_memory.cc -FILE: ../../../garnet/public/lib/ui/scenic/client/host_memory.h -FILE: ../../../garnet/public/lib/ui/scenic/client/resources.cc -FILE: ../../../garnet/public/lib/ui/scenic/client/resources.h -FILE: ../../../garnet/public/lib/ui/scenic/client/session.cc -FILE: ../../../garnet/public/lib/ui/scenic/client/session.h -FILE: ../../../garnet/public/lib/ui/scenic/fidl/display_info.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/events.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/hit.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/nodes.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/ops.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/renderer.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/resources.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/shapes.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl/types.fidl -FILE: ../../../garnet/public/lib/ui/scenic/fidl_helpers.cc -FILE: ../../../garnet/public/lib/ui/scenic/fidl_helpers.h -FILE: ../../../garnet/public/lib/ui/scenic/types.h -FILE: ../../../garnet/public/lib/ui/sketchy/canvas.cc -FILE: ../../../garnet/public/lib/ui/sketchy/canvas.h -FILE: ../../../garnet/public/lib/ui/sketchy/glm_hack.h -FILE: ../../../garnet/public/lib/ui/sketchy/resources.cc -FILE: ../../../garnet/public/lib/ui/sketchy/resources.h -FILE: ../../../garnet/public/lib/ui/sketchy/types.cc -FILE: ../../../garnet/public/lib/ui/sketchy/types.h -FILE: ../../../garnet/public/lib/wlan/fidl/wlan_mlme.fidl -FILE: ../../../garnet/public/lib/wlan/fidl/wlan_mlme_ext.fidl -FILE: ../../../garnet/public/lib/wlan/fidl/wlan_service.fidl -FILE: ../../../garnet/tools/vboot_reference/futility_cmds.c -FILE: ../../../garnet/tools/vboot_reference/uuid/uuid.cc -FILE: ../../../garnet/tools/vboot_reference/uuid/uuid.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/fidl/cpp/bindings/internal/array_internal.cc + ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/array.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/array_internal.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/array_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/bindings_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/bindings_serialization.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/bindings_serialization.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/buffer.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/connector.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/connector.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_builder.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_builder.h ----------------------------------------------------------------------------------------------------- -Copyright 2013 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/fidl/cpp/bindings/internal/array_serialization.h + ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/binding.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/binding_set.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/interface_ptr.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/interface_ptr_set.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/interface_request.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/array_serialization.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/bounds_checker.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/bounds_checker.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/fixed_buffer.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/fixed_buffer.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/interface_ptr_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/map_data_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/map_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/map_serialization.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_header_validator.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_header_validator.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_internal.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/router.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/router.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/shared_data.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/string_serialization.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/string_serialization.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/template_util.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/validate_params.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/validation_errors.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/validation_errors.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/map.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/message.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/string.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/struct_ptr.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/type_converter.h -FILE: ../../../garnet/public/lib/ui/input/fidl/input_event_constants.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/input_events.fidl ----------------------------------------------------------------------------------------------------- -Copyright 2014 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/fidl/cpp/bindings/internal/iterator_util.h + ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/interface_handle.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/iterator_util.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/map_serialization_forward.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_validation.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_validation.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/union_accessor.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/validation_util.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/validation_util.h -FILE: ../../../garnet/public/lib/icu_data/cpp/constants.h -FILE: ../../../garnet/public/lib/icu_data/cpp/icu_data.cc -FILE: ../../../garnet/public/lib/icu_data/cpp/icu_data.h -FILE: ../../../garnet/public/lib/ui/input/fidl/input_connection.fidl -FILE: ../../../garnet/public/lib/ui/input/fidl/input_dispatcher.fidl -FILE: ../../../garnet/public/lib/ui/view_framework/base_view.cc -FILE: ../../../garnet/public/lib/ui/view_framework/base_view.h -FILE: ../../../garnet/public/lib/ui/view_framework/view_provider_service.cc -FILE: ../../../garnet/public/lib/ui/view_framework/view_provider_service.h -FILE: ../../../garnet/public/lib/ui/views/cpp/formatting.cc -FILE: ../../../garnet/public/lib/ui/views/cpp/formatting.h -FILE: ../../../garnet/public/lib/ui/views/fidl/view_manager.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/view_provider.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/view_trees.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/views.fidl ----------------------------------------------------------------------------------------------------- -Copyright 2015 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/fidl/cpp/bindings/internal/type_converters.cc + ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/binding.h -FILE: ../../../garnet/public/lib/fidl/cpp/binding_set.h -FILE: ../../../garnet/public/lib/fidl/cpp/binding_set_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/binding_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/type_converters.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/type_converters.h -FILE: ../../../garnet/public/lib/fidl/cpp/clone.cc -FILE: ../../../garnet/public/lib/fidl/cpp/clone.h -FILE: ../../../garnet/public/lib/fidl/cpp/clone_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/coding_traits.cc -FILE: ../../../garnet/public/lib/fidl/cpp/coding_traits.h -FILE: ../../../garnet/public/lib/fidl/cpp/decoder.cc -FILE: ../../../garnet/public/lib/fidl/cpp/decoder.h -FILE: ../../../garnet/public/lib/fidl/cpp/encoder.cc -FILE: ../../../garnet/public/lib/fidl/cpp/encoder.h -FILE: ../../../garnet/public/lib/fidl/cpp/interface_handle.h -FILE: ../../../garnet/public/lib/fidl/cpp/interface_handle_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/interface_ptr.h -FILE: ../../../garnet/public/lib/fidl/cpp/interface_ptr_set.h -FILE: ../../../garnet/public/lib/fidl/cpp/interface_ptr_set_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/interface_ptr_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/interface_request.h -FILE: ../../../garnet/public/lib/fidl/cpp/interface_request_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/header.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/implementation.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/message_handler.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/message_handler.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/message_reader.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/message_reader.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/message_reader_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/pending_response.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/pending_response.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/proxy_controller.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/proxy_controller.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/proxy_controller_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/stub.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/stub.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/stub_controller.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/stub_controller.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/stub_controller_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/synchronous_proxy.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/synchronous_proxy.h -FILE: ../../../garnet/public/lib/fidl/cpp/internal/weak_stub_controller.cc -FILE: ../../../garnet/public/lib/fidl/cpp/internal/weak_stub_controller.h -FILE: ../../../garnet/public/lib/fidl/cpp/string.cc -FILE: ../../../garnet/public/lib/fidl/cpp/string.h -FILE: ../../../garnet/public/lib/fidl/cpp/string_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/synchronous_interface_ptr.h -FILE: ../../../garnet/public/lib/fidl/cpp/synchronous_interface_ptr_unittest.cc -FILE: ../../../garnet/public/lib/fidl/cpp/traits.h -FILE: ../../../garnet/public/lib/fidl/cpp/vector.h -FILE: ../../../garnet/public/lib/fidl/cpp/vector_unittest.cc -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings2/encoding.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings2/encoding_test.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings2/message.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings2/test_types.go -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/encoding2.rs -FILE: ../../../garnet/public/lib/fsl/syslogger/init.cc -FILE: ../../../garnet/public/lib/fsl/syslogger/init.h -FILE: ../../../garnet/public/lib/fsl/syslogger/init_unittest.cc -FILE: ../../../garnet/public/lib/fxl/type_converter.h -FILE: ../../../garnet/public/lib/gralloc/fidl/gralloc.fidl -FILE: ../../../garnet/public/lib/images/fidl/encoded_image.fidl -FILE: ../../../garnet/public/lib/logger/fidl/logger.fidl -FILE: ../../../garnet/public/lib/mdns/cpp/service_subscriber.cc -FILE: ../../../garnet/public/lib/mdns/cpp/service_subscriber.h -FILE: ../../../garnet/public/lib/syslog/cpp/logger.cc -FILE: ../../../garnet/public/lib/syslog/cpp/logger.h -FILE: ../../../garnet/public/lib/syslog/cpp/logger_init.cc -FILE: ../../../garnet/public/lib/syslog/cpp/logger_unittest.cc -FILE: ../../../garnet/public/lib/ui/mozart/fidl/commands.fidl -FILE: ../../../garnet/public/lib/ui/mozart/fidl/dummy_system/commands.fidl -FILE: ../../../garnet/public/lib/ui/mozart/fidl/dummy_system/events.fidl -FILE: ../../../garnet/public/lib/ui/mozart/fidl/events.fidl -FILE: ../../../garnet/public/lib/ui/mozart/fidl/mozart.fidl -FILE: ../../../garnet/public/lib/ui/mozart/fidl/session.fidl -FILE: ../../../garnet/public/lib/ui/presentation/fidl/display_usage.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/commands.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/events.fidl -FILE: ../../../garnet/public/lib/xi/fidl/xi.fidl ----------------------------------------------------------------------------------------------------- -Copyright 2018 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/media/audio/lpcm_payload.cc + ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/media/audio/lpcm_payload.cc -FILE: ../../../garnet/public/lib/media/audio/lpcm_payload.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 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. - * 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: garnet -ORIGIN: ../../../garnet/public/lib/netstack/fidl/netstack.fidl + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/netstack/fidl/netstack.fidl ----------------------------------------------------------------------------------------------------- -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: garnet -ORIGIN: ../../../garnet/public/lib/svc/cpp/service_namespace.cc + ../../../LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/svc/cpp/service_namespace.cc ----------------------------------------------------------------------------------------------------- -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: garnet -ORIGIN: ../../../third_party/icu/scripts/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/async_waiter.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/connector.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/decoder.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/encoder.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/interface.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/message.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/router.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/stub.go -FILE: ../../../garnet/public/lib/fidl/go/src/fidl/bindings/util.go -FILE: ../../../garnet/public/lib/netstack/fidl/net_address.fidl -FILE: ../../../garnet/public/lib/network/fidl/http_header.fidl -FILE: ../../../garnet/public/lib/network/fidl/network_error.fidl -FILE: ../../../garnet/public/lib/network/fidl/network_service.fidl -FILE: ../../../garnet/public/lib/network/fidl/url_loader.fidl -FILE: ../../../garnet/public/lib/network/fidl/url_request.fidl -FILE: ../../../garnet/public/lib/network/fidl/url_response.fidl ----------------------------------------------------------------------------------------------------- -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: garnet -ORIGIN: ../../../topaz/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/formatting.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/internal/message_validator.cc -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/message_validator.h -FILE: ../../../garnet/public/lib/fidl/cpp/bindings/synchronous_interface_ptr.h -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/client.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/encoding.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/error.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/interface.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/lib.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/message.rs -FILE: ../../../garnet/public/lib/fidl/rust/fidl/src/server.rs -FILE: ../../../garnet/public/lib/fonts/fidl/font_provider.fidl -FILE: ../../../garnet/public/lib/fsl/handles/object_info.cc -FILE: ../../../garnet/public/lib/fsl/handles/object_info.h -FILE: ../../../garnet/public/lib/fsl/handles/object_info_unittest.cc -FILE: ../../../garnet/public/lib/fsl/io/device_watcher.cc -FILE: ../../../garnet/public/lib/fsl/io/device_watcher.h -FILE: ../../../garnet/public/lib/fsl/io/redirection.cc -FILE: ../../../garnet/public/lib/fsl/io/redirection.h -FILE: ../../../garnet/public/lib/fsl/io/redirection_unittest.cc -FILE: ../../../garnet/public/lib/fsl/socket/blocking_drain.cc -FILE: ../../../garnet/public/lib/fsl/socket/blocking_drain.h -FILE: ../../../garnet/public/lib/fsl/socket/files.cc -FILE: ../../../garnet/public/lib/fsl/socket/files.h -FILE: ../../../garnet/public/lib/fsl/socket/files_unittest.cc -FILE: ../../../garnet/public/lib/fsl/socket/socket_drainer.cc -FILE: ../../../garnet/public/lib/fsl/socket/socket_drainer.h -FILE: ../../../garnet/public/lib/fsl/socket/socket_drainer_unittest.cc -FILE: ../../../garnet/public/lib/fsl/socket/strings.cc -FILE: ../../../garnet/public/lib/fsl/socket/strings.h -FILE: ../../../garnet/public/lib/fsl/tasks/incoming_task_queue.cc -FILE: ../../../garnet/public/lib/fsl/tasks/incoming_task_queue.h -FILE: ../../../garnet/public/lib/fsl/tasks/message_loop.cc -FILE: ../../../garnet/public/lib/fsl/tasks/message_loop.h -FILE: ../../../garnet/public/lib/fsl/tasks/message_loop_handler.cc -FILE: ../../../garnet/public/lib/fsl/tasks/message_loop_handler.h -FILE: ../../../garnet/public/lib/fsl/tasks/message_loop_unittest.cc -FILE: ../../../garnet/public/lib/fsl/threading/create_thread.cc -FILE: ../../../garnet/public/lib/fsl/threading/create_thread.h -FILE: ../../../garnet/public/lib/fsl/threading/create_thread_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/file.cc -FILE: ../../../garnet/public/lib/fsl/vmo/file.h -FILE: ../../../garnet/public/lib/fsl/vmo/shared_vmo.cc -FILE: ../../../garnet/public/lib/fsl/vmo/shared_vmo.h -FILE: ../../../garnet/public/lib/fsl/vmo/shared_vmo_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/strings.h -FILE: ../../../garnet/public/lib/fsl/vmo/strings_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/vector.h -FILE: ../../../garnet/public/lib/fsl/vmo/vector_unittest.cc -FILE: ../../../garnet/public/lib/fsl/vmo/vmo.cc -FILE: ../../../garnet/public/lib/fxl/arraysize.h -FILE: ../../../garnet/public/lib/fxl/arraysize_unittest.cc -FILE: ../../../garnet/public/lib/fxl/build_config.h -FILE: ../../../garnet/public/lib/fxl/command_line.cc -FILE: ../../../garnet/public/lib/fxl/command_line.h -FILE: ../../../garnet/public/lib/fxl/command_line_unittest.cc -FILE: ../../../garnet/public/lib/fxl/compiler_specific.h -FILE: ../../../garnet/public/lib/fxl/debug/debugger.cc -FILE: ../../../garnet/public/lib/fxl/debug/debugger.h -FILE: ../../../garnet/public/lib/fxl/files/directory.cc -FILE: ../../../garnet/public/lib/fxl/files/directory.h -FILE: ../../../garnet/public/lib/fxl/files/eintr_wrapper.h -FILE: ../../../garnet/public/lib/fxl/files/file.cc -FILE: ../../../garnet/public/lib/fxl/files/file.h -FILE: ../../../garnet/public/lib/fxl/files/file_descriptor.cc -FILE: ../../../garnet/public/lib/fxl/files/file_descriptor.h -FILE: ../../../garnet/public/lib/fxl/files/file_unittest.cc -FILE: ../../../garnet/public/lib/fxl/files/path.h -FILE: ../../../garnet/public/lib/fxl/files/path_posix.cc -FILE: ../../../garnet/public/lib/fxl/files/path_unittest.cc -FILE: ../../../garnet/public/lib/fxl/files/scoped_temp_dir.cc -FILE: ../../../garnet/public/lib/fxl/files/scoped_temp_dir.h -FILE: ../../../garnet/public/lib/fxl/files/scoped_temp_dir_unittest.cc -FILE: ../../../garnet/public/lib/fxl/files/symlink.h -FILE: ../../../garnet/public/lib/fxl/files/symlink_posix.cc -FILE: ../../../garnet/public/lib/fxl/files/unique_fd.cc -FILE: ../../../garnet/public/lib/fxl/files/unique_fd.h -FILE: ../../../garnet/public/lib/fxl/functional/auto_call.h -FILE: ../../../garnet/public/lib/fxl/functional/closure.h -FILE: ../../../garnet/public/lib/fxl/functional/make_copyable.h -FILE: ../../../garnet/public/lib/fxl/functional/make_copyable_unittest.cc -FILE: ../../../garnet/public/lib/fxl/log_level.h -FILE: ../../../garnet/public/lib/fxl/log_settings.cc -FILE: ../../../garnet/public/lib/fxl/log_settings.h -FILE: ../../../garnet/public/lib/fxl/log_settings_state.cc -FILE: ../../../garnet/public/lib/fxl/log_settings_unittest.cc -FILE: ../../../garnet/public/lib/fxl/logging.cc -FILE: ../../../garnet/public/lib/fxl/logging.h -FILE: ../../../garnet/public/lib/fxl/macros.h -FILE: ../../../garnet/public/lib/fxl/memory/ref_counted.h -FILE: ../../../garnet/public/lib/fxl/memory/ref_counted_internal.h -FILE: ../../../garnet/public/lib/fxl/memory/ref_counted_unittest.cc -FILE: ../../../garnet/public/lib/fxl/memory/ref_ptr.h -FILE: ../../../garnet/public/lib/fxl/memory/ref_ptr_internal.h -FILE: ../../../garnet/public/lib/fxl/memory/unique_object.h -FILE: ../../../garnet/public/lib/fxl/memory/weak_ptr.h -FILE: ../../../garnet/public/lib/fxl/memory/weak_ptr_internal.cc -FILE: ../../../garnet/public/lib/fxl/memory/weak_ptr_internal.h -FILE: ../../../garnet/public/lib/fxl/memory/weak_ptr_unittest.cc -FILE: ../../../garnet/public/lib/fxl/random/rand.cc -FILE: ../../../garnet/public/lib/fxl/random/rand.h -FILE: ../../../garnet/public/lib/fxl/random/uuid.cc -FILE: ../../../garnet/public/lib/fxl/random/uuid.h -FILE: ../../../garnet/public/lib/fxl/strings/ascii.cc -FILE: ../../../garnet/public/lib/fxl/strings/ascii.h -FILE: ../../../garnet/public/lib/fxl/strings/ascii_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/concatenate.cc -FILE: ../../../garnet/public/lib/fxl/strings/concatenate.h -FILE: ../../../garnet/public/lib/fxl/strings/concatenate_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/split_string.cc -FILE: ../../../garnet/public/lib/fxl/strings/split_string.h -FILE: ../../../garnet/public/lib/fxl/strings/split_string_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_number_conversions.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_number_conversions.h -FILE: ../../../garnet/public/lib/fxl/strings/string_number_conversions_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_printf.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_printf.h -FILE: ../../../garnet/public/lib/fxl/strings/string_printf_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_view.cc -FILE: ../../../garnet/public/lib/fxl/strings/string_view.h -FILE: ../../../garnet/public/lib/fxl/strings/string_view_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/trim.cc -FILE: ../../../garnet/public/lib/fxl/strings/trim.h -FILE: ../../../garnet/public/lib/fxl/strings/trim_unittest.cc -FILE: ../../../garnet/public/lib/fxl/strings/utf_codecs.cc -FILE: ../../../garnet/public/lib/fxl/strings/utf_codecs.h -FILE: ../../../garnet/public/lib/fxl/synchronization/sleep.cc -FILE: ../../../garnet/public/lib/fxl/synchronization/sleep.h -FILE: ../../../garnet/public/lib/fxl/synchronization/thread_annotations.h -FILE: ../../../garnet/public/lib/fxl/synchronization/thread_annotations_unittest.cc -FILE: ../../../garnet/public/lib/fxl/synchronization/thread_checker.h -FILE: ../../../garnet/public/lib/fxl/synchronization/thread_checker_unittest.cc -FILE: ../../../garnet/public/lib/fxl/synchronization/waitable_event.cc -FILE: ../../../garnet/public/lib/fxl/synchronization/waitable_event.h -FILE: ../../../garnet/public/lib/fxl/synchronization/waitable_event_unittest.cc -FILE: ../../../garnet/public/lib/fxl/tasks/one_shot_timer.cc -FILE: ../../../garnet/public/lib/fxl/tasks/one_shot_timer.h -FILE: ../../../garnet/public/lib/fxl/tasks/one_shot_timer_unittest.cc -FILE: ../../../garnet/public/lib/fxl/tasks/task_runner.cc -FILE: ../../../garnet/public/lib/fxl/tasks/task_runner.h -FILE: ../../../garnet/public/lib/fxl/time/stopwatch.cc -FILE: ../../../garnet/public/lib/fxl/time/stopwatch.h -FILE: ../../../garnet/public/lib/fxl/time/time_delta.h -FILE: ../../../garnet/public/lib/fxl/time/time_point.cc -FILE: ../../../garnet/public/lib/fxl/time/time_point.h -FILE: ../../../garnet/public/lib/fxl/time/time_printers.cc -FILE: ../../../garnet/public/lib/fxl/time/time_unittest.cc -FILE: ../../../garnet/public/lib/icu_data/fidl/icu_data.fidl -FILE: ../../../garnet/public/lib/media/audio/perceived_level.cc -FILE: ../../../garnet/public/lib/media/fidl/audio_renderer.fidl -FILE: ../../../garnet/public/lib/media/fidl/audio_server.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_capturer.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_metadata.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_player.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_renderer.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_result.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_sink.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_source.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_transport.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_type_converter.fidl -FILE: ../../../garnet/public/lib/media/fidl/media_types.fidl -FILE: ../../../garnet/public/lib/media/fidl/problem.fidl -FILE: ../../../garnet/public/lib/media/fidl/seeking_reader.fidl -FILE: ../../../garnet/public/lib/media/fidl/timeline_controller.fidl -FILE: ../../../garnet/public/lib/media/fidl/timelines.fidl -FILE: ../../../garnet/public/lib/media/fidl/tts_service.fidl -FILE: ../../../garnet/public/lib/media/fidl/video_renderer.fidl -FILE: ../../../garnet/public/lib/media/timeline/timeline.h -FILE: ../../../garnet/public/lib/media/timeline/timeline_function.cc -FILE: ../../../garnet/public/lib/media/timeline/timeline_function.h -FILE: ../../../garnet/public/lib/media/timeline/timeline_rate.cc -FILE: ../../../garnet/public/lib/media/timeline/timeline_rate.h -FILE: ../../../garnet/public/lib/media/transport/fifo_allocator.cc -FILE: ../../../garnet/public/lib/media/transport/fifo_allocator.h -FILE: ../../../garnet/public/lib/media/transport/mapped_shared_buffer.cc -FILE: ../../../garnet/public/lib/media/transport/mapped_shared_buffer.h -FILE: ../../../garnet/public/lib/media/transport/media_packet_consumer_base.cc -FILE: ../../../garnet/public/lib/media/transport/media_packet_consumer_base.h -FILE: ../../../garnet/public/lib/media/transport/media_packet_producer_base.cc -FILE: ../../../garnet/public/lib/media/transport/media_packet_producer_base.h -FILE: ../../../garnet/public/lib/media/transport/shared_buffer_set.cc -FILE: ../../../garnet/public/lib/media/transport/shared_buffer_set.h -FILE: ../../../garnet/public/lib/media/transport/shared_buffer_set_allocator.cc -FILE: ../../../garnet/public/lib/media/transport/shared_buffer_set_allocator.h -FILE: ../../../garnet/public/lib/netconnector/cpp/message_relay.cc -FILE: ../../../garnet/public/lib/netconnector/cpp/message_relay.h -FILE: ../../../garnet/public/lib/netconnector/fidl/netconnector.fidl -FILE: ../../../garnet/public/lib/network/fidl/url_body.fidl -FILE: ../../../garnet/public/lib/syslog/cpp/run_all_unittests.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/scope.cc -FILE: ../../../garnet/public/lib/test_runner/cpp/scope.h -FILE: ../../../garnet/public/lib/test_runner/cpp/test_runner.cc -FILE: ../../../garnet/public/lib/test_runner/fidl/test_runner.fidl -FILE: ../../../garnet/public/lib/tracing/fidl/trace_controller.fidl -FILE: ../../../garnet/public/lib/tracing/fidl/trace_provider.fidl -FILE: ../../../garnet/public/lib/tracing/fidl/trace_registry.fidl -FILE: ../../../garnet/public/lib/ui/geometry/cpp/formatting.cc -FILE: ../../../garnet/public/lib/ui/geometry/cpp/formatting.h -FILE: ../../../garnet/public/lib/ui/geometry/cpp/geometry_util.cc -FILE: ../../../garnet/public/lib/ui/geometry/cpp/geometry_util.h -FILE: ../../../garnet/public/lib/ui/geometry/fidl/geometry.fidl -FILE: ../../../garnet/public/lib/ui/input/cpp/formatting.cc -FILE: ../../../garnet/public/lib/ui/input/cpp/formatting.h -FILE: ../../../garnet/public/lib/ui/presentation/fidl/presenter.fidl -FILE: ../../../garnet/public/lib/ui/view_framework/view_provider_app.cc -FILE: ../../../garnet/public/lib/ui/view_framework/view_provider_app.h -FILE: ../../../garnet/public/lib/ui/views/fidl/view_containers.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/view_properties.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/view_token.fidl -FILE: ../../../garnet/public/lib/ui/views/fidl/view_tree_token.fidl -FILE: ../../../garnet/public/lib/zip/create_unzipper.cc -FILE: ../../../garnet/public/lib/zip/create_unzipper.h -FILE: ../../../garnet/public/lib/zip/memory_io.cc -FILE: ../../../garnet/public/lib/zip/memory_io.h -FILE: ../../../garnet/public/lib/zip/unique_unzipper.cc -FILE: ../../../garnet/public/lib/zip/unique_unzipper.h -FILE: ../../../garnet/public/lib/zip/unique_zipper.cc -FILE: ../../../garnet/public/lib/zip/unique_zipper.h -FILE: ../../../garnet/public/lib/zip/unzipper.cc -FILE: ../../../garnet/public/lib/zip/unzipper.h -FILE: ../../../garnet/public/lib/zip/zipper.cc -FILE: ../../../garnet/public/lib/zip/zipper.h ----------------------------------------------------------------------------------------------------- -Copyright 2016 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. - * 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: ../../../garnet/public/lib/fxl/third_party/icu/LICENSE -TYPE: LicenseType.unknown -FILE: ../../../garnet/public/lib/fxl/third_party/icu/icu_utf.cc -FILE: ../../../garnet/public/lib/fxl/third_party/icu/icu_utf.h ----------------------------------------------------------------------------------------------------- -ICU License - ICU 1.8.1 and later - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1995-2009 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. -==================================================================================================== -Total license count: 12 diff --git a/ci/licenses_golden/licenses_lib b/ci/licenses_golden/licenses_lib deleted file mode 100644 index 53bb313e2d357..0000000000000 --- a/ci/licenses_golden/licenses_lib +++ /dev/null @@ -1,158 +0,0 @@ -Signature: c6853c1bbc8db501fa71fb05acb5f2ba - -UNUSED LICENSES: - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -USED LICENSES: - -==================================================================================================== -LIBRARY: lib -ORIGIN: ../../../garnet/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../lib/tonic/dart_list.cc -FILE: ../../../lib/tonic/dart_list.h ----------------------------------------------------------------------------------------------------- -Copyright 2017 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. - * 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: lib -ORIGIN: ../../../lib/tonic/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../lib/tonic/converter/dart_converter.cc -FILE: ../../../lib/tonic/converter/dart_converter.h -FILE: ../../../lib/tonic/dart_message_handler.cc -FILE: ../../../lib/tonic/dart_message_handler.h -FILE: ../../../lib/tonic/dart_microtask_queue.cc -FILE: ../../../lib/tonic/dart_microtask_queue.h -FILE: ../../../lib/tonic/dart_sticky_error.cc -FILE: ../../../lib/tonic/dart_sticky_error.h -FILE: ../../../lib/tonic/debugger/dart_debugger.cc -FILE: ../../../lib/tonic/debugger/dart_debugger.h -FILE: ../../../lib/tonic/file_loader/file_loader.cc -FILE: ../../../lib/tonic/file_loader/file_loader.h -FILE: ../../../lib/tonic/logging/dart_error.cc -FILE: ../../../lib/tonic/logging/dart_error.h -FILE: ../../../lib/tonic/logging/dart_invoke.cc -FILE: ../../../lib/tonic/logging/dart_invoke.h -FILE: ../../../lib/tonic/parsers/packages_map.cc -FILE: ../../../lib/tonic/parsers/packages_map.h -FILE: ../../../lib/tonic/scopes/dart_api_scope.h -FILE: ../../../lib/tonic/scopes/dart_isolate_scope.cc -FILE: ../../../lib/tonic/scopes/dart_isolate_scope.h -FILE: ../../../lib/tonic/typed_data/dart_byte_data.h -FILE: ../../../lib/tonic/typed_data/int32_list.h -FILE: ../../../lib/tonic/typed_data/uint8_list.h ----------------------------------------------------------------------------------------------------- -Copyright 2016 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. - * 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: lib -ORIGIN: ../../../lib/tonic/typed_data/dart_byte_data.cc + ../../../lib/tonic/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../lib/tonic/dart_args.h -FILE: ../../../lib/tonic/dart_binding_macros.h -FILE: ../../../lib/tonic/dart_class_library.cc -FILE: ../../../lib/tonic/dart_class_library.h -FILE: ../../../lib/tonic/dart_class_provider.cc -FILE: ../../../lib/tonic/dart_class_provider.h -FILE: ../../../lib/tonic/dart_library_natives.cc -FILE: ../../../lib/tonic/dart_library_natives.h -FILE: ../../../lib/tonic/dart_persistent_value.cc -FILE: ../../../lib/tonic/dart_persistent_value.h -FILE: ../../../lib/tonic/dart_state.cc -FILE: ../../../lib/tonic/dart_state.h -FILE: ../../../lib/tonic/dart_wrappable.cc -FILE: ../../../lib/tonic/dart_wrappable.h -FILE: ../../../lib/tonic/dart_wrapper_info.h -FILE: ../../../lib/tonic/typed_data/dart_byte_data.cc -FILE: ../../../lib/tonic/typed_data/float32_list.cc -FILE: ../../../lib/tonic/typed_data/float32_list.h -FILE: ../../../lib/tonic/typed_data/float64_list.cc -FILE: ../../../lib/tonic/typed_data/float64_list.h -FILE: ../../../lib/tonic/typed_data/int32_list.cc -FILE: ../../../lib/tonic/typed_data/uint8_list.cc ----------------------------------------------------------------------------------------------------- -Copyright 2015 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. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== -Total license count: 3 diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a6e5f1f7e4750..9c936362aadc5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,7 +1,174 @@ -Signature: 34a318a5a1174afe381aa7e505a9474c +Signature: 030f180c2ea7f19ce2d731d1d314400b UNUSED LICENSES: +==================================================================================================== +ORIGIN: ../../../third_party/skia/third_party/etc1/LICENSE +TYPE: LicenseType.apache +---------------------------------------------------------------------------------------------------- +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that +You changed the files; and +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 +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 +==================================================================================================== + ==================================================================================================== ORIGIN: ../../../third_party/skia/third_party/gif/LICENSE TYPE: LicenseType.unknown @@ -44,515 +211,701 @@ the terms of any one of the MPL, the GPL or the LGPL. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USED LICENSES: +==================================================================================================== +LIBRARY: etc1 +LIBRARY: skia +LIBRARY: vulkan +ORIGIN: ../../../flutter/third_party/txt/LICENSE +TYPE: LicenseType.apache +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vk_platform.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_android.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_core.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_ios.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_macos.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_win32.h +FILE: ../../../third_party/skia/include/third_party/vulkan/vulkan/vulkan_xcb.h +FILE: ../../../third_party/skia/src/images/SkWebpEncoder.cpp +FILE: ../../../third_party/skia/third_party/etc1/etc1.cpp +FILE: ../../../third_party/skia/third_party/etc1/etc1.h +---------------------------------------------------------------------------------------------------- +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. +==================================================================================================== + ==================================================================================================== LIBRARY: gif ORIGIN: ../../../third_party/skia/third_party/gif/SkGifImageReader.cpp -TYPE: LicenseType.lgpl +TYPE: LicenseType.mpl FILE: ../../../third_party/skia/third_party/gif/SkGifImageReader.cpp FILE: ../../../third_party/skia/third_party/gif/SkGifImageReader.h ---------------------------------------------------------------------------------------------------- -GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! +MOZILLA PUBLIC LICENSE + Version 1.1 + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the MPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (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.mozilla.org/MPL + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] ==================================================================================================== ==================================================================================================== @@ -582,125 +935,6 @@ may be used: CompuServe Incorporated." ==================================================================================================== -==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/skia/third_party/harfbuzz/hb-buffer-deserialize-json.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-buffer-deserialize-json.hh -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-buffer-deserialize-text.hh ----------------------------------------------------------------------------------------------------- -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/skia/third_party/harfbuzz/hb-ot-shape-complex-indic-machine.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-ot-shape-complex-indic-machine.hh -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-ot-shape-complex-myanmar-machine.hh ----------------------------------------------------------------------------------------------------- -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/skia/third_party/harfbuzz/hb-ot-shape-complex-use-machine.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-ot-shape-complex-use-machine.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/skia/third_party/harfbuzz/hb-version.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/skia/third_party/harfbuzz/hb-version.h ----------------------------------------------------------------------------------------------------- -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: libsdl LIBRARY: skia @@ -709,7 +943,7 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/AndroidCodecBench.cpp FILE: ../../../third_party/skia/bench/AndroidCodecBench.h FILE: ../../../third_party/skia/bench/DrawLatticeBench.cpp -FILE: ../../../third_party/skia/bench/EncoderBench.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 @@ -718,7 +952,6 @@ 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/SkRasterPipelineBench.cpp FILE: ../../../third_party/skia/bench/StreamBench.cpp FILE: ../../../third_party/skia/bench/SwizzleBench.cpp FILE: ../../../third_party/skia/bench/TileImageFilterBench.cpp @@ -796,7 +1029,6 @@ 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/gamut.cpp FILE: ../../../third_party/skia/gm/hardstop_gradients.cpp FILE: ../../../third_party/skia/gm/imagemakewithfilter.cpp FILE: ../../../third_party/skia/gm/imagemasksubset.cpp @@ -826,12 +1058,11 @@ 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/SkYUVSizeInfo.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/GrVkDefines.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 @@ -861,8 +1092,6 @@ 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/SkRawAdapterCodec.cpp -FILE: ../../../third_party/skia/src/codec/SkRawAdapterCodec.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 @@ -951,7 +1180,6 @@ FILE: ../../../third_party/skia/src/compute/skc/styling.c FILE: ../../../third_party/skia/src/compute/skc/styling.h FILE: ../../../third_party/skia/src/compute/skc/styling_types.h FILE: ../../../third_party/skia/src/compute/skc/tile.h -FILE: ../../../third_party/skia/src/core/Sk4x4f.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 @@ -966,7 +1194,6 @@ 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/SkDeduper.h FILE: ../../../third_party/skia/src/core/SkExchange.h FILE: ../../../third_party/skia/src/core/SkFixed15.h FILE: ../../../third_party/skia/src/core/SkFuzzLogging.h @@ -976,7 +1203,6 @@ 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/SkLights.cpp FILE: ../../../third_party/skia/src/core/SkLiteDL.cpp FILE: ../../../third_party/skia/src/core/SkLiteDL.h FILE: ../../../third_party/skia/src/core/SkLiteRecorder.cpp @@ -992,8 +1218,6 @@ FILE: ../../../third_party/skia/src/core/SkNormalMapSource.h FILE: ../../../third_party/skia/src/core/SkNormalSource.cpp FILE: ../../../third_party/skia/src/core/SkNormalSource.h FILE: ../../../third_party/skia/src/core/SkOverdrawCanvas.cpp -FILE: ../../../third_party/skia/src/core/SkPM4f.h -FILE: ../../../third_party/skia/src/core/SkPM4fPriv.h FILE: ../../../third_party/skia/src/core/SkPathMeasurePriv.h FILE: ../../../third_party/skia/src/core/SkRasterPipeline.cpp FILE: ../../../third_party/skia/src/core/SkRasterPipeline.h @@ -1014,8 +1238,6 @@ FILE: ../../../third_party/skia/src/gpu/GrAppliedClip.h FILE: ../../../third_party/skia/src/gpu/GrAuditTrail.cpp FILE: ../../../third_party/skia/src/gpu/GrBitmapTextureMaker.cpp FILE: ../../../third_party/skia/src/gpu/GrBitmapTextureMaker.h -FILE: ../../../third_party/skia/src/gpu/GrBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/GrBuffer.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 @@ -1133,6 +1355,7 @@ FILE: ../../../third_party/skia/src/ports/SkImageGeneratorCG.cpp FILE: ../../../third_party/skia/src/ports/SkImageGeneratorWIC.cpp FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.h FILE: ../../../third_party/skia/src/shaders/SkColorShader.cpp +FILE: ../../../third_party/skia/src/shaders/SkLights.cpp FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.cpp FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.h FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientPriv.h @@ -1185,6 +1408,7 @@ FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTIntLiteral.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTInterfaceBlock.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTModifiersDeclaration.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTNode.h +FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTNullLiteral.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTParameter.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTPositionNode.h FILE: ../../../third_party/skia/src/sksl/ast/SkSLASTPrecision.h @@ -1227,6 +1451,7 @@ 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 @@ -1287,7 +1512,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skcms LIBRARY: skia -LIBRARY: vulkan LIBRARY: vulkanmemoryallocator ORIGIN: ../../../third_party/skia/bench/ClearBench.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd @@ -1297,8 +1521,13 @@ FILE: ../../../third_party/skia/bench/CubicMapBench.cpp FILE: ../../../third_party/skia/bench/GrCCFillGeometryBench.cpp FILE: ../../../third_party/skia/bench/ImageCycleBench.cpp FILE: ../../../third_party/skia/bench/JSONBench.cpp +FILE: ../../../third_party/skia/bench/PathOpsBench.cpp FILE: ../../../third_party/skia/bench/PolyUtilsBench.cpp FILE: ../../../third_party/skia/bench/ShaderMaskFilterBench.cpp +FILE: ../../../third_party/skia/bench/TypefaceBench.cpp +FILE: ../../../third_party/skia/experimental/nima/NimaActor.cpp +FILE: ../../../third_party/skia/experimental/nima/NimaActor.h +FILE: ../../../third_party/skia/experimental/pvg/draw_msg.proto FILE: ../../../third_party/skia/fuzz/FuzzCommon.cpp FILE: ../../../third_party/skia/fuzz/FuzzPathMeasure.cpp FILE: ../../../third_party/skia/fuzz/FuzzRegionOp.cpp @@ -1309,11 +1538,20 @@ FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzRegionSetPath.cpp FILE: ../../../third_party/skia/gm/3dgm.cpp FILE: ../../../third_party/skia/gm/analytic_gradients.cpp FILE: ../../../third_party/skia/gm/androidblendmodes.cpp +FILE: ../../../third_party/skia/gm/b_119394958.cpp FILE: ../../../third_party/skia/gm/clockwise.cpp FILE: ../../../third_party/skia/gm/crbug_847759.cpp FILE: ../../../third_party/skia/gm/crbug_884166.cpp FILE: ../../../third_party/skia/gm/crbug_887103.cpp +FILE: ../../../third_party/skia/gm/crbug_892988.cpp +FILE: ../../../third_party/skia/gm/crbug_899512.cpp +FILE: ../../../third_party/skia/gm/crbug_905548.cpp +FILE: ../../../third_party/skia/gm/daa.cpp FILE: ../../../third_party/skia/gm/drawimageset.cpp +FILE: ../../../third_party/skia/gm/drawquadset.cpp +FILE: ../../../third_party/skia/gm/fontregen.cpp +FILE: ../../../third_party/skia/gm/fwidth_squircle.cpp +FILE: ../../../third_party/skia/gm/gradients_degenerate.cpp FILE: ../../../third_party/skia/gm/hugepath.cpp FILE: ../../../third_party/skia/gm/localmatrixshader.cpp FILE: ../../../third_party/skia/gm/makeRasterImage.cpp @@ -1329,19 +1567,27 @@ FILE: ../../../third_party/skia/gm/shadermaskfilter.cpp FILE: ../../../third_party/skia/gm/sharedcorners.cpp FILE: ../../../third_party/skia/gm/skinning.cpp FILE: ../../../third_party/skia/gm/trickycubicstrokes.cpp +FILE: ../../../third_party/skia/gm/unpremul.cpp FILE: ../../../third_party/skia/gm/wacky_yuv_formats.cpp FILE: ../../../third_party/skia/include/android/SkAnimatedImage.h FILE: ../../../third_party/skia/include/c/sk_colorspace.h FILE: ../../../third_party/skia/include/c/sk_imageinfo.h FILE: ../../../third_party/skia/include/core/SkCanvasVirtualEnforcer.h +FILE: ../../../third_party/skia/include/core/SkContourMeasure.h FILE: ../../../third_party/skia/include/core/SkCoverageMode.h +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/effects/SkOpPathEffect.h FILE: ../../../third_party/skia/include/effects/SkShaderMaskFilter.h FILE: ../../../third_party/skia/include/effects/SkTrimPathEffect.h +FILE: ../../../third_party/skia/include/gpu/GrBackendDrawableInfo.h FILE: ../../../third_party/skia/include/gpu/GrDriverBugWorkarounds.h FILE: ../../../third_party/skia/include/gpu/vk/GrVkMemoryAllocator.h +FILE: ../../../third_party/skia/include/gpu/vk/GrVkVulkan.h +FILE: ../../../third_party/skia/include/ports/SkFontMgr_fuchsia.h FILE: ../../../third_party/skia/include/private/GrCCClipPath.h FILE: ../../../third_party/skia/include/private/GrCCPerOpListPaths.h FILE: ../../../third_party/skia/include/private/GrProxyRef.h @@ -1351,6 +1597,7 @@ FILE: ../../../third_party/skia/include/private/SkSafe32.h FILE: ../../../third_party/skia/include/private/SkTo.h FILE: ../../../third_party/skia/include/utils/Sk3D.h FILE: ../../../third_party/skia/include/utils/SkAnimCodecPlayer.h +FILE: ../../../third_party/skia/include/utils/SkTextUtils.h FILE: ../../../third_party/skia/infra/cts/run_testlab.go FILE: ../../../third_party/skia/modules/skottie/include/SkottieProperty.h FILE: ../../../third_party/skia/modules/skottie/src/SkottieAdapter.cpp @@ -1366,6 +1613,8 @@ FILE: ../../../third_party/skia/modules/skottie/src/SkottieShapeLayer.cpp FILE: ../../../third_party/skia/modules/skottie/src/SkottieTest.cpp FILE: ../../../third_party/skia/modules/skottie/src/SkottieTextLayer.cpp FILE: ../../../third_party/skia/modules/skottie/src/SkottieTool.cpp +FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.cpp +FILE: ../../../third_party/skia/modules/skottie/utils/SkottieUtils.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGClipEffect.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGColorFilter.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGGeometryTransform.h @@ -1388,16 +1637,18 @@ FILE: ../../../third_party/skia/modules/sksg/src/SkSGPlane.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGRoundEffect.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGScene.cpp FILE: ../../../third_party/skia/modules/sksg/src/SkSGText.cpp +FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper.cpp FILE: ../../../third_party/skia/samplecode/SampleAnimatedImage.cpp FILE: ../../../third_party/skia/samplecode/SampleCusp.cpp FILE: ../../../third_party/skia/samplecode/SampleFlutterAnimate.cpp FILE: ../../../third_party/skia/samplecode/SampleGlyphTransform.cpp FILE: ../../../third_party/skia/samplecode/SampleNima.cpp -FILE: ../../../third_party/skia/samplecode/SampleNimaActor.cpp -FILE: ../../../third_party/skia/samplecode/SampleNimaActor.h FILE: ../../../third_party/skia/src/android/SkAnimatedImage.cpp FILE: ../../../third_party/skia/src/c/sk_imageinfo.cpp FILE: ../../../third_party/skia/src/codec/SkEncodedInfo.cpp +FILE: ../../../third_party/skia/src/codec/SkOrientationMarker.cpp +FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.cpp +FILE: ../../../third_party/skia/src/codec/SkWuffsCodec.h FILE: ../../../third_party/skia/src/compute/common/cl/find_cl.c FILE: ../../../third_party/skia/src/compute/common/cl/find_cl.h FILE: ../../../third_party/skia/src/compute/common/vk/cache_vk.c @@ -1433,16 +1684,17 @@ 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 +FILE: ../../../third_party/skia/src/core/SkContourMeasure.cpp FILE: ../../../third_party/skia/src/core/SkCoverageModePriv.h FILE: ../../../third_party/skia/src/core/SkCubicMap.cpp -FILE: ../../../third_party/skia/src/core/SkCubicMap.h FILE: ../../../third_party/skia/src/core/SkDeferredDisplayList.cpp FILE: ../../../third_party/skia/src/core/SkDeferredDisplayListPriv.h FILE: ../../../third_party/skia/src/core/SkDraw_text.cpp +FILE: ../../../third_party/skia/src/core/SkFontPriv.cpp +FILE: ../../../third_party/skia/src/core/SkFontPriv.h FILE: ../../../third_party/skia/src/core/SkGlyph.cpp FILE: ../../../third_party/skia/src/core/SkIPoint16.h FILE: ../../../third_party/skia/src/core/SkMaskFilterBase.h -FILE: ../../../third_party/skia/src/core/SkPaint_text.cpp FILE: ../../../third_party/skia/src/core/SkPath_serial.cpp FILE: ../../../third_party/skia/src/core/SkPicturePriv.h FILE: ../../../third_party/skia/src/core/SkRRectPriv.h @@ -1457,6 +1709,7 @@ 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 @@ -1464,16 +1717,16 @@ FILE: ../../../third_party/skia/src/effects/SkTrimPE.h FILE: ../../../third_party/skia/src/effects/SkTrimPathEffect.cpp FILE: ../../../third_party/skia/src/gpu/GrContextThreadSafeProxyPriv.h FILE: ../../../third_party/skia/src/gpu/GrDDLContext.cpp -FILE: ../../../third_party/skia/src/gpu/GrDirectContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrDeinstantiateProxyTracker.cpp +FILE: ../../../third_party/skia/src/gpu/GrDeinstantiateProxyTracker.h FILE: ../../../third_party/skia/src/gpu/GrDriverBugWorkarounds.cpp FILE: ../../../third_party/skia/src/gpu/GrFPArgs.h +FILE: ../../../third_party/skia/src/gpu/GrLegacyDirectContext.cpp FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrProxyProvider.h FILE: ../../../third_party/skia/src/gpu/GrQuad.cpp FILE: ../../../third_party/skia/src/gpu/GrRenderTargetProxyPriv.h FILE: ../../../third_party/skia/src/gpu/GrResourceProviderPriv.h -FILE: ../../../third_party/skia/src/gpu/GrUninstantiateProxyTracker.cpp -FILE: ../../../third_party/skia/src/gpu/GrUninstantiateProxyTracker.h FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCClipPath.cpp FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.cpp FILE: ../../../third_party/skia/src/gpu/ccpr/GrCCConicShader.h @@ -1562,6 +1815,7 @@ FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCopyManager.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCopyManager.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCopyPipelineState.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCopyPipelineState.mm +FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlCppUtil.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlGpuCommandBuffer.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlGpuCommandBuffer.mm FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlPipelineState.h @@ -1584,21 +1838,36 @@ FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.h FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlVaryingHandler.mm FILE: ../../../third_party/skia/src/gpu/ops/GrClearStencilClipOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrDebugMarkerOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrDrawableOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRRectOp.h +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrFillRectOp.h +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/text/GrAtlasManager.cpp FILE: ../../../third_party/skia/src/gpu/text/GrAtlasManager.h FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.cpp FILE: ../../../third_party/skia/src/gpu/text/GrSDFMaskFilter.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkAMDMemoryAllocator.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkCommandPool.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkImageLayout.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineLayout.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkPipelineLayout.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSamplerYcbcrConversion.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkTypesPriv.cpp FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.cpp FILE: ../../../third_party/skia/src/image/SkImage_GpuBase.h FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.cpp FILE: ../../../third_party/skia/src/image/SkImage_GpuYUVA.h FILE: ../../../third_party/skia/src/image/SkImage_Lazy.h +FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts.h FILE: ../../../third_party/skia/src/opts/SkOpts_hsw.cpp FILE: ../../../third_party/skia/src/opts/SkRasterPipeline_opts.h FILE: ../../../third_party/skia/src/pathops/SkPathOpsAsWinding.cpp @@ -1607,6 +1876,7 @@ FILE: ../../../third_party/skia/src/pdf/SkClusterator.cpp 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/SkSLCPPUniformCTypes.cpp FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.h FILE: ../../../third_party/skia/src/sksl/SkSLInterpreter.cpp @@ -1621,300 +1891,78 @@ FILE: ../../../third_party/skia/src/utils/SkAnimCodecPlayer.cpp FILE: ../../../third_party/skia/src/utils/SkCallableTraits.h FILE: ../../../third_party/skia/src/utils/SkJSON.cpp FILE: ../../../third_party/skia/src/utils/SkJSON.h +FILE: ../../../third_party/skia/src/utils/SkTextUtils.cpp FILE: ../../../third_party/skia/src/utils/mac/SkUniqueCFRef.h FILE: ../../../third_party/skia/src/utils/win/SkDWriteNTDDI_VERSION.h FILE: ../../../third_party/skia/third_party/skcms/skcms.cc FILE: ../../../third_party/skia/third_party/skcms/skcms.h -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/vulkan/SkiaVulkan.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/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 -LIBRARY: vulkan -ORIGIN: ../../../flutter/third_party/txt/LICENSE -TYPE: LicenseType.apache -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 -FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/package.json -FILE: ../../../third_party/skia/src/images/SkWebpEncoder.cpp -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vk_platform.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_android.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_core.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_ios.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_macos.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_win32.h -FILE: ../../../third_party/skia/third_party/vulkan/vulkan/vulkan_xcb.h ----------------------------------------------------------------------------------------------------- -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. +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. -END OF TERMS AND CONDITIONS +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -APPENDIX: How to apply the Apache License to your work. + * 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. - 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. +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. +==================================================================================================== -Copyright [yyyy] [name of copyright owner] +==================================================================================================== +LIBRARY: skcms +ORIGIN: ../../../third_party/skia/include/third_party/vulkan/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/third_party/skcms/version.sha1 +---------------------------------------------------------------------------------------------------- +Copyright (c) 2018 Google Inc. All rights reserved. -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 +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - http://www.apache.org/licenses/LICENSE-2.0 + * 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. -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. +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. ==================================================================================================== ==================================================================================================== @@ -1956,9 +2004,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LIBRARY: skia ORIGIN: ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/.clang-tidy FILE: ../../../third_party/skia/CQ_COMMITTERS FILE: ../../../third_party/skia/DEPS -FILE: ../../../third_party/skia/Doxyfile FILE: ../../../third_party/skia/animations/checkbox.xml FILE: ../../../third_party/skia/animations/chest#1.jpg FILE: ../../../third_party/skia/animations/fire#1.jpg @@ -1971,52 +2019,12 @@ FILE: ../../../third_party/skia/animations/redcross#1.jpg FILE: ../../../third_party/skia/animations/text#1.xml FILE: ../../../third_party/skia/bench/microbench.json FILE: ../../../third_party/skia/bench/skpbench.json +FILE: ../../../third_party/skia/docker/binary-size/Dockerfile +FILE: ../../../third_party/skia/docker/cmake-release/Dockerfile FILE: ../../../third_party/skia/docker/skia-release/Dockerfile +FILE: ../../../third_party/skia/docker/skia-wasm-release/Dockerfile FILE: ../../../third_party/skia/docker/skia-with-swift-shader-base/Dockerfile FILE: ../../../third_party/skia/docker/skia-with-swift-shader-base/build-with-swift-shader-and-run -FILE: ../../../third_party/skia/docs/SkAutoCanvasRestore_Reference.bmh -FILE: ../../../third_party/skia/docs/SkBitmap_Reference.bmh -FILE: ../../../third_party/skia/docs/SkBlendMode_Overview.bmh -FILE: ../../../third_party/skia/docs/SkBlendMode_Reference.bmh -FILE: ../../../third_party/skia/docs/SkCanvas_Reference.bmh -FILE: ../../../third_party/skia/docs/SkColor4f_Reference.bmh -FILE: ../../../third_party/skia/docs/SkColor_Reference.bmh -FILE: ../../../third_party/skia/docs/SkDynamicMemoryWStream_Reference.bmh -FILE: ../../../third_party/skia/docs/SkFILEStream_Reference.bmh -FILE: ../../../third_party/skia/docs/SkFILEWStream_Reference.bmh -FILE: ../../../third_party/skia/docs/SkIPoint_Reference.bmh -FILE: ../../../third_party/skia/docs/SkIRect_Reference.bmh -FILE: ../../../third_party/skia/docs/SkImageInfo_Reference.bmh -FILE: ../../../third_party/skia/docs/SkImage_Reference.bmh -FILE: ../../../third_party/skia/docs/SkMatrix_Reference.bmh -FILE: ../../../third_party/skia/docs/SkMemoryStream_Reference.bmh -FILE: ../../../third_party/skia/docs/SkPaint_Reference.bmh -FILE: ../../../third_party/skia/docs/SkPath_Overview.bmh -FILE: ../../../third_party/skia/docs/SkPath_Reference.bmh -FILE: ../../../third_party/skia/docs/SkPicture_Reference.bmh -FILE: ../../../third_party/skia/docs/SkPixmap_Reference.bmh -FILE: ../../../third_party/skia/docs/SkPoint_Reference.bmh -FILE: ../../../third_party/skia/docs/SkRRect_Reference.bmh -FILE: ../../../third_party/skia/docs/SkRect_Reference.bmh -FILE: ../../../third_party/skia/docs/SkRegion_Reference.bmh -FILE: ../../../third_party/skia/docs/SkStream_Reference.bmh -FILE: ../../../third_party/skia/docs/SkSurface_Reference.bmh -FILE: ../../../third_party/skia/docs/SkTextBlobBuilder_Reference.bmh -FILE: ../../../third_party/skia/docs/SkTextBlob_Reference.bmh -FILE: ../../../third_party/skia/docs/SkWStream_Reference.bmh -FILE: ../../../third_party/skia/docs/illustrations.bmh -FILE: ../../../third_party/skia/docs/markup.bmh -FILE: ../../../third_party/skia/docs/overview.bmh -FILE: ../../../third_party/skia/docs/status.json -FILE: ../../../third_party/skia/docs/undocumented.bmh -FILE: ../../../third_party/skia/docs/usingBookmaker.bmh -FILE: ../../../third_party/skia/experimental/canvaskit/canvas-kit/cpu_example.html -FILE: ../../../third_party/skia/experimental/canvaskit/canvas-kit/example.html -FILE: ../../../third_party/skia/experimental/canvaskit/canvas-kit/package.json -FILE: ../../../third_party/skia/experimental/canvaskit/externs.js -FILE: ../../../third_party/skia/experimental/canvaskit/helper.js -FILE: ../../../third_party/skia/experimental/canvaskit/interface.js -FILE: ../../../third_party/skia/experimental/canvaskit/package.json FILE: ../../../third_party/skia/experimental/docs/animationCommon.js FILE: ../../../third_party/skia/experimental/docs/backend.js FILE: ../../../third_party/skia/experimental/docs/canvasBackend.js @@ -2026,7 +2034,15 @@ FILE: ../../../third_party/skia/experimental/docs/jsonbaseddoc.htm FILE: ../../../third_party/skia/experimental/docs/svgBackend.js FILE: ../../../third_party/skia/experimental/docs/svgbaseddoc.htm FILE: ../../../third_party/skia/experimental/docs/utilities.js -FILE: ../../../third_party/skia/infra/bots/android_bin.isolate +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/cpu.js +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/index.html +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger/sample.skp +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/externs.js +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/fonts/NotoMono-Regular.ttf +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/gpu.js +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/experimental/wasm-skp-debugger/ready.js 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 @@ -2040,6 +2056,7 @@ FILE: ../../../third_party/skia/infra/bots/assets/chromebook_x86_64_gles/VERSION FILE: ../../../third_party/skia/infra/bots/assets/clang_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/clang_win/VERSION FILE: ../../../third_party/skia/infra/bots/assets/cmake_linux/VERSION +FILE: ../../../third_party/skia/infra/bots/assets/cmake_mac/VERSION FILE: ../../../third_party/skia/infra/bots/assets/gcloud_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/go/VERSION FILE: ../../../third_party/skia/infra/bots/assets/go_deps/VERSION @@ -2062,7 +2079,6 @@ 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/win_toolchain_2015/VERSION -FILE: ../../../third_party/skia/infra/bots/assets/win_vulkan_sdk/VERSION FILE: ../../../third_party/skia/infra/bots/calmbench.isolate FILE: ../../../third_party/skia/infra/bots/cfg.json FILE: ../../../third_party/skia/infra/bots/empty.isolate @@ -2084,9 +2100,11 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-CMake.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Mini.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json @@ -2095,7 +2113,7 @@ 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-EMCC-asmjs-Release-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Debug-CanvasKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Debug-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-CanvasKit_CPU.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json @@ -2112,6 +2130,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.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-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-MSVC-x86_64-Debug-MSRTC.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json @@ -2126,6 +2145,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/doxygen/examples/full.expected/doxygen.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/env/examples/full.expected/test.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json 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-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json @@ -2143,7 +2163,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.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-Ubuntu17-GCC-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/cpu_scale_failed.json @@ -2166,15 +2186,10 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.exp 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/win_test.json FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_nontrybot.json +FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_sdk_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/nightly_bookmaker.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/nightly_failed_extract_fiddles.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/nightly_failed_fiddlecli.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/nightly_failed_fiddles.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/nightly_failed_upload.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/percommit_bookmaker.json -FILE: ../../../third_party/skia/infra/bots/recipes/bookmaker.expected/percommit_failed_validation.json +FILE: ../../../third_party/skia/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json FILE: ../../../third_party/skia/infra/bots/recipes/calmbench.expected/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/calmbench.expected/Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json @@ -2200,17 +2215,24 @@ FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Cl FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-BonusConfigs.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-MoltenVK_Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-MoltenVK_Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-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.expected/Perf-Win10-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Release-All-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Release-All-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/trybot.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.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 @@ -2238,6 +2260,7 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Cl FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Wuffs.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_10-Coverage.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER.json @@ -2245,12 +2268,12 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Cl FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-MoltenVK_Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-MoltenVK_Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json @@ -2261,21 +2284,23 @@ FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clan FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts_GDI.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC6i5SYK-GPU-IntelIris540-x86_64-Debug-All-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC8i5BEK-GPU-IntelIris655-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FAAA.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FDAA.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FSAA.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win2016-MSVC-GCE-CPU-AVX2-x86_64-Debug-All-MSRTC.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_dm.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_get_hashes.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_pull.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/failed_push.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/internal_bot_2.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/internal_bot_5.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/trybot.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json FILE: ../../../third_party/skia/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json @@ -2305,8 +2330,9 @@ 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/branch-config/cq.cfg +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 FILE: ../../../third_party/skia/infra/cts/whitelist_devices.json FILE: ../../../third_party/skia/infra/lottiecap/docker/gold-lottie-web-puppeteer/Dockerfile FILE: ../../../third_party/skia/infra/lottiecap/docker/lottie-web-puppeteer/Dockerfile @@ -2318,16 +2344,62 @@ 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/modules/canvaskit/canvaskit/NotoSerif-Regular.ttf +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.ttf +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/Roboto-Regular.woff +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/test.png +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/fonts/NotoMono-Regular.ttf +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 +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/font.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/htmlcanvas.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/imagedata.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/lineargradient.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/path2d.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/pattern.js +FILE: ../../../third_party/skia/modules/canvaskit/htmlcanvas/postamble.js +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/package.json +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/postamble.js +FILE: ../../../third_party/skia/modules/canvaskit/preamble.js +FILE: ../../../third_party/skia/modules/canvaskit/ready.js +FILE: ../../../third_party/skia/modules/canvaskit/release.js +FILE: ../../../third_party/skia/modules/canvaskit/skottie.js 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/npm-asmjs/example.html +FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/package.json +FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/example.html +FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/package.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 FILE: ../../../third_party/skia/modules/pathkit/perf/pathops.bench.js FILE: ../../../third_party/skia/modules/pathkit/perf/perfReporter.js +FILE: ../../../third_party/skia/modules/pathkit/ready.js FILE: ../../../third_party/skia/public.bzl FILE: ../../../third_party/skia/site/METADATA FILE: ../../../third_party/skia/site/dev/METADATA @@ -2385,14 +2457,6 @@ 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/user/sample/architecture.png -FILE: ../../../third_party/skia/site/user/sample/detail_correct.png -FILE: ../../../third_party/skia/site/user/sample/detail_wrong.png -FILE: ../../../third_party/skia/site/user/sample/gamut_correct.png -FILE: ../../../third_party/skia/site/user/sample/gamut_wrong.png -FILE: ../../../third_party/skia/site/user/sample/gradient_correct.png -FILE: ../../../third_party/skia/site/user/sample/gradient_wrong.png -FILE: ../../../third_party/skia/site/user/sample/transfer_fn.png FILE: ../../../third_party/skia/src/compute/hs/cl/intel/gen8/u32/hs_kernels.bin FILE: ../../../third_party/skia/src/compute/hs/cl/intel/gen8/u32/hs_kernels.bin.len.xxd FILE: ../../../third_party/skia/src/compute/hs/cl/intel/gen8/u32/hs_kernels.bin.xxd @@ -2663,7 +2727,6 @@ 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 -FILE: ../../../third_party/skia/bench/FontScalerBench.cpp FILE: ../../../third_party/skia/bench/GradientBench.cpp FILE: ../../../third_party/skia/bench/MatrixBench.cpp FILE: ../../../third_party/skia/bench/MutexBench.cpp @@ -2674,8 +2737,6 @@ FILE: ../../../third_party/skia/bench/RectBench.cpp FILE: ../../../third_party/skia/bench/RegionBench.cpp FILE: ../../../third_party/skia/bench/RepeatTileBench.cpp FILE: ../../../third_party/skia/bench/ScalarBench.cpp -FILE: ../../../third_party/skia/bench/ShaderMaskBench.cpp -FILE: ../../../third_party/skia/bench/TextBench.cpp FILE: ../../../third_party/skia/bench/VertBench.cpp FILE: ../../../third_party/skia/experimental/Networking/SkSockets.cpp FILE: ../../../third_party/skia/experimental/Networking/SkSockets.h @@ -2736,8 +2797,6 @@ FILE: ../../../third_party/skia/gm/texteffects.cpp FILE: ../../../third_party/skia/gm/tilemodes.cpp FILE: ../../../third_party/skia/gm/tilemodes_scaled.cpp FILE: ../../../third_party/skia/gm/tinybitmap.cpp -FILE: ../../../third_party/skia/gm/verttext.cpp -FILE: ../../../third_party/skia/gm/verttext2.cpp FILE: ../../../third_party/skia/gm/xfermodes.cpp FILE: ../../../third_party/skia/include/core/SkData.h FILE: ../../../third_party/skia/include/core/SkImageEncoder.h @@ -2761,12 +2820,10 @@ 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/SampleAll.cpp FILE: ../../../third_party/skia/samplecode/SampleArc.cpp FILE: ../../../third_party/skia/samplecode/SampleBigBlur.cpp FILE: ../../../third_party/skia/samplecode/SampleBigGradient.cpp FILE: ../../../third_party/skia/samplecode/SampleBitmapRect.cpp -FILE: ../../../third_party/skia/samplecode/SampleBlur.cpp FILE: ../../../third_party/skia/samplecode/SampleCamera.cpp FILE: ../../../third_party/skia/samplecode/SampleCircle.cpp FILE: ../../../third_party/skia/samplecode/SampleClamp.cpp @@ -2774,15 +2831,12 @@ 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/SampleConcavePaths.cpp -FILE: ../../../third_party/skia/samplecode/SampleDash.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/SampleFontScalerTest.cpp -FILE: ../../../third_party/skia/samplecode/SampleFuzz.cpp FILE: ../../../third_party/skia/samplecode/SampleGradients.cpp FILE: ../../../third_party/skia/samplecode/SampleHairCurves.cpp FILE: ../../../third_party/skia/samplecode/SampleHairModes.cpp @@ -2791,7 +2845,6 @@ 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/SampleLines.cpp -FILE: ../../../third_party/skia/samplecode/SampleMeasure.cpp FILE: ../../../third_party/skia/samplecode/SamplePatch.cpp FILE: ../../../third_party/skia/samplecode/SamplePath.cpp FILE: ../../../third_party/skia/samplecode/SamplePathClip.cpp @@ -2805,8 +2858,6 @@ 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/SampleText.cpp -FILE: ../../../third_party/skia/samplecode/SampleTextAlpha.cpp FILE: ../../../third_party/skia/samplecode/SampleTextBox.cpp FILE: ../../../third_party/skia/samplecode/SampleTextEffects.cpp FILE: ../../../third_party/skia/samplecode/SampleTextureDomain.cpp @@ -2819,11 +2870,6 @@ FILE: ../../../third_party/skia/src/core/SkAAClip.cpp FILE: ../../../third_party/skia/src/core/SkAAClip.h FILE: ../../../third_party/skia/src/core/SkAdvancedTypefaceMetrics.h FILE: ../../../third_party/skia/src/core/SkBitmapProcState.cpp -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_matrix.h -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_procs.h -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_sample.h -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_shaderproc.h -FILE: ../../../third_party/skia/src/core/SkBlitMask.h FILE: ../../../third_party/skia/src/core/SkBlitRow.h FILE: ../../../third_party/skia/src/core/SkBlitRow_D32.cpp FILE: ../../../third_party/skia/src/core/SkClipStack.cpp @@ -2884,7 +2930,7 @@ FILE: ../../../third_party/skia/src/gpu/gl/GrGLGpu.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLGpu.h FILE: ../../../third_party/skia/src/gpu/gl/GrGLGpuProgramCache.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLIRect.h -FILE: ../../../third_party/skia/src/gpu/gl/GrGLInterface.cpp +FILE: ../../../third_party/skia/src/gpu/gl/GrGLInterfaceAutogen.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLMakeNativeInterface_none.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgram.cpp FILE: ../../../third_party/skia/src/gpu/gl/GrGLProgram.h @@ -2903,8 +2949,6 @@ 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/opts/SkBitmapProcState_opts_none.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_none.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFConvertType1FontStream.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFDevice.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFDevice.h @@ -2919,7 +2963,6 @@ FILE: ../../../third_party/skia/src/pdf/SkPDFTypes.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFUtils.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFUtils.h FILE: ../../../third_party/skia/src/ports/SkGlobalInitialization_default.cpp -FILE: ../../../third_party/skia/src/ports/SkGlobalInitialization_default_imagefilters.cpp FILE: ../../../third_party/skia/src/ports/SkImageEncoder_WIC.cpp FILE: ../../../third_party/skia/src/ports/SkMemory_malloc.cpp FILE: ../../../third_party/skia/src/ports/SkScalerContext_win_dw.cpp @@ -3055,10 +3098,11 @@ FILE: ../../../third_party/skia/include/core/SkPictureRecorder.h FILE: ../../../third_party/skia/include/core/SkSurfaceProps.h FILE: ../../../third_party/skia/include/core/SkTextBlob.h FILE: ../../../third_party/skia/include/gpu/GrGpuResource.h -FILE: ../../../third_party/skia/include/gpu/GrResourceKey.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleInterface.h 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 @@ -3067,7 +3111,6 @@ 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 FILE: ../../../third_party/skia/src/core/SkBitmapCache.h -FILE: ../../../third_party/skia/src/core/SkBlitMask_D32.cpp FILE: ../../../third_party/skia/src/core/SkCachedData.cpp FILE: ../../../third_party/skia/src/core/SkCachedData.h FILE: ../../../third_party/skia/src/core/SkCanvasPriv.h @@ -3078,7 +3121,6 @@ FILE: ../../../third_party/skia/src/core/SkDrawable.cpp FILE: ../../../third_party/skia/src/core/SkFont.cpp FILE: ../../../third_party/skia/src/core/SkForceCPlusPlusLinking.cpp FILE: ../../../third_party/skia/src/core/SkHalf.cpp -FILE: ../../../third_party/skia/src/core/SkHalf.h FILE: ../../../third_party/skia/src/core/SkImageGenerator.cpp FILE: ../../../third_party/skia/src/core/SkMaskCache.cpp FILE: ../../../third_party/skia/src/core/SkMaskCache.h @@ -3163,9 +3205,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrDashOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrSmallPathRenderer.h FILE: ../../../third_party/skia/src/image/SkReadPixelsRec.h FILE: ../../../third_party/skia/src/image/SkSurface_Gpu.h -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_matrix_neon.h -FILE: ../../../third_party/skia/src/opts/SkBlitMask_opts_arm.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitMask_opts_none.cpp FILE: ../../../third_party/skia/src/pathops/SkOpBuilder.cpp FILE: ../../../third_party/skia/src/pathops/SkOpSpan.cpp FILE: ../../../third_party/skia/src/pathops/SkPathOpsTSect.cpp @@ -3341,8 +3380,6 @@ FILE: ../../../third_party/skia/src/image/SkSurface.cpp FILE: ../../../third_party/skia/src/image/SkSurface_Base.h FILE: ../../../third_party/skia/src/image/SkSurface_Gpu.cpp FILE: ../../../third_party/skia/src/image/SkSurface_Raster.cpp -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_arm_neon.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_arm_neon.h FILE: ../../../third_party/skia/src/pathops/SkAddIntersections.cpp FILE: ../../../third_party/skia/src/pathops/SkAddIntersections.h FILE: ../../../third_party/skia/src/pathops/SkDCubicLineIntersection.cpp @@ -3369,7 +3406,6 @@ FILE: ../../../third_party/skia/src/pathops/SkPathOpsCurve.h FILE: ../../../third_party/skia/src/pathops/SkPathOpsLine.cpp FILE: ../../../third_party/skia/src/pathops/SkPathOpsLine.h FILE: ../../../third_party/skia/src/pathops/SkPathOpsOp.cpp -FILE: ../../../third_party/skia/src/pathops/SkPathOpsPoint.cpp FILE: ../../../third_party/skia/src/pathops/SkPathOpsPoint.h FILE: ../../../third_party/skia/src/pathops/SkPathOpsQuad.cpp FILE: ../../../third_party/skia/src/pathops/SkPathOpsQuad.h @@ -3546,7 +3582,6 @@ 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/SkLights.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 @@ -3562,8 +3597,10 @@ FILE: ../../../third_party/skia/include/ports/SkFontMgr_android.h FILE: ../../../third_party/skia/include/ports/SkFontMgr_directory.h FILE: ../../../third_party/skia/include/ports/SkFontMgr_empty.h FILE: ../../../third_party/skia/include/ports/SkFontMgr_fontconfig.h -FILE: ../../../third_party/skia/include/private/SkAtomics.h FILE: ../../../third_party/skia/include/private/SkMutex.h +FILE: ../../../third_party/skia/include/private/SkNx.h +FILE: ../../../third_party/skia/include/private/SkNx_neon.h +FILE: ../../../third_party/skia/include/private/SkNx_sse.h FILE: ../../../third_party/skia/include/private/SkSemaphore.h FILE: ../../../third_party/skia/include/private/SkSpinlock.h FILE: ../../../third_party/skia/include/private/SkTHash.h @@ -3577,7 +3614,6 @@ FILE: ../../../third_party/skia/samplecode/SampleAtlas.cpp FILE: ../../../third_party/skia/samplecode/SampleClipDrawMatch.cpp FILE: ../../../third_party/skia/samplecode/SampleFilterQuality.cpp FILE: ../../../third_party/skia/samplecode/SampleLighting.cpp -FILE: ../../../third_party/skia/samplecode/SamplePathFuzz.cpp FILE: ../../../third_party/skia/samplecode/SampleShip.cpp FILE: ../../../third_party/skia/samplecode/SampleXfer.cpp FILE: ../../../third_party/skia/src/android/SkBitmapRegionCodec.cpp @@ -3588,6 +3624,8 @@ FILE: ../../../third_party/skia/src/c/sk_c_from_to.h FILE: ../../../third_party/skia/src/c/sk_paint.cpp FILE: ../../../third_party/skia/src/c/sk_types_priv.h FILE: ../../../third_party/skia/src/codec/SkAndroidCodec.cpp +FILE: ../../../third_party/skia/src/codec/SkAndroidCodecAdapter.cpp +FILE: ../../../third_party/skia/src/codec/SkAndroidCodecAdapter.h FILE: ../../../third_party/skia/src/codec/SkBmpCodec.cpp FILE: ../../../third_party/skia/src/codec/SkBmpCodec.h FILE: ../../../third_party/skia/src/codec/SkBmpMaskCodec.cpp @@ -3623,8 +3661,6 @@ FILE: ../../../third_party/skia/src/codec/SkSwizzler.cpp FILE: ../../../third_party/skia/src/codec/SkSwizzler.h FILE: ../../../third_party/skia/src/codec/SkWbmpCodec.cpp FILE: ../../../third_party/skia/src/codec/SkWbmpCodec.h -FILE: ../../../third_party/skia/src/codec/SkWebpAdapterCodec.cpp -FILE: ../../../third_party/skia/src/codec/SkWebpAdapterCodec.h FILE: ../../../third_party/skia/src/codec/SkWebpCodec.cpp FILE: ../../../third_party/skia/src/codec/SkWebpCodec.h FILE: ../../../third_party/skia/src/core/Sk4px.h @@ -3642,13 +3678,11 @@ 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/SkNextID.h -FILE: ../../../third_party/skia/src/core/SkNx.h FILE: ../../../third_party/skia/src/core/SkOpts.cpp FILE: ../../../third_party/skia/src/core/SkOpts.h FILE: ../../../third_party/skia/src/core/SkPathPriv.h FILE: ../../../third_party/skia/src/core/SkPictureCommon.h FILE: ../../../third_party/skia/src/core/SkPictureImageGenerator.cpp -FILE: ../../../third_party/skia/src/core/SkPictureImageGenerator.h FILE: ../../../third_party/skia/src/core/SkPixmap.cpp FILE: ../../../third_party/skia/src/core/SkPixmapPriv.h FILE: ../../../third_party/skia/src/core/SkPoint3.cpp @@ -3726,10 +3760,8 @@ 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/GrAAFillRectOp.cpp 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/GrAAStrokeRectOp.cpp 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 @@ -3748,7 +3780,6 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrLatticeOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrLatticeOp.h 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/GrNonAAStrokeRectOp.cpp 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 @@ -3756,8 +3787,8 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrTessellatingPathRenderer.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrTessellatingPathRenderer.h FILE: ../../../third_party/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp FILE: ../../../third_party/skia/src/gpu/text/GrDistanceFieldAdjustTable.h -FILE: ../../../third_party/skia/src/gpu/text/GrGlyphCache.cpp -FILE: ../../../third_party/skia/src/gpu/text/GrGlyphCache.h +FILE: ../../../third_party/skia/src/gpu/text/GrStrikeCache.cpp +FILE: ../../../third_party/skia/src/gpu/text/GrStrikeCache.h 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 @@ -3806,13 +3837,7 @@ FILE: ../../../third_party/skia/src/opts/Sk4px_NEON.h FILE: ../../../third_party/skia/src/opts/Sk4px_SSE2.h FILE: ../../../third_party/skia/src/opts/Sk4px_none.h FILE: ../../../third_party/skia/src/opts/SkBlitMask_opts.h -FILE: ../../../third_party/skia/src/opts/SkBlitMask_opts_arm_neon.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitMask_opts_arm_neon.h FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts.h -FILE: ../../../third_party/skia/src/opts/SkColor_opts_neon.h -FILE: ../../../third_party/skia/src/opts/SkMorphologyImageFilter_opts.h -FILE: ../../../third_party/skia/src/opts/SkNx_neon.h -FILE: ../../../third_party/skia/src/opts/SkNx_sse.h FILE: ../../../third_party/skia/src/opts/SkOpts_sse41.cpp FILE: ../../../third_party/skia/src/opts/SkOpts_ssse3.cpp FILE: ../../../third_party/skia/src/opts/SkXfermode_opts.h @@ -3828,8 +3853,6 @@ FILE: ../../../third_party/skia/src/pdf/SkJpegInfo.cpp FILE: ../../../third_party/skia/src/pdf/SkJpegInfo.h FILE: ../../../third_party/skia/src/pdf/SkPDFBitmap.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFBitmap.h -FILE: ../../../third_party/skia/src/pdf/SkPDFCanon.cpp -FILE: ../../../third_party/skia/src/pdf/SkPDFCanon.h FILE: ../../../third_party/skia/src/pdf/SkPDFMetadata.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFMetadata.h FILE: ../../../third_party/skia/src/ports/SkFontMgr_android_factory.cpp @@ -3844,6 +3867,7 @@ FILE: ../../../third_party/skia/src/shaders/SkImageShader.cpp FILE: ../../../third_party/skia/src/shaders/SkImageShader.h FILE: ../../../third_party/skia/src/shaders/SkLightingShader.cpp FILE: ../../../third_party/skia/src/shaders/SkLightingShader.h +FILE: ../../../third_party/skia/src/shaders/SkLights.h FILE: ../../../third_party/skia/src/svg/SkSVGCanvas.cpp FILE: ../../../third_party/skia/src/svg/SkSVGDevice.cpp FILE: ../../../third_party/skia/src/svg/SkSVGDevice.h @@ -3999,7 +4023,6 @@ FILE: ../../../third_party/skia/include/utils/SkLua.h FILE: ../../../third_party/skia/include/utils/SkLuaCanvas.h FILE: ../../../third_party/skia/samplecode/SampleChart.cpp FILE: ../../../third_party/skia/samplecode/SampleClock.cpp -FILE: ../../../third_party/skia/samplecode/SampleFilterFuzz.cpp FILE: ../../../third_party/skia/samplecode/SampleLua.cpp FILE: ../../../third_party/skia/samplecode/SampleManyRects.cpp FILE: ../../../third_party/skia/samplecode/SamplePdfFileViewer.cpp @@ -4007,7 +4030,6 @@ 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/SkBitmapProcState_matrix_template.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 @@ -4125,11 +4147,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/ColorCanvasDrawBitmapBench.cpp -FILE: ../../../third_party/skia/bench/ColorSpaceXformBench.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 @@ -4141,7 +4163,6 @@ 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/experimental/tools/gerrit-change-id-to-number FILE: ../../../third_party/skia/fuzz/FuzzCanvas.cpp FILE: ../../../third_party/skia/gm/alpha_image.cpp FILE: ../../../third_party/skia/gm/atlastext.cpp @@ -4159,6 +4180,7 @@ FILE: ../../../third_party/skia/gm/crosscontextimage.cpp FILE: ../../../third_party/skia/gm/dftext_blob_persp.cpp FILE: ../../../third_party/skia/gm/drrect_small_inner.cpp FILE: ../../../third_party/skia/gm/encode-alpha-jpeg.cpp +FILE: ../../../third_party/skia/gm/etc1.cpp FILE: ../../../third_party/skia/gm/flippity.cpp FILE: ../../../third_party/skia/gm/highcontrastfilter.cpp FILE: ../../../third_party/skia/gm/hsl.cpp @@ -4171,30 +4193,26 @@ FILE: ../../../third_party/skia/gm/pictureshadercache.cpp FILE: ../../../third_party/skia/gm/radial_gradient_precision.cpp FILE: ../../../third_party/skia/gm/savelayer.cpp FILE: ../../../third_party/skia/gm/shadowutils.cpp -FILE: ../../../third_party/skia/gm/shapes_as_paths.cpp FILE: ../../../third_party/skia/gm/simple_magnification.cpp FILE: ../../../third_party/skia/gm/srgb.cpp FILE: ../../../third_party/skia/gm/testgradient.cpp FILE: ../../../third_party/skia/gm/text_scale_skew.cpp FILE: ../../../third_party/skia/gm/thinconcavepaths.cpp -FILE: ../../../third_party/skia/gm/tonalshadows.cpp -FILE: ../../../third_party/skia/gm/tosrgb_colorfilter.cpp FILE: ../../../third_party/skia/include/android/SkAndroidFrameworkUtils.h FILE: ../../../third_party/skia/include/atlastext/SkAtlasTextContext.h FILE: ../../../third_party/skia/include/atlastext/SkAtlasTextFont.h FILE: ../../../third_party/skia/include/atlastext/SkAtlasTextRenderer.h FILE: ../../../third_party/skia/include/atlastext/SkAtlasTextTarget.h FILE: ../../../third_party/skia/include/codec/SkEncodedOrigin.h -FILE: ../../../third_party/skia/include/core/SkColorSpaceXformCanvas.h FILE: ../../../third_party/skia/include/core/SkDeferredDisplayListRecorder.h FILE: ../../../third_party/skia/include/core/SkExecutor.h FILE: ../../../third_party/skia/include/core/SkFontArguments.h +FILE: ../../../third_party/skia/include/core/SkPromiseImageTexture.h FILE: ../../../third_party/skia/include/core/SkSerialProcs.h FILE: ../../../third_party/skia/include/core/SkSurfaceCharacterization.h FILE: ../../../third_party/skia/include/core/SkVertices.h FILE: ../../../third_party/skia/include/docs/SkXPSDocument.h FILE: ../../../third_party/skia/include/effects/SkHighContrastFilter.h -FILE: ../../../third_party/skia/include/effects/SkToSRGBColorFilter.h FILE: ../../../third_party/skia/include/encode/SkEncoder.h FILE: ../../../third_party/skia/include/encode/SkJpegEncoder.h FILE: ../../../third_party/skia/include/encode/SkPngEncoder.h @@ -4246,6 +4264,7 @@ FILE: ../../../third_party/skia/modules/sksg/src/SkSGTrimEffect.cpp FILE: ../../../third_party/skia/samplecode/SampleCCPRGeometry.cpp FILE: ../../../third_party/skia/samplecode/SampleChineseFling.cpp FILE: ../../../third_party/skia/samplecode/SampleCowboy.cpp +FILE: ../../../third_party/skia/samplecode/SampleMixer.cpp FILE: ../../../third_party/skia/samplecode/SamplePathText.cpp FILE: ../../../third_party/skia/samplecode/SampleShadowColor.cpp FILE: ../../../third_party/skia/samplecode/SampleShadowReference.cpp @@ -4336,38 +4355,30 @@ FILE: ../../../third_party/skia/src/compute/skc/weakref.c FILE: ../../../third_party/skia/src/compute/skc/weakref.h FILE: ../../../third_party/skia/src/core/SkArenaAllocList.h FILE: ../../../third_party/skia/src/core/SkAutoBlitterChoose.h -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_utils.h FILE: ../../../third_party/skia/src/core/SkBlendMode.cpp FILE: ../../../third_party/skia/src/core/SkBlitter_RGB565.cpp FILE: ../../../third_party/skia/src/core/SkClipStackDevice.cpp FILE: ../../../third_party/skia/src/core/SkClipStackDevice.h -FILE: ../../../third_party/skia/src/core/SkColorSpaceXformCanvas.cpp -FILE: ../../../third_party/skia/src/core/SkColorSpaceXformer.cpp -FILE: ../../../third_party/skia/src/core/SkColorSpaceXformer.h -FILE: ../../../third_party/skia/src/core/SkCoverageDelta.cpp -FILE: ../../../third_party/skia/src/core/SkCoverageDelta.h FILE: ../../../third_party/skia/src/core/SkDeferredDisplayListRecorder.cpp FILE: ../../../third_party/skia/src/core/SkDrawShadowInfo.cpp FILE: ../../../third_party/skia/src/core/SkDrawShadowInfo.h FILE: ../../../third_party/skia/src/core/SkDraw_vertices.cpp FILE: ../../../third_party/skia/src/core/SkExecutor.cpp -FILE: ../../../third_party/skia/src/core/SkFDot6Constants.cpp FILE: ../../../third_party/skia/src/core/SkFontMgrPriv.h FILE: ../../../third_party/skia/src/core/SkGaussFilter.cpp FILE: ../../../third_party/skia/src/core/SkGaussFilter.h FILE: ../../../third_party/skia/src/core/SkImageFilterPriv.h FILE: ../../../third_party/skia/src/core/SkMaskBlurFilter.cpp FILE: ../../../third_party/skia/src/core/SkMaskBlurFilter.h +FILE: ../../../third_party/skia/src/core/SkPromiseImageTexture.cpp FILE: ../../../third_party/skia/src/core/SkRasterClipStack.h FILE: ../../../third_party/skia/src/core/SkSafeMath.h FILE: ../../../third_party/skia/src/core/SkSpriteBlitter_RGB565.cpp -FILE: ../../../third_party/skia/src/core/SkUnPreMultiplyPriv.h FILE: ../../../third_party/skia/src/core/SkVertices.cpp FILE: ../../../third_party/skia/src/core/SkVptr.h FILE: ../../../third_party/skia/src/core/SkWritePixelsRec.h FILE: ../../../third_party/skia/src/effects/SkDashImpl.h FILE: ../../../third_party/skia/src/effects/SkHighContrastFilter.cpp -FILE: ../../../third_party/skia/src/effects/SkToSRGBColorFilter.cpp FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferImageGenerator.cpp FILE: ../../../third_party/skia/src/gpu/GrAHardwareBufferImageGenerator.h FILE: ../../../third_party/skia/src/gpu/GrBackendSurface.cpp @@ -4448,8 +4459,6 @@ FILE: ../../../third_party/skia/src/gpu/mtl/GrMtlUtil.h 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/GrDebugMarkerOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrNonAAFillRectOp.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrRectOpFactory.h FILE: ../../../third_party/skia/src/gpu/ops/GrSemaphoreOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSemaphoreOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp @@ -4461,9 +4470,6 @@ FILE: ../../../third_party/skia/src/gpu/vk/GrVkBufferView.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkBufferView.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkSemaphore.h -FILE: ../../../third_party/skia/src/jumper/SkJumper.cpp -FILE: ../../../third_party/skia/src/jumper/SkJumper.h -FILE: ../../../third_party/skia/src/jumper/SkJumper_misc.h FILE: ../../../third_party/skia/src/opts/SkUtils_opts.h FILE: ../../../third_party/skia/src/pdf/SkKeyedImage.cpp FILE: ../../../third_party/skia/src/pdf/SkKeyedImage.h @@ -4472,8 +4478,6 @@ FILE: ../../../third_party/skia/src/pdf/SkPDFGradientShader.h FILE: ../../../third_party/skia/src/ports/SkFontMgr_custom_directory.cpp FILE: ../../../third_party/skia/src/ports/SkFontMgr_custom_embedded.cpp FILE: ../../../third_party/skia/src/ports/SkFontMgr_custom_empty.cpp -FILE: ../../../third_party/skia/src/ports/SkGlobalInitialization_none.cpp -FILE: ../../../third_party/skia/src/ports/SkGlobalInitialization_none_imagefilters.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 @@ -4550,6 +4554,92 @@ 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/MixerBench.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/bench/MixerBench.cpp +FILE: ../../../third_party/skia/gm/backdrop.cpp +FILE: ../../../third_party/skia/gm/compositor_quads.cpp +FILE: ../../../third_party/skia/gm/crbug_938592.cpp +FILE: ../../../third_party/skia/gm/mac_aa_explorer.cpp +FILE: ../../../third_party/skia/gm/mixercolorfilter.cpp +FILE: ../../../third_party/skia/gm/samplelocations.cpp +FILE: ../../../third_party/skia/gm/skbug_8664.cpp +FILE: ../../../third_party/skia/include/core/SkMixer.h +FILE: ../../../third_party/skia/include/gpu/GrContextThreadSafeProxy.h +FILE: ../../../third_party/skia/include/private/GrContext_Base.h +FILE: ../../../third_party/skia/include/private/GrImageContext.h +FILE: ../../../third_party/skia/include/private/GrRecordingContext.h +FILE: ../../../third_party/skia/include/private/SkVx.h +FILE: ../../../third_party/skia/modules/skottie/src/SkottieShaper.cpp +FILE: ../../../third_party/skia/modules/skottie/src/SkottieShaper.h +FILE: ../../../third_party/skia/modules/sksg/include/SkSGRenderEffect.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/core/SkDescriptor.cpp +FILE: ../../../third_party/skia/src/core/SkEffectPriv.h +FILE: ../../../third_party/skia/src/core/SkMixer.cpp +FILE: ../../../third_party/skia/src/core/SkMixerBase.h +FILE: ../../../third_party/skia/src/core/SkStrikeInterface.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/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/GrRecordingContext.cpp +FILE: ../../../third_party/skia/src/gpu/GrRecordingContextPriv.h +FILE: ../../../third_party/skia/src/gpu/GrSamplePatternDictionary.cpp +FILE: ../../../third_party/skia/src/gpu/GrSamplePatternDictionary.h +FILE: ../../../third_party/skia/src/gpu/effects/GrMixerEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/GrMixerEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrMixerEffect.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkSecondaryCBDrawContext.h +FILE: ../../../third_party/skia/src/shaders/SkMixerShader.cpp +FILE: ../../../third_party/skia/src/shaders/SkMixerShader.h +FILE: ../../../third_party/skia/src/sksl/SkSLDefines.h +FILE: ../../../third_party/skia/src/sksl/SkSLOutputStream.cpp +---------------------------------------------------------------------------------------------------- +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 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/ReadPixBench.cpp + ../../../third_party/skia/LICENSE @@ -4565,7 +4655,6 @@ 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/core/SkUtilsArm.h FILE: ../../../third_party/skia/src/effects/imagefilters/SkColorFilterImageFilter.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkLightingImageFilter.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkMagnifierImageFilter.cpp @@ -4574,12 +4663,6 @@ FILE: ../../../third_party/skia/src/effects/imagefilters/SkMergeImageFilter.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkMorphologyImageFilter.cpp FILE: ../../../third_party/skia/src/effects/imagefilters/SkOffsetImageFilter.cpp FILE: ../../../third_party/skia/src/images/SkImageEncoderFns.h -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_filter_neon.h -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts_SSSE3.h -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_SSE2.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_arm.cpp -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_arm_neon.cpp ---------------------------------------------------------------------------------------------------- Copyright 2012 The Android Open Source Project @@ -4612,16 +4695,1153 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/experimental/canvaskit/canvaskit_bindings.cpp + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/docs/examples/Alpha_Constants_a.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/experimental/canvaskit/canvaskit_bindings.cpp -FILE: ../../../third_party/skia/fuzz/FuzzEncoders.cpp -FILE: ../../../third_party/skia/fuzz/FuzzPolyUtils.cpp -FILE: ../../../third_party/skia/include/private/GrSkSLFPFactoryCache.h -FILE: ../../../third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp -FILE: ../../../third_party/skia/src/gpu/ccpr/GrCoverageCountingPathRenderer_none.cpp +FILE: ../../../third_party/skia/docs/examples/Alpha_Constants_a.cpp +FILE: ../../../third_party/skia/docs/examples/Alpha_Constants_b.cpp +FILE: ../../../third_party/skia/docs/examples/Alpha_Type_Opaque.cpp +FILE: ../../../third_party/skia/docs/examples/Alpha_Type_Premul.cpp +FILE: ../../../third_party/skia/docs/examples/Alpha_Type_Unpremul.cpp +FILE: ../../../third_party/skia/docs/examples/Anti_Alias.cpp +FILE: ../../../third_party/skia/docs/examples/Arc.cpp +FILE: ../../../third_party/skia/docs/examples/AutoCanvasRestore_SkCanvas_star.cpp +FILE: ../../../third_party/skia/docs/examples/AutoCanvasRestore_restore.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_012.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_ComputeIsOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_HeapAllocator_allocPixelRef.cpp +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 +FILE: ../../../third_party/skia/docs/examples/Bitmap_colorType.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_computeByteSize.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_copy_const_SkBitmap.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_copy_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_dimensions.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_drawsNothing.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_empty.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_erase.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_eraseARGB.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_eraseColor.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_extractAlpha.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_extractAlpha_2.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_extractAlpha_3.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_extractSubset.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getAddr.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getAddr16.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getAddr32.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getAddr8.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getBounds_2.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getColor.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getGenerationID.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_getSubset.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_height.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_info.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_installPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_installPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_installPixels_3.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_isImmutable.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_isNull.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_isOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_isVolatile.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_move_SkBitmap.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_move_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_notifyPixelsChanged.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_peekPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_pixelRef.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_pixelRefOrigin.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_pixmap.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_readPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_readPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_readPixels_3.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_readyToDraw.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_refColorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_rowBytes.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_rowBytesAsPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setAlphaType.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setImmutable.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setInfo.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setIsVolatile.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setPixelRef.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_setPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_shiftPerPixel.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_swap.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocN32Pixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocPixelsFlags.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Bitmap_tryAllocPixels_3.cpp +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 +FILE: ../../../third_party/skia/docs/examples/Canvas_MakeRasterDirectN32.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_PointMode.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_SaveLayerRec.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_SaveLayerRec_SaveLayerRec.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_SaveLayerRec_const_SkRect_star_const_SkPaint_star.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_SaveLayerRec_const_SkRect_star_const_SkPaint_star_const_SkImageFilter_star.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_SrcRectConstraint.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_accessTopLayerPixels_a.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_accessTopLayerPixels_b.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_accessTopRasterHandle.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clear.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipPath.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipPath_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipPath_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_clipRegion.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_concat.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_const_SkBitmap_const_SkSurfaceProps.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_copy_const_SkBitmap.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_destructor.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawAnnotation_2.cpp +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_drawBitmapLattice.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapNine.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawBitmapRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawCircle.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawCircle_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawColor.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawDRRect_a.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawDRRect_b.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawDrawable.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawDrawable_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawIRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImage.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageNine.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageNine_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect_4.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect_5.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImageRect_6.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawImage_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawLine.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawLine_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawOval.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPaint.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPatch.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPatch_2_a.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPatch_2_b.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPath.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPicture_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPicture_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPicture_4.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPoint.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPoint_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPoints.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPosText.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawPosTextH.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawRRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawRegion.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawRoundRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawString.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawString_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawText.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawTextBlob.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawTextBlob_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawTextRSXform.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawVertices.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_drawVertices_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getBaseLayerSize.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getDeviceClipBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getDeviceClipBounds_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getGrContext.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getLocalClipBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getLocalClipBounds_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getProps.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getSaveCount.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_getTotalMatrix.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_imageInfo.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_int_int_const_SkSurfaceProps_star.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_isClipEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_isClipRect.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_kInitWithPrevious_SaveLayerFlag.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_makeSurface.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_peekPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_quickReject.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_quickReject_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_readPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_readPixels_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_readPixels_a.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_readPixels_b.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_resetMatrix.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_restore.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_restoreToCount.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_rotate.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_rotate_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_save.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayer.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayerAlpha.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayerPreserveLCDTextRequests.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayer_2.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayer_3.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_scale.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_setMatrix.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_skew.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_translate.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_writePixels.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_writePixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Clear.cpp +FILE: ../../../third_party/skia/docs/examples/Clip.cpp +FILE: ../../../third_party/skia/docs/examples/Color.cpp +FILE: ../../../third_party/skia/docs/examples/ColorGetA.cpp +FILE: ../../../third_party/skia/docs/examples/ColorGetB.cpp +FILE: ../../../third_party/skia/docs/examples/ColorGetG.cpp +FILE: ../../../third_party/skia/docs/examples/ColorGetR.cpp +FILE: ../../../third_party/skia/docs/examples/ColorSetA.cpp +FILE: ../../../third_party/skia/docs/examples/ColorSetARGB.cpp +FILE: ../../../third_party/skia/docs/examples/ColorSetRGB.cpp +FILE: ../../../third_party/skia/docs/examples/ColorToHSV.cpp +FILE: ../../../third_party/skia/docs/examples/ColorTypeBytesPerPixel.cpp +FILE: ../../../third_party/skia/docs/examples/ColorTypeIsAlwaysOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/ColorTypeValidateAlphaType.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Burn.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Constants_a.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Constants_b.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Constants_c.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Constants_d.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Dodge.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Filter_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_ARGB_4444.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_Alpha_8.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_BGRA_8888.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_Gray_8.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGBA_1010102.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGBA_8888.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGBA_F16.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGB_101010.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGB_565.cpp +FILE: ../../../third_party/skia/docs/examples/Color_Type_RGB_888.cpp +FILE: ../../../third_party/skia/docs/examples/Conic_Weight_a.cpp +FILE: ../../../third_party/skia/docs/examples/Conic_Weight_b.cpp +FILE: ../../../third_party/skia/docs/examples/Conic_Weight_c.cpp +FILE: ../../../third_party/skia/docs/examples/Cubic.cpp +FILE: ../../../third_party/skia/docs/examples/Darken.cpp +FILE: ../../../third_party/skia/docs/examples/Device_Text.cpp +FILE: ../../../third_party/skia/docs/examples/Difference.cpp +FILE: ../../../third_party/skia/docs/examples/Dither_a.cpp +FILE: ../../../third_party/skia/docs/examples/Dither_b.cpp +FILE: ../../../third_party/skia/docs/examples/Draw_Looper_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Dst.cpp +FILE: ../../../third_party/skia/docs/examples/Dst_Atop.cpp +FILE: ../../../third_party/skia/docs/examples/Dst_In.cpp +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 +FILE: ../../../third_party/skia/docs/examples/Hard_Light.cpp +FILE: ../../../third_party/skia/docs/examples/Hue.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_Make.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_add_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_addto_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_equals.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_isZero.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_minus_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_set.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_subtract_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_subtractfrom_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_x.cpp +FILE: ../../../third_party/skia/docs/examples/IPoint_y.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_EmptyIRect.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_Intersects.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_IntersectsNoEmptyCheck.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_MakeEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_MakeLTRB.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_MakeSize.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_MakeWH.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_MakeXYWH.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_adjust.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_bottom.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_contains.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_containsNoEmptyCheck.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_containsNoEmptyCheck_2.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_contains_2.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_contains_3.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_contains_4.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_height.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_height64.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_inset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_intersect.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_intersectNoEmptyCheck.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_intersect_2.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_intersect_3.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_isEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_isEmpty64.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_join.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_join_2.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_left.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_makeInset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_makeOffset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_makeOutset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_makeSorted.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_offset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_offsetTo.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_offset_2.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_outset.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_right.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_set.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_setEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_setLTRB.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_setXYWH.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_size.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_sort.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_top.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_width.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_width64.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_x.cpp +FILE: ../../../third_party/skia/docs/examples/IRect_y.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_ByteSizeOverflowed.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_Make.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeA8.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeN32.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeN32Premul.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeN32Premul_2.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeS32.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeUnknown.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_MakeUnknown_2.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_alphaType.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_bounds.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_bytesPerPixel.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_colorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_colorType.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_computeByteSize.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_computeMinByteSize.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_computeOffset.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_dimensions.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_equal1_operator.cpp +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 +FILE: ../../../third_party/skia/docs/examples/ImageInfo_notequal1_operator.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_refColorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_reset.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_shiftPerPixel.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_validRowBytes.cpp +FILE: ../../../third_party/skia/docs/examples/ImageInfo_width.cpp +FILE: ../../../third_party/skia/docs/examples/Image_Filter_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeBackendTextureFromSkImage.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeCrossContextFromEncoded.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeCrossContextFromPixmap.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromAdoptedTexture.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromBitmap.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromEncoded.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromGenerator.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromPicture.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromRaster.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromTexture.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeFromTexture_2.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeRasterCopy.cpp +FILE: ../../../third_party/skia/docs/examples/Image_MakeRasterData.cpp +FILE: ../../../third_party/skia/docs/examples/Image_alphaType.cpp +FILE: ../../../third_party/skia/docs/examples/Image_bounds.cpp +FILE: ../../../third_party/skia/docs/examples/Image_colorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Image_colorType.cpp +FILE: ../../../third_party/skia/docs/examples/Image_dimensions.cpp +FILE: ../../../third_party/skia/docs/examples/Image_encodeToData.cpp +FILE: ../../../third_party/skia/docs/examples/Image_encodeToData_2.cpp +FILE: ../../../third_party/skia/docs/examples/Image_getBackendTexture.cpp +FILE: ../../../third_party/skia/docs/examples/Image_height.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isAlphaOnly.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isLazyGenerated_a.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isLazyGenerated_b.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isTextureBacked.cpp +FILE: ../../../third_party/skia/docs/examples/Image_isValid.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeColorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeNonTextureImage.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeRasterImage.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeShader.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeShader_2.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeSubset.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeTextureImage.cpp +FILE: ../../../third_party/skia/docs/examples/Image_makeWithFilter.cpp +FILE: ../../../third_party/skia/docs/examples/Image_peekPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Image_readPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Image_readPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Image_refColorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Image_refEncodedData.cpp +FILE: ../../../third_party/skia/docs/examples/Image_scalePixels.cpp +FILE: ../../../third_party/skia/docs/examples/Image_uniqueID.cpp +FILE: ../../../third_party/skia/docs/examples/Image_width.cpp +FILE: ../../../third_party/skia/docs/examples/Lighten.cpp +FILE: ../../../third_party/skia/docs/examples/Luminosity.cpp +FILE: ../../../third_party/skia/docs/examples/Mask_Filter_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_063.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_Concat.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_I.cpp +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 +FILE: ../../../third_party/skia/docs/examples/Matrix_TypeMask.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_array_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_asAffine.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_cheapEqualTo.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_decomposeScale.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_dirtyMatrixTypeCache.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_dump.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_fixedStepInX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_get.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_get9.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getMaxScale.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getMinMaxScales.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getMinScale.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getPerspX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getPerspY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getScaleX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getScaleY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getSkewX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getSkewY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getTranslateX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getTranslateY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_getType.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_hasPerspective.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_invert.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isFinite.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isFixedStepInX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isIdentity.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isScaleTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isSimilarity.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_isTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapHomogeneousPoints.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapPoints.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapPoints_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRadius.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRect.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRectScaleTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRectToQuad.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapVector.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapVector_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapVectors.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapVectors_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapXY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_mapXY_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postConcat.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postRotate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postRotate_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postScale.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postScale_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postSkew.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postSkew_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_postTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preConcat.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preRotate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preRotate_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preScale.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preScale_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preSkew.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preSkew_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preservesAxisAlignment.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_preservesRightAngles.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_rectStaysRect.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_set.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_set9.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setAffine.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setAll.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setConcat.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setIdentity.cpp +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 +FILE: ../../../third_party/skia/docs/examples/Matrix_setScaleTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setScaleX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setScaleY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setScale_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSinCos.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSinCos_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSkew.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSkewX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSkewY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setSkew_2.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setTranslate.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setTranslateX.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setTranslateY.cpp +FILE: ../../../third_party/skia/docs/examples/Matrix_setTranslate_2.cpp +FILE: ../../../third_party/skia/docs/examples/MemberIndex.cpp +FILE: ../../../third_party/skia/docs/examples/Miter_Limit.cpp +FILE: ../../../third_party/skia/docs/examples/Modulate.cpp +FILE: ../../../third_party/skia/docs/examples/Multiply.cpp +FILE: ../../../third_party/skia/docs/examples/Overlay.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_053.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_057.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_containsText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_copy_const_SkPaint.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_copy_operator.cpp +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_getDrawLooper.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 +FILE: ../../../third_party/skia/docs/examples/Paint_getPathEffect.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getPosTextPath.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getShader.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getStrokeCap.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getStrokeJoin.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getStrokeMiter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getStrokeWidth.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getStyle.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextEncoding.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextPath.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextScaleX.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextSize.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextSkewX.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTextWidths.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_getTypeface.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isAntiAlias.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isAutohinted.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isDither.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isEmbeddedBitmapText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isFakeBoldText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isLCDRenderText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isLinearText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_isSubpixelText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_measureText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_measureText_2.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_move_SkPaint.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_move_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_nothingToDraw.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refColorFilter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refDrawLooper.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refImageFilter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refMaskFilter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refPathEffect.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refShader.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_refTypeface.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setARGB.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setAlpha.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setAntiAlias.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setAutohinted.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setBlendMode.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setColor.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setColor4f.cpp +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_setDrawLooper.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 +FILE: ../../../third_party/skia/docs/examples/Paint_setLCDRenderText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setLinearText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setMaskFilter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setPathEffect.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setShader.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStrokeCap_a.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStrokeCap_b.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStrokeJoin.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStrokeMiter.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStrokeWidth.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setStyle.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setSubpixelText.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setTextEncoding.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setTextScaleX.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setTextSize.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setTextSkewX.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_setTypeface.cpp +FILE: ../../../third_party/skia/docs/examples/Paint_textToGlyphs.cpp +FILE: ../../../third_party/skia/docs/examples/Path_AddPathMode.cpp +FILE: ../../../third_party/skia/docs/examples/Path_ArcSize.cpp +FILE: ../../../third_party/skia/docs/examples/Path_ConvertConicToQuads.cpp +FILE: ../../../third_party/skia/docs/examples/Path_ConvertToNonInverseFillType.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Convexity.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Direction.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Effect_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Path_FillType_a.cpp +FILE: ../../../third_party/skia/docs/examples/Path_FillType_b.cpp +FILE: ../../../third_party/skia/docs/examples/Path_IsCubicDegenerate.cpp +FILE: ../../../third_party/skia/docs/examples/Path_IsInverseFillType.cpp +FILE: ../../../third_party/skia/docs/examples/Path_IsLineDegenerate.cpp +FILE: ../../../third_party/skia/docs/examples/Path_IsQuadDegenerate.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_Iter.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_conicWeight.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_const_SkPath.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_isCloseLine.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_isClosedContour.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_next.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Iter_setPath.cpp +FILE: ../../../third_party/skia/docs/examples/Path_RawIter_conicWeight.cpp +FILE: ../../../third_party/skia/docs/examples/Path_RawIter_next.cpp +FILE: ../../../third_party/skia/docs/examples/Path_RawIter_peek.cpp +FILE: ../../../third_party/skia/docs/examples/Path_SegmentMask.cpp +FILE: ../../../third_party/skia/docs/examples/Path_Verb.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addArc.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addCircle.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addOval.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addOval_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addPath.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addPath_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addPath_3.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addPoly.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addPoly_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRoundRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_addRoundRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo_2_a.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo_2_b.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo_2_c.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo_3.cpp +FILE: ../../../third_party/skia/docs/examples/Path_arcTo_4.cpp +FILE: ../../../third_party/skia/docs/examples/Path_close.cpp +FILE: ../../../third_party/skia/docs/examples/Path_computeTightBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Path_conicTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_conicTo_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_conservativelyContainsRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_contains.cpp +FILE: ../../../third_party/skia/docs/examples/Path_copy_const_SkPath.cpp +FILE: ../../../third_party/skia/docs/examples/Path_copy_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Path_countPoints.cpp +FILE: ../../../third_party/skia/docs/examples/Path_countVerbs.cpp +FILE: ../../../third_party/skia/docs/examples/Path_cubicTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_cubicTo_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_destructor.cpp +FILE: ../../../third_party/skia/docs/examples/Path_dump.cpp +FILE: ../../../third_party/skia/docs/examples/Path_dumpHex.cpp +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_getConvexity.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 +FILE: ../../../third_party/skia/docs/examples/Path_getPoint.cpp +FILE: ../../../third_party/skia/docs/examples/Path_getPoints.cpp +FILE: ../../../third_party/skia/docs/examples/Path_getSegmentMasks.cpp +FILE: ../../../third_party/skia/docs/examples/Path_getVerbs.cpp +FILE: ../../../third_party/skia/docs/examples/Path_incReserve.cpp +FILE: ../../../third_party/skia/docs/examples/Path_interpolate.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isConvex.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isFinite.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isInterpolatable.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isInverseFillType_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isLastContourClosed.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isLine.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isNestedFillRects.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isOval.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isRRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isRect.cpp +FILE: ../../../third_party/skia/docs/examples/Path_isVolatile.cpp +FILE: ../../../third_party/skia/docs/examples/Path_lineTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_lineTo_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_moveTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_moveTo_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Path_offset.cpp +FILE: ../../../third_party/skia/docs/examples/Path_offset_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_quadTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_quadTo_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rArcTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rConicTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rCubicTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rLineTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rMoveTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rQuadTo.cpp +FILE: ../../../third_party/skia/docs/examples/Path_readFromMemory.cpp +FILE: ../../../third_party/skia/docs/examples/Path_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Path_reverseAddPath.cpp +FILE: ../../../third_party/skia/docs/examples/Path_rewind.cpp +FILE: ../../../third_party/skia/docs/examples/Path_serialize.cpp +FILE: ../../../third_party/skia/docs/examples/Path_setConvexity.cpp +FILE: ../../../third_party/skia/docs/examples/Path_setFillType.cpp +FILE: ../../../third_party/skia/docs/examples/Path_setIsVolatile.cpp +FILE: ../../../third_party/skia/docs/examples/Path_setLastPt.cpp +FILE: ../../../third_party/skia/docs/examples/Path_setLastPt_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_swap.cpp +FILE: ../../../third_party/skia/docs/examples/Path_toggleInverseFillType.cpp +FILE: ../../../third_party/skia/docs/examples/Path_transform.cpp +FILE: ../../../third_party/skia/docs/examples/Path_transform_2.cpp +FILE: ../../../third_party/skia/docs/examples/Path_updateBoundsCache.cpp +FILE: ../../../third_party/skia/docs/examples/Path_writeToMemory.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_008.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_AbortCallback_abort.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_MakeFromData.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_MakeFromStream.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_MakePlaceholder.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_approximateBytesUsed.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_approximateOpCount.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_cullRect.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_playback.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_serialize.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_serialize_2.cpp +FILE: ../../../third_party/skia/docs/examples/Picture_uniqueID.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr16.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr16_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr32.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr32_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr64.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr64_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr8.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr8_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addrF16.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addrF16_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_addr_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_alphaType.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_bounds.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_colorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_colorType.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_computeByteSize.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_computeIsOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_const_SkImageInfo_const_star.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_erase.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_erase_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_erase_3.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_extractSubset.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_getColor.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_height.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_info.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_isOpaque.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_readPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_readPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_readPixels_3.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_readPixels_4.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_reset_2.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_rowBytes.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_rowBytesAsPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_scalePixels.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_setColorSpace.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_shiftPerPixel.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_width.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr16.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr32.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr64.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr8.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addrF16.cpp +FILE: ../../../third_party/skia/docs/examples/Pixmap_writable_addr_2.cpp +FILE: ../../../third_party/skia/docs/examples/Plus.cpp +FILE: ../../../third_party/skia/docs/examples/Point_CrossProduct.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Distance.cpp +FILE: ../../../third_party/skia/docs/examples/Point_DotProduct.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Length.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Make.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Normalize.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Offset.cpp +FILE: ../../../third_party/skia/docs/examples/Point_Offset_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_add_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_addto_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_cross.cpp +FILE: ../../../third_party/skia/docs/examples/Point_distanceToOrigin.cpp +FILE: ../../../third_party/skia/docs/examples/Point_dot.cpp +FILE: ../../../third_party/skia/docs/examples/Point_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_equals.cpp +FILE: ../../../third_party/skia/docs/examples/Point_isFinite.cpp +FILE: ../../../third_party/skia/docs/examples/Point_isZero.cpp +FILE: ../../../third_party/skia/docs/examples/Point_iset.cpp +FILE: ../../../third_party/skia/docs/examples/Point_iset_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_length_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_minus_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_multiply_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_multiplyby_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_negate.cpp +FILE: ../../../third_party/skia/docs/examples/Point_normalize_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_offset_3.cpp +FILE: ../../../third_party/skia/docs/examples/Point_scale.cpp +FILE: ../../../third_party/skia/docs/examples/Point_scale_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_set.cpp +FILE: ../../../third_party/skia/docs/examples/Point_setAbs.cpp +FILE: ../../../third_party/skia/docs/examples/Point_setLength.cpp +FILE: ../../../third_party/skia/docs/examples/Point_setLength_2.cpp +FILE: ../../../third_party/skia/docs/examples/Point_setNormalize.cpp +FILE: ../../../third_party/skia/docs/examples/Point_subtract_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_subtractfrom_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Point_x.cpp +FILE: ../../../third_party/skia/docs/examples/Point_y.cpp +FILE: ../../../third_party/skia/docs/examples/PreMultiplyARGB.cpp +FILE: ../../../third_party/skia/docs/examples/PreMultiplyColor.cpp +FILE: ../../../third_party/skia/docs/examples/Quad_a.cpp +FILE: ../../../third_party/skia/docs/examples/Quad_b.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_FromColor.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_equal1_operator.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_notequal1_operator.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_toSkColor.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_vec.cpp +FILE: ../../../third_party/skia/docs/examples/RGBA4f_vec_2.cpp +FILE: ../../../third_party/skia/docs/examples/RGBToHSV.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_Corner.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_MakeEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_MakeOval.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_MakeRect.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_MakeRectXY.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_Type.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_contains.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_copy_const_SkRRect.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_copy_operator.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_dump.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_dumpHex.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_dump_2.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_getBounds.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_getSimpleRadii.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_getType.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_height.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_inset.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_inset_2.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isComplex.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isNinePatch.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isOval.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isRect.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isSimple.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_isValid.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_makeOffset.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_offset.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_outset.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_outset_2.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_radii.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_readFromMemory.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_rect.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setNinePatch.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setOval.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setRect.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setRectRadii.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_setRectXY.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_transform.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_type_2.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_width.cpp +FILE: ../../../third_party/skia/docs/examples/RRect_writeToMemory.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_Intersects.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_Make.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeIWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeLTRB.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeSize.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_MakeXYWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_Make_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_asScalars.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_bottom.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_centerX.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_centerY.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_contains.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_contains_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_contains_3.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_dump.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_dumpHex.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_dump_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_equal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_height.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_inset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_intersect.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_intersect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_intersect_3.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_intersects_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_intersects_3.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_isEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_isFinite.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_isSorted.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_iset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_isetWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_join.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_joinNonEmptyArg.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_joinPossiblyEmptyRect.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_join_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_left.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_makeInset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_makeOffset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_makeOutset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_makeSorted.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_notequal_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_offset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_offsetTo.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_offset_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_outset.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_right.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_round.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_roundIn.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_roundOut.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_roundOut_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_roundOut_3.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_round_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_set.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setBoundsCheck.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setBoundsNoCheck.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setLTRB.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_setXYWH.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_set_2.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_set_3.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_set_4.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_sort.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_toQuad.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_top.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_width.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_x.cpp +FILE: ../../../third_party/skia/docs/examples/Rect_y.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Cliperator_const_SkRegion_const_SkIRect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Cliperator_done.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Cliperator_next.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Cliperator_rect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_Iterator.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_copy_const_SkRegion.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_done.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_next.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_rect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_reset.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_rewind.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Iterator_rgn.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Op.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Spanerator_const_SkRegion_int_int_int.cpp +FILE: ../../../third_party/skia/docs/examples/Region_Spanerator_next.cpp +FILE: ../../../third_party/skia/docs/examples/Region_computeRegionComplexity.cpp +FILE: ../../../third_party/skia/docs/examples/Region_contains.cpp +FILE: ../../../third_party/skia/docs/examples/Region_contains_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_contains_3.cpp +FILE: ../../../third_party/skia/docs/examples/Region_copy_const_SkIRect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_copy_const_SkRegion.cpp +FILE: ../../../third_party/skia/docs/examples/Region_copy_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Region_destructor.cpp +FILE: ../../../third_party/skia/docs/examples/Region_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/Region_equal1_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Region_getBoundaryPath.cpp +FILE: ../../../third_party/skia/docs/examples/Region_getBounds.cpp +FILE: ../../../third_party/skia/docs/examples/Region_intersects.cpp +FILE: ../../../third_party/skia/docs/examples/Region_intersects_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_isComplex.cpp +FILE: ../../../third_party/skia/docs/examples/Region_isEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Region_isRect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_notequal1_operator.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_1.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_3.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_4.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_5.cpp +FILE: ../../../third_party/skia/docs/examples/Region_op_6.cpp +FILE: ../../../third_party/skia/docs/examples/Region_quickContains.cpp +FILE: ../../../third_party/skia/docs/examples/Region_quickContains_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_quickReject.cpp +FILE: ../../../third_party/skia/docs/examples/Region_quickReject_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_readFromMemory.cpp +FILE: ../../../third_party/skia/docs/examples/Region_set.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setEmpty.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setPath.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setRect.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setRect_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setRects.cpp +FILE: ../../../third_party/skia/docs/examples/Region_setRegion.cpp +FILE: ../../../third_party/skia/docs/examples/Region_swap.cpp +FILE: ../../../third_party/skia/docs/examples/Region_translate.cpp +FILE: ../../../third_party/skia/docs/examples/Region_translate_2.cpp +FILE: ../../../third_party/skia/docs/examples/Region_writeToMemory.cpp +FILE: ../../../third_party/skia/docs/examples/Saturation.cpp +FILE: ../../../third_party/skia/docs/examples/Screen.cpp +FILE: ../../../third_party/skia/docs/examples/Shader_Methods_a.cpp +FILE: ../../../third_party/skia/docs/examples/Shader_Methods_b.cpp +FILE: ../../../third_party/skia/docs/examples/Soft_Light.cpp +FILE: ../../../third_party/skia/docs/examples/Src.cpp +FILE: ../../../third_party/skia/docs/examples/Src_Atop.cpp +FILE: ../../../third_party/skia/docs/examples/Src_In.cpp +FILE: ../../../third_party/skia/docs/examples/Src_Out.cpp +FILE: ../../../third_party/skia/docs/examples/Src_Over.cpp +FILE: ../../../third_party/skia/docs/examples/State_Stack_a.cpp +FILE: ../../../third_party/skia/docs/examples/State_Stack_b.cpp +FILE: ../../../third_party/skia/docs/examples/Stroke_Width.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeFromBackendTexture.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeFromBackendTextureAsRenderTarget.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeNull.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRaster.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRasterDirect.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRasterDirectReleaseProc.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRasterN32Premul.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRaster_2.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRenderTarget.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRenderTarget_2.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_MakeRenderTarget_3.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_characterize.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_draw.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_draw_2.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_getCanvas.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_height.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_makeImageSnapshot.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_makeImageSnapshot_2.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_makeSurface.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_notifyContentWillChange.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_peekPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_props.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_readPixels.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_readPixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_readPixels_3.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_width.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_writePixels.cpp +FILE: ../../../third_party/skia/docs/examples/Surface_writePixels_2.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlobBuilder_allocRun.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlobBuilder_allocRunPos.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlobBuilder_allocRunPosH.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlobBuilder_empty_constructor.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlobBuilder_make.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_Deserialize.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_MakeFromString.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_MakeFromText.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_bounds.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_getIntercepts.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_serialize.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_serialize_2.cpp +FILE: ../../../third_party/skia/docs/examples/TextBlob_uniqueID.cpp +FILE: ../../../third_party/skia/docs/examples/Text_Encoding.cpp +FILE: ../../../third_party/skia/docs/examples/Text_Scale_X.cpp +FILE: ../../../third_party/skia/docs/examples/Text_Size.cpp +FILE: ../../../third_party/skia/docs/examples/Text_Skew_X.cpp +FILE: ../../../third_party/skia/docs/examples/Typeface_Methods.cpp +FILE: ../../../third_party/skia/docs/examples/Xor.cpp +FILE: ../../../third_party/skia/gm/crbug_918512.cpp ---------------------------------------------------------------------------------------------------- -Copyright 2018 Google LLC +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 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/experimental/wasm-skp-debugger/debugger_bindings.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/experimental/wasm-skp-debugger/debugger_bindings.cpp +FILE: ../../../third_party/skia/gm/runtimecolorfilter.cpp +FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h +FILE: ../../../third_party/skia/modules/canvaskit/WasmAliases.h +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/SkCurve.h +FILE: ../../../third_party/skia/modules/particles/include/SkParticleAffector.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/SkCurve.cpp +FILE: ../../../third_party/skia/modules/particles/src/SkParticleAffector.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/src/core/SkColorFilterPriv.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/sksl/SkSLByteCode.h +FILE: ../../../third_party/skia/src/sksl/SkSLByteCodeGenerator.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLByteCodeGenerator.h +---------------------------------------------------------------------------------------------------- +Copyright 2019 Google LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -4685,16 +5905,59 @@ 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/fuzz/FuzzEncoders.cpp + ../../../third_party/skia/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/skia/fuzz/FuzzEncoders.cpp +FILE: ../../../third_party/skia/fuzz/FuzzPolyUtils.cpp +FILE: ../../../third_party/skia/include/private/GrSkSLFPFactoryCache.h +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit_bindings.cpp +FILE: ../../../third_party/skia/modules/pathkit/pathkit_wasm_bindings.cpp +FILE: ../../../third_party/skia/src/core/SkPicture_none.cpp +FILE: ../../../third_party/skia/src/gpu/ccpr/GrCoverageCountingPathRenderer_none.cpp +---------------------------------------------------------------------------------------------------- +Copyright 2018 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 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/oss_fuzz/FuzzAPIImageFilter.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/fuzz/FuzzCommon.h FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzAPIImageFilter.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzAndroidCodec.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzAnimatedImage.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzDrawFunctions.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzGradients.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzImage.cpp +FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzIncrementalImage.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzJPEGEncoder.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzJSON.cpp FILE: ../../../third_party/skia/fuzz/oss_fuzz/FuzzMockGPUCanvas.cpp @@ -4737,6 +6000,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/fuzz/oss_fuzz/FuzzSKSL2GLSL.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 +---------------------------------------------------------------------------------------------------- +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 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/gm/circles.cpp + ../../../third_party/skia/LICENSE @@ -4788,7 +6089,6 @@ FILE: ../../../third_party/skia/include/core/SkGraphics.h FILE: ../../../third_party/skia/include/core/SkMaskFilter.h FILE: ../../../third_party/skia/include/core/SkMath.h FILE: ../../../third_party/skia/include/core/SkMatrix.h -FILE: ../../../third_party/skia/include/core/SkMetaData.h FILE: ../../../third_party/skia/include/core/SkPaint.h FILE: ../../../third_party/skia/include/core/SkPath.h FILE: ../../../third_party/skia/include/core/SkPathEffect.h @@ -4813,6 +6113,7 @@ FILE: ../../../third_party/skia/include/effects/SkDashPathEffect.h FILE: ../../../third_party/skia/include/effects/SkDiscretePathEffect.h FILE: ../../../third_party/skia/include/effects/SkGradientShader.h FILE: ../../../third_party/skia/include/effects/SkTableMaskFilter.h +FILE: ../../../third_party/skia/include/private/SkColorData.h FILE: ../../../third_party/skia/include/private/SkFixed.h FILE: ../../../third_party/skia/include/private/SkFloatingPoint.h FILE: ../../../third_party/skia/include/private/SkNoncopyable.h @@ -4841,7 +6142,6 @@ FILE: ../../../third_party/skia/src/core/SkBlurMask.h FILE: ../../../third_party/skia/src/core/SkBuffer.cpp FILE: ../../../third_party/skia/src/core/SkBuffer.h FILE: ../../../third_party/skia/src/core/SkColor.cpp -FILE: ../../../third_party/skia/src/core/SkColorData.h FILE: ../../../third_party/skia/src/core/SkColorFilter.cpp FILE: ../../../third_party/skia/src/core/SkCoreBlitters.h FILE: ../../../third_party/skia/src/core/SkDebug.cpp @@ -4853,17 +6153,13 @@ FILE: ../../../third_party/skia/src/core/SkEdge.cpp FILE: ../../../third_party/skia/src/core/SkEdge.h FILE: ../../../third_party/skia/src/core/SkEndian.h FILE: ../../../third_party/skia/src/core/SkFDot6.h -FILE: ../../../third_party/skia/src/core/SkFDot6Constants.h FILE: ../../../third_party/skia/src/core/SkGeometry.cpp FILE: ../../../third_party/skia/src/core/SkGeometry.h FILE: ../../../third_party/skia/src/core/SkGlyph.h -FILE: ../../../third_party/skia/src/core/SkGlyphCache.cpp -FILE: ../../../third_party/skia/src/core/SkGlyphCache.h FILE: ../../../third_party/skia/src/core/SkGraphics.cpp FILE: ../../../third_party/skia/src/core/SkMask.h FILE: ../../../third_party/skia/src/core/SkMaskFilter.cpp FILE: ../../../third_party/skia/src/core/SkMatrix.cpp -FILE: ../../../third_party/skia/src/core/SkMetaData.cpp FILE: ../../../third_party/skia/src/core/SkModeColorFilter.cpp FILE: ../../../third_party/skia/src/core/SkOSFile.h FILE: ../../../third_party/skia/src/core/SkPaint.cpp @@ -4884,6 +6180,8 @@ FILE: ../../../third_party/skia/src/core/SkScan_Path.cpp FILE: ../../../third_party/skia/src/core/SkSpriteBlitter.h FILE: ../../../third_party/skia/src/core/SkSpriteBlitter_ARGB32.cpp FILE: ../../../third_party/skia/src/core/SkStream.cpp +FILE: ../../../third_party/skia/src/core/SkStrike.cpp +FILE: ../../../third_party/skia/src/core/SkStrike.h FILE: ../../../third_party/skia/src/core/SkString.cpp FILE: ../../../third_party/skia/src/core/SkStroke.h FILE: ../../../third_party/skia/src/core/SkStrokerPriv.cpp @@ -4920,6 +6218,8 @@ FILE: ../../../third_party/skia/src/shaders/gradients/SkGradientShader.cpp FILE: ../../../third_party/skia/src/utils/SkBase64.cpp FILE: ../../../third_party/skia/src/utils/SkBase64.h FILE: ../../../third_party/skia/src/utils/SkCamera.cpp +FILE: ../../../third_party/skia/src/utils/SkMetaData.cpp +FILE: ../../../third_party/skia/src/utils/SkMetaData.h FILE: ../../../third_party/skia/src/utils/SkParse.cpp FILE: ../../../third_party/skia/src/utils/SkParseColor.cpp FILE: ../../../third_party/skia/src/xml/SkDOM.cpp @@ -5135,6 +6435,11 @@ ORIGIN: ../../../third_party/skia/include/docs/SkPDFDocument.h + ../../../third_ 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 +FILE: ../../../third_party/skia/src/pdf/SkPDFUnion.h +FILE: ../../../third_party/skia/src/pdf/SkUUID.h FILE: ../../../third_party/skia/src/utils/SkUTF.cpp FILE: ../../../third_party/skia/src/utils/SkUTF.h ---------------------------------------------------------------------------------------------------- @@ -5229,6 +6534,7 @@ FILE: ../../../third_party/skia/src/gpu/GrRectanizer.h FILE: ../../../third_party/skia/src/gpu/GrRectanizer_pow2.cpp FILE: ../../../third_party/skia/src/gpu/GrRenderTargetOpList.cpp FILE: ../../../third_party/skia/src/gpu/GrRenderTargetOpList.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/ports/SkDebug_win.cpp @@ -5269,8 +6575,8 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/include/gpu/GrDriverBugWorkaroundsAutogen.h FILE: ../../../third_party/skia/include/utils/SkTraceEventPhase.h FILE: ../../../third_party/skia/infra/lottiecap/gold/lottie-web-aggregator.go -FILE: ../../../third_party/skia/infra/pathkit/gold/pathkit_gold_aggregator.go -FILE: ../../../third_party/skia/infra/pathkit/perf/pathkit_perf_aggregator.go +FILE: ../../../third_party/skia/infra/pathkit/gold/wasm_gold_aggregator.go +FILE: ../../../third_party/skia/infra/pathkit/perf/wasm_perf_aggregator.go ---------------------------------------------------------------------------------------------------- Copyright 2018 The Chromium Authors. All rights reserved. @@ -5414,7 +6720,6 @@ LIBRARY: skia ORIGIN: ../../../third_party/skia/src/codec/SkColorTable.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd FILE: ../../../third_party/skia/src/codec/SkColorTable.cpp -FILE: ../../../third_party/skia/src/core/SkBitmapProcState_filter.h FILE: ../../../third_party/skia/src/core/SkCubicClipper.cpp FILE: ../../../third_party/skia/src/core/SkCubicClipper.h FILE: ../../../third_party/skia/src/core/SkEdgeClipper.cpp @@ -5423,10 +6728,6 @@ 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 -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_opts_SSE2.h -FILE: ../../../third_party/skia/src/opts/SkBlitRow_opts_SSE2.h -FILE: ../../../third_party/skia/src/opts/opts_check_x86.cpp ---------------------------------------------------------------------------------------------------- Copyright 2009 The Android Open Source Project @@ -5493,7 +6794,6 @@ ORIGIN: ../../../third_party/skia/src/compute/sk/SkDevice_Compute.h + ../../../t TYPE: LicenseType.bsd FILE: ../../../third_party/skia/src/compute/sk/SkDevice_Compute.h FILE: ../../../third_party/skia/src/core/SkScan_AAAPath.cpp -FILE: ../../../third_party/skia/src/core/SkScan_DAAPath.cpp ---------------------------------------------------------------------------------------------------- Copyright 2016 The Android Open Source Project @@ -5642,9 +6942,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: skia -ORIGIN: ../../../third_party/skia/src/core/SkFlattenablePriv.h + ../../../third_party/skia/LICENSE +ORIGIN: ../../../third_party/skia/src/core/SkGlyphRun.cpp + ../../../third_party/skia/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/src/core/SkFlattenablePriv.h FILE: ../../../third_party/skia/src/core/SkGlyphRun.cpp FILE: ../../../third_party/skia/src/core/SkGlyphRun.h FILE: ../../../third_party/skia/src/core/SkGlyphRunPainter.cpp @@ -5685,7 +6984,6 @@ ORIGIN: ../../../third_party/skia/src/core/SkMatrixImageFilter.cpp + ../../../th TYPE: LicenseType.bsd FILE: ../../../third_party/skia/src/core/SkMatrixImageFilter.cpp FILE: ../../../third_party/skia/src/core/SkMatrixImageFilter.h -FILE: ../../../third_party/skia/src/opts/SkColor_opts_SSE2.h ---------------------------------------------------------------------------------------------------- Copyright 2014 The Android Open Source Project @@ -5823,41 +7121,6 @@ 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/opts/SkBitmapProcState_matrixProcs_neon.cpp + ../../../third_party/skia/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/skia/src/opts/SkBitmapProcState_matrixProcs_neon.cpp ----------------------------------------------------------------------------------------------------- -Copyright 2009 Motorola - -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/src/ports/SkFontConfigInterface_direct.cpp + ../../../third_party/skia/LICENSE diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index c3ff235b72fd5..c1da59b9aaf3e 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: aafea799ed666129bcb0e64c356e77f1 +Signature: 7fd62bb67d412c7e900c9f47816f1440 UNUSED LICENSES: @@ -149,7 +149,7 @@ FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCryp FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/policydocs/BoringCrypto-Security-Policy-20170615.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/print.c +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 @@ -158,6 +158,7 @@ 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/fipstools/run_cavp.go FILE: ../../../third_party/boringssl/src/infra/config/cq.cfg @@ -167,6 +168,7 @@ 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/yasm-win32.exe.sha1 @@ -386,10 +388,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LIBRARY: boringssl LIBRARY: observatory_pub_packages LIBRARY: vulkan +LIBRARY: wuffs ORIGIN: ../../../flutter/third_party/txt/LICENSE TYPE: LicenseType.apache FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/METADATA 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 FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_eax_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_gcm_siv_test.json @@ -397,6 +401,20 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/aes_ 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_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 +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_brainpoolP320r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_brainpoolP384r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_brainpoolP512r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp224r1_ecpoint_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp224r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp256k1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp256r1_ecpoint_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp256r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp384r1_ecpoint_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp384r1_test.json +FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdh_secp521r1_ecpoint_test.json +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_test.json @@ -406,8 +424,11 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecds 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_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_sha512_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_sha512_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_sha512_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_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecdsa_secp521r1_sha512_test.json @@ -416,6 +437,21 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/ecds FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/eddsa_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/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_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_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_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_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_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_test.json FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/async.dart @@ -466,8 +502,114 @@ FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/qu FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/time/util.dart FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/strings.dart FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/time.dart -FILE: ../../../third_party/vulkan/src/vulkan/vk_platform.h -FILE: ../../../third_party/vulkan/src/vulkan/vulkan.h +FILE: ../../../third_party/vulkan/include/vulkan/vk_platform.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_android.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_core.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_fuchsia.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_ios.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_macos.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_vi.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_wayland.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_win32.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_xcb.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_xlib.h +FILE: ../../../third_party/vulkan/include/vulkan/vulkan_xlib_xrandr.h +FILE: ../../../third_party/wuffs/cmd/commonflags/commonflags.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/genlib.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/all-impl.c +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/core-private.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/core-public.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/image-impl.c +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/image-private.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/image-public.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/io-private.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/io-public.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/memory-private.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/memory-public.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/range-private.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/base/range-public.h +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/builtin.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/cgen.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/data.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/expr.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/func.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/gen.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/resume.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/statement.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/internal/cgen/var.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/main.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/release.go +FILE: ../../../third_party/wuffs/cmd/wuffs-c/test.go +FILE: ../../../third_party/wuffs/cmd/wuffs/gen.go +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/crc32/crc32.cc +FILE: ../../../third_party/wuffs/example/gifplayer/gifplayer.c +FILE: ../../../third_party/wuffs/example/library/library.c +FILE: ../../../third_party/wuffs/example/zcat/zcat.c +FILE: ../../../third_party/wuffs/fuzz/c/fuzzlib/fuzzlib.c +FILE: ../../../third_party/wuffs/fuzz/c/std/gif_fuzzer.c +FILE: ../../../third_party/wuffs/fuzz/c/std/zlib_fuzzer.c +FILE: ../../../third_party/wuffs/lang/ast/ast.go +FILE: ../../../third_party/wuffs/lang/ast/eq.go +FILE: ../../../third_party/wuffs/lang/ast/sort.go +FILE: ../../../third_party/wuffs/lang/ast/string.go +FILE: ../../../third_party/wuffs/lang/ast/string_test.go +FILE: ../../../third_party/wuffs/lang/builtin/builtin.go +FILE: ../../../third_party/wuffs/lang/check/assert.go +FILE: ../../../third_party/wuffs/lang/check/bounds.go +FILE: ../../../third_party/wuffs/lang/check/check.go +FILE: ../../../third_party/wuffs/lang/check/check_test.go +FILE: ../../../third_party/wuffs/lang/check/data.go +FILE: ../../../third_party/wuffs/lang/check/gen.go +FILE: ../../../third_party/wuffs/lang/check/optimize.go +FILE: ../../../third_party/wuffs/lang/check/resolve.go +FILE: ../../../third_party/wuffs/lang/check/type.go +FILE: ../../../third_party/wuffs/lang/generate/generate.go +FILE: ../../../third_party/wuffs/lang/parse/parse.go +FILE: ../../../third_party/wuffs/lang/render/render.go +FILE: ../../../third_party/wuffs/lang/token/list.go +FILE: ../../../third_party/wuffs/lang/token/token.go +FILE: ../../../third_party/wuffs/lang/wuffsroot/wuffsroot.go +FILE: ../../../third_party/wuffs/lib/base38/base38.go +FILE: ../../../third_party/wuffs/lib/base38/base38_test.go +FILE: ../../../third_party/wuffs/lib/interval/interval.go +FILE: ../../../third_party/wuffs/lib/interval/interval_test.go +FILE: ../../../third_party/wuffs/lib/interval/radial_test.go +FILE: ../../../third_party/wuffs/release/c/wuffs-unsupported-snapshot.c +FILE: ../../../third_party/wuffs/release/c/wuffs-v0.2.c +FILE: ../../../third_party/wuffs/script/adler32-standalone.c +FILE: ../../../third_party/wuffs/script/bench-c-deflate-fragmentation.c +FILE: ../../../third_party/wuffs/script/bench-go-gif/main.go +FILE: ../../../third_party/wuffs/script/bench-rust-gif-dot-rs/src/main.rs +FILE: ../../../third_party/wuffs/script/bench-rust-gif/src/main.rs +FILE: ../../../third_party/wuffs/script/checksum.go +FILE: ../../../third_party/wuffs/script/compress-giflzw.go +FILE: ../../../third_party/wuffs/script/crawl.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/print-bits.go +FILE: ../../../third_party/wuffs/script/print-crc32-example.go +FILE: ../../../third_party/wuffs/script/print-crc32-magic-numbers.go +FILE: ../../../third_party/wuffs/script/print-deflate-magic-numbers.go +FILE: ../../../third_party/wuffs/script/print-lzw-example.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/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/decode_gif.wuffs +FILE: ../../../third_party/wuffs/std/gzip/decode_gzip.wuffs +FILE: ../../../third_party/wuffs/std/lzw/decode_lzw.wuffs +FILE: ../../../third_party/wuffs/std/zlib/decode_zlib.wuffs ---------------------------------------------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -1078,7 +1220,7 @@ 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/ecdh_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 @@ -1193,7 +1335,6 @@ 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_ssl3.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 @@ -1231,7 +1372,6 @@ 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/custom_extensions.cc 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 @@ -1455,6 +1595,7 @@ LIBRARY: boringssl ORIGIN: ../../../third_party/boringssl/src/crypto/bytestring/unicode.c TYPE: LicenseType.unknown 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/fipsmodule/bn/div_extra.c @@ -1465,12 +1606,14 @@ FILE: ../../../third_party/boringssl/src/crypto/fipsmodule/ec/make_p256-x86_64-t 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/tls/internal.h +FILE: ../../../third_party/boringssl/src/crypto/pem/pem_test.cc FILE: ../../../third_party/boringssl/src/crypto/self_test.cc FILE: ../../../third_party/boringssl/src/fipstools/cavp_kas_test.cc FILE: ../../../third_party/boringssl/src/fipstools/cavp_tlskdf_test.cc FILE: ../../../third_party/boringssl/src/include/openssl/e_os2.h FILE: ../../../third_party/boringssl/src/ssl/handoff.cc -FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/convert_wycheproof.go +FILE: ../../../third_party/boringssl/src/util/check_filenames.go +FILE: ../../../third_party/boringssl/src/util/convert_wycheproof.go ---------------------------------------------------------------------------------------------------- Copyright (c) 2018, Google Inc. @@ -1855,9 +1998,10 @@ OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh/ecdh.c +ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh_extra/ecdh_extra.c TYPE: LicenseType.bsd -FILE: ../../../third_party/boringssl/src/crypto/ecdh/ecdh.c +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. @@ -1909,9 +2053,10 @@ OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/ecdh/ecdh.c + ../../../third_party/boringssl/src/crypto/ecdh/ecdh.c +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/ecdh.c +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. @@ -2968,9 +3113,9 @@ OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: boringssl -ORIGIN: ../../../third_party/boringssl/src/crypto/rsa_extra/print.c + ../../../third_party/boringssl/src/LICENSE +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/print.c +FILE: ../../../third_party/boringssl/src/crypto/rsa_extra/rsa_print.c ---------------------------------------------------------------------------------------------------- Copyright 2006-2017 The OpenSSL Project Authors. All Rights Reserved. @@ -3033,6 +3178,20 @@ 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 @@ -4439,6 +4598,8 @@ FILE: ../../../third_party/dart/client/idea/.idea/vcs.xml FILE: ../../../third_party/dart/runtime/CPPLINT.cfg 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/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 @@ -4448,13 +4609,14 @@ 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/vm/snapshot_test_in.dat +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/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 -FILE: ../../../third_party/dart/third_party/gsutil.tar.gz.sha1 ---------------------------------------------------------------------------------------------------- Copyright 2012, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -4495,6 +4657,7 @@ 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/common_patch.dart +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 @@ -4692,6 +4855,7 @@ FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler.h 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 @@ -4799,8 +4963,6 @@ 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/resolver_test.cc -FILE: ../../../third_party/dart/runtime/vm/scanner.cc -FILE: ../../../third_party/dart/runtime/vm/scanner_test.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 @@ -4827,9 +4989,7 @@ 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_android.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_linux.cc -FILE: ../../../third_party/dart/runtime/vm/virtual_memory_macos.cc +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 @@ -4932,7 +5092,6 @@ FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_dart2js.dart FILE: ../../../third_party/dart/sdk/lib/web_sql/dart2js/web_sql_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. @@ -4962,6 +5121,123 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: dart +ORIGIN: ../../../third_party/dart/runtime/bin/abi_version.h + ../../../third_party/dart/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../third_party/dart/runtime/bin/abi_version.h +FILE: ../../../third_party/dart/runtime/bin/abi_version_in.cc +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_dynamic_library.cc +FILE: ../../../third_party/dart/runtime/bin/ffi_test_functions.cc +FILE: ../../../third_party/dart/runtime/bin/namespace_fuchsia.h +FILE: ../../../third_party/dart/runtime/lib/ffi.cc +FILE: ../../../third_party/dart/runtime/lib/ffi_dynamic_library.cc +FILE: ../../../third_party/dart/runtime/lib/ffi_dynamic_library_patch.dart +FILE: ../../../third_party/dart/runtime/lib/ffi_native_type_patch.dart +FILE: ../../../third_party/dart/runtime/lib/ffi_patch.dart +FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.html +FILE: ../../../third_party/dart/runtime/tools/graphexplorer/graphexplorer.js +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_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/flow_graph_checker.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/flow_graph_checker.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/slot_test.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/ffi.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/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/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_dbc.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_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/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/longjump.h +FILE: ../../../third_party/dart/runtime/vm/pointer_tagging.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/runtime/vm/type_testing_stubs_arm.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_x64.cc +FILE: ../../../third_party/dart/samples/ffi/coordinate.dart +FILE: ../../../third_party/dart/samples/ffi/dylib_utils.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/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/cstring.dart +FILE: ../../../third_party/dart/samples/ffi/sqlite/lib/src/ffi/dylib_utils.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 +---------------------------------------------------------------------------------------------------- +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/runtime/bin/cli.cc + ../../../third_party/dart/LICENSE @@ -5073,7 +5349,6 @@ 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_jemalloc.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 @@ -5164,6 +5439,104 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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/tools/dartfuzz/dartfuzz_values.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/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_evaluator.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.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 @@ -5350,6 +5723,7 @@ FILE: ../../../third_party/dart/runtime/platform/utils_fuchsia.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/code_patcher_dbc.cc +FILE: ../../../third_party/dart/runtime/vm/compiler/asm_intrinsifier_dbc.cc FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_dbc.cc FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_dbc.h FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_dbc_test.cc @@ -5363,7 +5737,6 @@ FILE: ../../../third_party/dart/runtime/vm/compiler/backend/redundancy_eliminati 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/compiler/intrinsifier_dbc.cc FILE: ../../../third_party/dart/runtime/vm/constants_dbc.h FILE: ../../../third_party/dart/runtime/vm/cpu_dbc.cc FILE: ../../../third_party/dart/runtime/vm/cpu_dbc.h @@ -5399,7 +5772,6 @@ FILE: ../../../third_party/dart/runtime/vm/signal_handler_fuchsia.cc FILE: ../../../third_party/dart/runtime/vm/simulator_dbc.cc FILE: ../../../third_party/dart/runtime/vm/simulator_dbc.h FILE: ../../../third_party/dart/runtime/vm/stack_frame_dbc.h -FILE: ../../../third_party/dart/runtime/vm/stub_code_dbc.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 @@ -5440,89 +5812,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: dart -ORIGIN: ../../../third_party/dart/runtime/bin/dart_embedder_api_impl.cc + ../../../third_party/dart/LICENSE -TYPE: LicenseType.bsd -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/loops.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/backend/loops.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.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/constant_evaluator.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/frontend/constant_evaluator.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/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/interpreter_unsupported.cc -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/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/process_test.cc + ../../../third_party/dart/LICENSE @@ -5557,6 +5846,7 @@ 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 @@ -5567,7 +5857,6 @@ 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.h 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 @@ -5578,6 +5867,7 @@ 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 @@ -5589,7 +5879,6 @@ 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/scanner.h 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 @@ -5725,9 +6014,6 @@ 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/convert/base64.dart -FILE: ../../../third_party/dart/sdk/lib/dart_client.platform -FILE: ../../../third_party/dart/sdk/lib/dart_server.platform -FILE: ../../../third_party/dart/sdk/lib/dart_shared.platform 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 @@ -5924,9 +6210,6 @@ 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/intrinsifier_arm.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/compiler/intrinsifier_x64.cc 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 @@ -5980,10 +6263,7 @@ 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.cc FILE: ../../../third_party/dart/runtime/vm/stub_code_arm_test.cc -FILE: ../../../third_party/dart/runtime/vm/stub_code_ia32.cc -FILE: ../../../third_party/dart/runtime/vm/stub_code_x64.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 @@ -6194,7 +6474,6 @@ 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/intrinsifier_arm64.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 @@ -6245,7 +6524,6 @@ 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.cc 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 @@ -6290,6 +6568,40 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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 +---------------------------------------------------------------------------------------------------- +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: double-conversion LIBRARY: icu @@ -7703,25 +8015,326 @@ THE SOFTWARE. ==================================================================================================== ==================================================================================================== -LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/COPYING -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/.circleci/config.yml -FILE: ../../../third_party/harfbuzz/.editorconfig -FILE: ../../../third_party/harfbuzz/THANKS -FILE: ../../../third_party/harfbuzz/TODO -FILE: ../../../third_party/harfbuzz/appveyor.yml -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/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-glyph-information.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-hello-harfbuzz.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-install-harfbuzz.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-opentype-features.xml -FILE: ../../../third_party/harfbuzz/docs/usermanual-what-is-harfbuzz.xml +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/.editorconfig +FILE: ../../../third_party/harfbuzz/THANKS +FILE: ../../../third_party/harfbuzz/TODO +FILE: ../../../third_party/harfbuzz/appveyor.yml +FILE: ../../../third_party/harfbuzz/azure-pipelines.yml +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/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-hello-harfbuzz.xml +FILE: ../../../third_party/harfbuzz/docs/usermanual-install-harfbuzz.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-what-is-harfbuzz.xml FILE: ../../../third_party/harfbuzz/docs/version.xml.in FILE: ../../../third_party/harfbuzz/harfbuzz.doap FILE: ../../../third_party/harfbuzz/src/Makefile.sources @@ -7733,6 +8346,9 @@ FILE: ../../../third_party/harfbuzz/src/harfbuzz.pc.in 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-unicode-emoji-table.hh ---------------------------------------------------------------------------------------------------- HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. For parts of HarfBuzz that are licensed under different licenses see individual @@ -7815,23 +8431,37 @@ 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-iter-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-iter.hh FILE: ../../../third_party/harfbuzz/src/hb-map-private.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-null.hh +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.cc +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-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-khmer.hh FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-myanmar-private.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-glyf.cc FILE: ../../../third_party/harfbuzz/src/hb-subset-glyf.hh 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-private.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-name-table.cc FILE: ../../../third_party/harfbuzz/src/test-unicode-ranges.cc ---------------------------------------------------------------------------------------------------- Copyright © 2018 Google, Inc. @@ -7862,9 +8492,11 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-aat-layout-common-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-aat-layout-common-private.hh +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-private.hh FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.cc +FILE: ../../../third_party/harfbuzz/src/hb-aat-layout.hh FILE: ../../../third_party/harfbuzz/src/hb-debug.hh FILE: ../../../third_party/harfbuzz/src/hb-dsalgs.hh FILE: ../../../third_party/harfbuzz/src/hb-ot-kern-table.hh @@ -7931,13 +8563,47 @@ 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-atomic-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-atomic-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-atomic.hh FILE: ../../../third_party/harfbuzz/src/hb-mutex-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-mutex.hh FILE: ../../../third_party/harfbuzz/src/hb-object-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-object.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007 Chris Wilson Copyright © 2009,2010 Red Hat, Inc. @@ -7969,6 +8635,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-blob-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-blob-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-blob.hh ---------------------------------------------------------------------------------------------------- Copyright © 2009 Red Hat, Inc. Copyright © 2018 Google, Inc. @@ -8100,6 +8767,7 @@ ORIGIN: ../../../third_party/harfbuzz/src/hb-buffer-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-buffer-private.hh 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. @@ -8186,6 +8854,57 @@ 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-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize.hh +FILE: ../../../third_party/harfbuzz/src/hb-set-digest-private.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-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.cc +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.h +FILE: ../../../third_party/harfbuzz/src/hb-shape-plan.hh +FILE: ../../../third_party/harfbuzz/src/hb-shaper-impl-private.hh +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-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-shaper.cc +FILE: ../../../third_party/harfbuzz/src/hb-shaper.hh +FILE: ../../../third_party/harfbuzz/src/hb-warning.cc +---------------------------------------------------------------------------------------------------- +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-common.cc @@ -8222,6 +8941,7 @@ 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-private.hh +FILE: ../../../third_party/harfbuzz/src/hb.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009 Red Hat, Inc. Copyright © 2011,2012 Google, Inc. @@ -8369,7 +9089,9 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-face-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-face-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-face.hh FILE: ../../../third_party/harfbuzz/src/hb-font-private.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 @@ -8626,6 +9348,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-machinery-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-machinery-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-machinery.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009,2010 Red Hat, Inc. Copyright © 2012,2018 Google, Inc. @@ -8656,6 +9379,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-open-file-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-open-file-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-open-file.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009 Red Hat, Inc. Copyright © 2012 Google, Inc. @@ -8686,6 +9410,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-open-type-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-open-type-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-open-type.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009,2010 Red Hat, Inc. Copyright © 2012 Google, Inc. @@ -8803,6 +9528,69 @@ 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.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-face.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-private.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 @@ -8969,6 +9757,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-common-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-common-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-common.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009 Red Hat, Inc. Copyright © 2010,2012 Google, Inc. @@ -9060,6 +9849,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-gsubgpos.hh ---------------------------------------------------------------------------------------------------- Copyright © 2007,2008,2009,2010 Red Hat, Inc. Copyright © 2010,2012 Google, Inc. @@ -9085,36 +9875,6 @@ 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-private.hh -TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-layout-private.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-layout.cc @@ -9182,6 +9942,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-map-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-map-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-map.hh ---------------------------------------------------------------------------------------------------- Copyright © 2009,2010 Red Hat, Inc. Copyright © 2010,2011,2012,2013 Google, Inc. @@ -9207,36 +9968,6 @@ 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.cc -TYPE: LicenseType.unknown -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-ot-math-table.hh @@ -9270,25 +10001,11 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ==================================================================================================== LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh +ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-name.h TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-indic-private.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-fallback-private.hh -FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-normalize-private.hh -FILE: ../../../third_party/harfbuzz/src/hb-set-digest-private.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-private.hh -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-private.hh -FILE: ../../../third_party/harfbuzz/src/hb-shaper-list.hh -FILE: ../../../third_party/harfbuzz/src/hb-shaper-private.hh -FILE: ../../../third_party/harfbuzz/src/hb-shaper.cc -FILE: ../../../third_party/harfbuzz/src/hb-warning.cc +FILE: ../../../third_party/harfbuzz/src/hb-ot-name.h ---------------------------------------------------------------------------------------------------- -Copyright © 2012 Google, Inc. +Copyright © 2018 Ebrahim Byagowi. This is part of HarfBuzz, a text shaping library. @@ -9316,10 +10033,12 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-arabic-private.hh +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-private.hh 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. @@ -9413,6 +10132,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-complex.hh ---------------------------------------------------------------------------------------------------- Copyright © 2010,2011,2012 Google, Inc. @@ -9442,6 +10162,7 @@ LIBRARY: harfbuzz ORIGIN: ../../../third_party/harfbuzz/src/hb-ot-shape-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-ot-shape-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-ot-shape.hh ---------------------------------------------------------------------------------------------------- Copyright © 2010 Google, Inc. @@ -9525,11 +10246,41 @@ 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-vorg-table.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-ot-vorg-table.hh +---------------------------------------------------------------------------------------------------- +Copyright © 2018 Adobe Systems Incorporated. + + 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-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-set-private.hh +FILE: ../../../third_party/harfbuzz/src/hb-set.hh ---------------------------------------------------------------------------------------------------- Copyright © 2012,2017 Google, Inc. @@ -9605,6 +10356,7 @@ ORIGIN: ../../../third_party/harfbuzz/src/hb-unicode-private.hh TYPE: LicenseType.unknown FILE: ../../../third_party/harfbuzz/src/hb-unicode-private.hh 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 @@ -9664,11 +10416,70 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ==================================================================================================== LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/hb-utf-private.hh +ORIGIN: ../../../third_party/harfbuzz/src/hb-utf-private.hh +TYPE: LicenseType.unknown +FILE: ../../../third_party/harfbuzz/src/hb-utf-private.hh +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/test-buffer-serialize.cc TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/hb-utf-private.hh +FILE: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc ---------------------------------------------------------------------------------------------------- -Copyright © 2011,2012,2014 Google, Inc. +Copyright © 2010,2011,2013 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -9693,11 +10504,12 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ==================================================================================================== LIBRARY: harfbuzz -ORIGIN: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc +ORIGIN: ../../../third_party/harfbuzz/src/test-ot-color.cc TYPE: LicenseType.unknown -FILE: ../../../third_party/harfbuzz/src/test-buffer-serialize.cc +FILE: ../../../third_party/harfbuzz/src/test-ot-color.cc ---------------------------------------------------------------------------------------------------- -Copyright © 2010,2011,2013 Google, Inc. +Copyright © 2018 Ebrahim Byagowi +Copyright © 2018 Khaled Hosny This is part of HarfBuzz, a text shaping library. @@ -12910,6 +13722,547 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +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/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/FileTimeType.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/ThreadingSupportAPI.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/VisibilityMacros.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/__string +FILE: ../../../third_party/libcxx/include/__undef_macros +FILE: ../../../third_party/libcxx/include/any +FILE: ../../../third_party/libcxx/include/atomic +FILE: ../../../third_party/libcxx/include/cinttypes +FILE: ../../../third_party/libcxx/include/cstring +FILE: ../../../third_party/libcxx/include/experimental/any +FILE: ../../../third_party/libcxx/include/experimental/chrono +FILE: ../../../third_party/libcxx/include/experimental/coroutine +FILE: ../../../third_party/libcxx/include/experimental/iterator +FILE: ../../../third_party/libcxx/include/experimental/memory_resource +FILE: ../../../third_party/libcxx/include/experimental/ratio +FILE: ../../../third_party/libcxx/include/experimental/string_view +FILE: ../../../third_party/libcxx/include/inttypes.h +FILE: ../../../third_party/libcxx/include/module.modulemap +FILE: ../../../third_party/libcxx/include/stdint.h +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/tuple +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/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.exp +FILE: ../../../third_party/libcxx/lib/libc++abi2.exp +FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.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/filesystem/int128_builtins.cpp +FILE: ../../../third_party/libcxx/www/TS_deprecation.html +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/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_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/Compiler.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 +---------------------------------------------------------------------------------------------------- +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +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: libcxx +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/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/CapturingConfigInfo.rst +FILE: ../../../third_party/libcxx/docs/DesignDocs/DebugMode.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/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/any +FILE: ../../../third_party/libcxx/include/experimental/chrono +FILE: ../../../third_party/libcxx/include/experimental/coroutine +FILE: ../../../third_party/libcxx/include/experimental/deque +FILE: ../../../third_party/libcxx/include/experimental/dynarray +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/numeric +FILE: ../../../third_party/libcxx/include/experimental/optional +FILE: ../../../third_party/libcxx/include/experimental/propagate_const +FILE: ../../../third_party/libcxx/include/experimental/ratio +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/string_view +FILE: ../../../third_party/libcxx/include/experimental/system_error +FILE: ../../../third_party/libcxx/include/experimental/tuple +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/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/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.exp +FILE: ../../../third_party/libcxx/lib/libc++abi2.exp +FILE: ../../../third_party/libcxx/lib/libc++sjlj-abi.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/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/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/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/TS_deprecation.html +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 +---------------------------------------------------------------------------------------------------- +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2017 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: libcxxabi +ORIGIN: null +TYPE: LicenseType.bsd +FILE: ../../../third_party/libcxxabi/.arcconfig +FILE: ../../../third_party/libcxxabi/fuzz/cxa_demangle_fuzzer.cpp +FILE: ../../../third_party/libcxxabi/src/demangle/Compiler.h +FILE: ../../../third_party/libcxxabi/src/demangle/StringView.h +FILE: ../../../third_party/libcxxabi/src/demangle/Utility.h +FILE: ../../../third_party/libcxxabi/www/index.html +FILE: ../../../third_party/libcxxabi/www/spec.html +---------------------------------------------------------------------------------------------------- +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2018 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: libjpeg-turbo ORIGIN: ../../../third_party/libjpeg-turbo/README.ijg @@ -16708,8 +18061,6 @@ FILE: ../../../third_party/tonic/dart_message_handler.cc FILE: ../../../third_party/tonic/dart_message_handler.h FILE: ../../../third_party/tonic/dart_microtask_queue.cc FILE: ../../../third_party/tonic/dart_microtask_queue.h -FILE: ../../../third_party/tonic/dart_sticky_error.cc -FILE: ../../../third_party/tonic/dart_sticky_error.h FILE: ../../../third_party/tonic/file_loader/file_loader.cc FILE: ../../../third_party/tonic/file_loader/file_loader.h FILE: ../../../third_party/tonic/filesystem/filesystem/eintr_wrapper.h @@ -16896,6 +18247,191 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== +==================================================================================================== +LIBRARY: wuffs +ORIGIN: ../../../third_party/wuffs/LICENSE +TYPE: LicenseType.apache +FILE: ../../../third_party/wuffs/script/bench-rust-gif-dot-rs/Cargo.toml +FILE: ../../../third_party/wuffs/script/bench-rust-gif/Cargo.toml +---------------------------------------------------------------------------------------------------- +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 +==================================================================================================== + ==================================================================================================== LIBRARY: zlib ORIGIN: ../../../third_party/zlib/LICENSE @@ -17631,4 +19167,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: 299 +Total license count: 319 diff --git a/ci/licenses_golden/tool_signature b/ci/licenses_golden/tool_signature new file mode 100644 index 0000000000000..8356ed4f258b8 --- /dev/null +++ b/ci/licenses_golden/tool_signature @@ -0,0 +1,2 @@ +Signature: f2113ac67466eb24d34e9f41c3c59e17 + diff --git a/common/BUILD.gn b/common/BUILD.gn index c0212e3528920..b9b05073501d2 100644 --- a/common/BUILD.gn +++ b/common/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/common/config.gni b/common/config.gni index dd08c934dc22d..f81be4aac0e7a 100644 --- a/common/config.gni +++ b/common/config.gni @@ -1,4 +1,4 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/common/settings.cc b/common/settings.cc index 9aa8997ba947b..b292d376be0c3 100644 --- a/common/settings.cc +++ b/common/settings.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,12 @@ namespace blink { +Settings::Settings() = default; + +Settings::Settings(const Settings& other) = default; + +Settings::~Settings() = default; + std::string Settings::ToString() const { std::stringstream stream; stream << "Settings: " << std::endl; @@ -19,8 +25,6 @@ std::string Settings::ToString() const { << std::endl; stream << "application_library_path: " << application_library_path << std::endl; - stream << "main_dart_file_path: " << main_dart_file_path << std::endl; - stream << "packages_file_path: " << packages_file_path << std::endl; stream << "temp_directory_path: " << temp_directory_path << std::endl; stream << "dart_flags:" << std::endl; for (const auto& dart_flag : dart_flags) { @@ -29,9 +33,12 @@ std::string Settings::ToString() const { stream << "start_paused: " << start_paused << std::endl; stream << "trace_skia: " << trace_skia << std::endl; stream << "trace_startup: " << trace_startup << std::endl; + stream << "trace_systrace: " << trace_systrace << std::endl; + stream << "dump_skp_on_shader_compilation: " << dump_skp_on_shader_compilation + << std::endl; stream << "endless_trace_buffer: " << endless_trace_buffer << std::endl; stream << "enable_dart_profiling: " << enable_dart_profiling << std::endl; - stream << "dart_non_checked_mode: " << dart_non_checked_mode << std::endl; + stream << "disable_dart_asserts: " << disable_dart_asserts << std::endl; stream << "enable_observatory: " << enable_observatory << std::endl; stream << "observatory_port: " << observatory_port << std::endl; stream << "ipv6: " << ipv6 << std::endl; @@ -39,6 +46,8 @@ std::string Settings::ToString() const { stream << "enable_software_rendering: " << enable_software_rendering << std::endl; stream << "log_tag: " << log_tag << std::endl; + stream << "icu_initialization_required: " << icu_initialization_required + << std::endl; stream << "icu_data_path: " << icu_data_path << std::endl; stream << "assets_dir: " << assets_dir << std::endl; stream << "assets_path: " << assets_path << std::endl; diff --git a/common/settings.h b/common/settings.h index 1c0002e7ad7d6..bfbfd4139d322 100644 --- a/common/settings.h +++ b/common/settings.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ #include #include "flutter/fml/closure.h" +#include "flutter/fml/mapping.h" #include "flutter/fml/unique_fd.h" namespace blink { @@ -20,21 +21,41 @@ namespace blink { using TaskObserverAdd = std::function; using TaskObserverRemove = std::function; +using UnhandledExceptionCallback = + std::function; + +// TODO(chinmaygarde): 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)>; struct Settings { + Settings(); + + Settings(const Settings& other); + + ~Settings(); + // VM settings - std::string vm_snapshot_data_path; - std::string vm_snapshot_instr_path; - std::string isolate_snapshot_data_path; - std::string isolate_snapshot_instr_path; + std::string vm_snapshot_data_path; // deprecated + MappingCallback vm_snapshot_data; + std::string vm_snapshot_instr_path; // deprecated + MappingCallback vm_snapshot_instr; + + std::string isolate_snapshot_data_path; // deprecated + MappingCallback isolate_snapshot_data; + std::string isolate_snapshot_instr_path; // deprecated + MappingCallback isolate_snapshot_instr; + + // Returns the Mapping to a kernel buffer which contains sources for dart:* + // libraries. + MappingCallback dart_library_sources_kernel; std::string application_library_path; std::string application_kernel_asset; std::string application_kernel_list_asset; - std::string main_dart_file_path; - std::string packages_file_path; - std::string temp_directory_path; std::vector dart_flags; @@ -42,9 +63,11 @@ struct Settings { bool start_paused = false; bool trace_skia = false; bool trace_startup = false; + bool trace_systrace = false; + bool dump_skp_on_shader_compilation = false; bool endless_trace_buffer = false; bool enable_dart_profiling = false; - bool dart_non_checked_mode = false; + bool disable_dart_asserts = false; // Used as the script URI in debug messages. Does not affect how the Dart code // is executed. std::string advisory_script_uri = "main.dart"; @@ -71,11 +94,29 @@ struct Settings { // The isolate is not current and may have already been destroyed when this // call is made. fml::closure root_isolate_shutdown_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 + // as fast as possible in returning from this callback. Long running + // operations in this callback do have the capability of introducing jank. + std::function idle_notification_callback; + // A callback given to the embedder to react to unhandled exceptions in the + // running Flutter application. This callback is made on an internal engine + // managed thread and embedders must re-thread as necessary. Performing + // blocking calls in this callback will cause applications to jank. + UnhandledExceptionCallback unhandled_exception_callback; bool enable_software_rendering = false; bool skia_deterministic_rendering_on_cpu = false; bool verbose_logging = false; std::string log_tag = "flutter"; + + // The icu_initialization_required setting does not have a corresponding + // switch because it is intended to be decided during build time, not runtime. + // Some companies apply source modification here because their build system + // brings its own ICU data files. + bool icu_initialization_required = true; std::string icu_data_path; + MappingCallback icu_mapper; // Assets settings fml::UniqueFD::element_type assets_dir = diff --git a/common/task_runners.cc b/common/task_runners.cc index bb62c8e9c04e3..d76bc20f6ae38 100644 --- a/common/task_runners.cc +++ b/common/task_runners.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,6 +19,8 @@ TaskRunners::TaskRunners(std::string label, ui_(std::move(ui)), io_(std::move(io)) {} +TaskRunners::TaskRunners(const TaskRunners& other) = default; + TaskRunners::~TaskRunners() = default; const std::string& TaskRunners::GetLabel() const { diff --git a/common/task_runners.h b/common/task_runners.h index 642a8a158f12d..0bd0c301903ae 100644 --- a/common/task_runners.h +++ b/common/task_runners.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,6 +20,8 @@ class TaskRunners { fml::RefPtr ui, fml::RefPtr io); + TaskRunners(const TaskRunners& other); + ~TaskRunners(); const std::string& GetLabel() const; diff --git a/shell/version/BUILD.gn b/common/version/BUILD.gn similarity index 55% rename from shell/version/BUILD.gn rename to common/version/BUILD.gn index 348ea3e26d2a7..d34928de8afaa 100644 --- a/shell/version/BUILD.gn +++ b/common/version/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -11,9 +11,9 @@ source_set("version") { ] defines = [ - "SHELL_FLUTTER_ENGINE_VERSION=\"$shell_engine_version\"", - "SHELL_SKIA_VERSION=\"$shell_skia_version\"", - "SHELL_DART_VERSION=\"$shell_dart_version\"", + "FLUTTER_ENGINE_VERSION=\"$engine_version\"", + "SKIA_VERSION=\"$skia_version\"", + "DART_VERSION=\"$dart_version\"", ] public_configs = [ "$flutter_root:config" ] diff --git a/common/version/version.cc b/common/version/version.cc new file mode 100644 index 0000000000000..e1d1c57d3baa8 --- /dev/null +++ b/common/version/version.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/common/version/version.h" + +namespace blink { + +const char* GetFlutterEngineVersion() { + return FLUTTER_ENGINE_VERSION; +} + +const char* GetSkiaVersion() { + return SKIA_VERSION; +} + +const char* GetDartVersion() { + return DART_VERSION; +} + +} // namespace blink diff --git a/shell/version/version.gni b/common/version/version.gni similarity index 63% rename from shell/version/version.gni rename to common/version/version.gni index 66ca9fdd8e10d..a4520c410006c 100644 --- a/shell/version/version.gni +++ b/common/version/version.gni @@ -1,44 +1,44 @@ -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. declare_args() { - shell_engine_version = "" + engine_version = "" - shell_skia_version = "" + skia_version = "" - shell_dart_version = "" + dart_version = "" } -if (shell_engine_version == "") { - shell_engine_version_lines = +if (engine_version == "") { + engine_version_lines = exec_script("$flutter_root/build/git_revision.py", [ "--repository", rebase_path(flutter_root, "", flutter_root), ], "list lines") - shell_engine_version = shell_engine_version_lines[0] + engine_version = engine_version_lines[0] } -if (shell_skia_version == "") { - shell_skia_version_lines = +if (skia_version == "") { + skia_version_lines = exec_script("$flutter_root/build/git_revision.py", [ "--repository", rebase_path("//third_party/skia", "", flutter_root), ], "list lines") - shell_skia_version = shell_skia_version_lines[0] + skia_version = skia_version_lines[0] } -if (shell_dart_version == "") { - shell_dart_version_lines = +if (dart_version == "") { + dart_version_lines = exec_script("$flutter_root/build/git_revision.py", [ "--repository", rebase_path("//third_party/dart", "", flutter_root), ], "list lines") - shell_dart_version = shell_dart_version_lines[0] + dart_version = dart_version_lines[0] } diff --git a/common/version/version.h b/common/version/version.h new file mode 100644 index 0000000000000..d9ca8aa3f17e3 --- /dev/null +++ b/common/version/version.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_COMMON_VERSION_VERSION_H_ +#define FLUTTER_COMMON_VERSION_VERSION_H_ + +namespace blink { + +const char* GetFlutterEngineVersion(); + +const char* GetSkiaVersion(); + +const char* GetDartVersion(); + +} // namespace blink + +#endif // FLUTTER_COMMON_VERSION_VERSION_H_ diff --git a/examples/BUILD.gn b/examples/BUILD.gn deleted file mode 100644 index 3d6d850afbcab..0000000000000 --- a/examples/BUILD.gn +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -group("examples") { - deps = [ - "hello_flutter", - "hello_flutter:hello_flutter_aot", - "spinning_square", - ] -} diff --git a/examples/hello_flutter/lib/main.dart b/examples/hello_flutter/lib/main.dart deleted file mode 100644 index 3de5584342897..0000000000000 --- a/examples/hello_flutter/lib/main.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2016 The Chromium 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 example shows how to show the text 'Hello, world.' using using the raw -// interface to the engine. - -import 'dart:ui' as ui; - -void beginFrame(Duration timeStamp) { - final double devicePixelRatio = ui.window.devicePixelRatio; - final ui.Size physicalSize = ui.window.physicalSize; - final ui.Size logicalSize = physicalSize / devicePixelRatio; - - final ui.ParagraphBuilder paragraphBuilder = new ui.ParagraphBuilder(new ui.ParagraphStyle()) - ..addText('Hello, world.'); - final ui.Paragraph paragraph = paragraphBuilder.build() - ..layout(new ui.ParagraphConstraints(width: logicalSize.width)); - - final ui.Rect physicalBounds = ui.Offset.zero & physicalSize; - final ui.PictureRecorder recorder = new ui.PictureRecorder(); - final ui.Canvas canvas = new ui.Canvas(recorder, physicalBounds); - canvas.scale(devicePixelRatio, devicePixelRatio); - canvas.drawRect(ui.Offset.zero & logicalSize, new ui.Paint()..color = const ui.Color(0xFF0000FF)); - canvas.drawParagraph(paragraph, new ui.Offset( - (logicalSize.width - paragraph.maxIntrinsicWidth) / 2.0, - (logicalSize.height - paragraph.height) / 2.0 - )); - final ui.Picture picture = recorder.endRecording(); - - final ui.SceneBuilder sceneBuilder = new ui.SceneBuilder() - // TODO(abarth): We should be able to add a picture without pushing a - // container layer first. - ..pushClipRect(physicalBounds) - ..addPicture(ui.Offset.zero, picture) - ..pop(); - - ui.window.render(sceneBuilder.build()); -} - -// This function is the primary entry point to your application. The engine -// calls main() as soon as it has loaded your code. -void main() { - // The engine calls onBeginFrame whenever it wants us to produce a frame. - ui.window.onBeginFrame = beginFrame; - // Here we kick off the whole process by asking the engine to schedule a new - // frame. The engine will eventually call onBeginFrame when it is time for us - // to actually produce the frame. - ui.window.scheduleFrame(); -} diff --git a/examples/hello_flutter/pubspec.yaml b/examples/hello_flutter/pubspec.yaml deleted file mode 100644 index a22a6ee39a974..0000000000000 --- a/examples/hello_flutter/pubspec.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2017 The Chromium 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/examples/spinning_square/lib/main.dart b/examples/spinning_square/lib/main.dart deleted file mode 100644 index a6dc2099058ae..0000000000000 --- a/examples/spinning_square/lib/main.dart +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015 The Chromium 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 example shows how to perform a simple animation using the raw interface -// to the engine. - -import 'dart:math' as math; -import 'dart:typed_data'; -import 'dart:ui' as ui; - -void beginFrame(Duration timeStamp) { - // The timeStamp argument to beginFrame indicates the timing information we - // should use to clock our animations. It's important to use timeStamp rather - // than reading the system time because we want all the parts of the system to - // coordinate the timings of their animations. If each component read the - // system clock independently, the animations that we processed later would be - // slightly ahead of the animations we processed earlier. - - final double devicePixelRatio = ui.window.devicePixelRatio; - - // PAINT - final ui.Size logicalSize = ui.window.physicalSize / devicePixelRatio; - final ui.Rect paintBounds = ui.Offset.zero & logicalSize; - final ui.PictureRecorder recorder = new ui.PictureRecorder(); - final ui.Canvas canvas = new ui.Canvas(recorder, paintBounds); - canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0); - - // Here we determine the rotation according to the timeStamp given to us by - // the engine. - final double t = timeStamp.inMicroseconds / Duration.MICROSECONDS_PER_MILLISECOND / 1800.0; - canvas.rotate(math.PI * (t % 1.0)); - - canvas.drawRect(new ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0), - new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0)); - final ui.Picture picture = recorder.endRecording(); - - // COMPOSITE - - final Float64List deviceTransform = new Float64List(16) - ..[0] = devicePixelRatio - ..[5] = devicePixelRatio - ..[10] = 1.0 - ..[15] = 1.0; - final ui.SceneBuilder sceneBuilder = new ui.SceneBuilder() - ..pushTransform(deviceTransform) - ..addPicture(ui.Offset.zero, picture) - ..pop(); - ui.window.render(sceneBuilder.build()); - - // After rendering the current frame of the animation, we ask the engine to - // schedule another frame. The engine will call beginFrame again when its time - // to produce the next frame. - ui.window.scheduleFrame(); -} - -void main() { - ui.window.onBeginFrame = beginFrame; - ui.window.scheduleFrame(); -} diff --git a/examples/spinning_square/pubspec.yaml b/examples/spinning_square/pubspec.yaml deleted file mode 100644 index a22a6ee39a974..0000000000000 --- a/examples/spinning_square/pubspec.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Copyright 2017 The Chromium 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/flow/BUILD.gn b/flow/BUILD.gn index c476b4c5bdf48..fbbf0e49db095 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -8,6 +8,8 @@ source_set("flow") { "compositor_context.h", "debug_print.cc", "debug_print.h", + "embedded_views.cc", + "embedded_views.h", "instrumentation.cc", "instrumentation.h", "layers/backdrop_filter_layer.cc", @@ -65,7 +67,6 @@ source_set("flow") { "$flutter_root/fml", "$flutter_root/synchronization", "//third_party/skia", - "//third_party/skia:gpu", ] if (is_fuchsia) { @@ -79,7 +80,7 @@ source_set("flow") { ] public_deps += [ - "//garnet/public/fidl/fuchsia.ui.scenic", + "//sdk/fidl/fuchsia.ui.scenic", "//garnet/public/lib/ui/scenic/cpp", "//topaz/public/dart-pkg/zircon", ] @@ -92,13 +93,18 @@ executable("flow_unittests") { testonly = true sources = [ + "flow_run_all_unittests.cc", + "flow_test_utils.h", + "flow_test_utils.cc", "matrix_decomposition_unittests.cc", "raster_cache_unittests.cc", + "layers/performance_overlay_layer_unittests.cc", ] deps = [ ":flow", - "$flutter_root/testing", + "$flutter_root/fml", + "//third_party/googletest:gtest", "//third_party/dart/runtime:libdart_jit", # for tracing "//third_party/skia", ] diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index e5a725ef6478c..30f1132818b2c 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -32,25 +32,25 @@ void CompositorContext::EndFrame(ScopedFrame& frame, std::unique_ptr CompositorContext::AcquireFrame( GrContext* gr_context, SkCanvas* canvas, + ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled) { - return std::make_unique(*this, // - gr_context, // - canvas, // - root_surface_transformation, // - instrumentation_enabled // - ); + return std::make_unique(*this, gr_context, canvas, view_embedder, + root_surface_transformation, + instrumentation_enabled); } CompositorContext::ScopedFrame::ScopedFrame( CompositorContext& context, GrContext* gr_context, SkCanvas* canvas, + ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled) : context_(context), gr_context_(gr_context), canvas_(canvas), + view_embedder_(view_embedder), root_surface_transformation_(root_surface_transformation), instrumentation_enabled_(instrumentation_enabled) { context_.BeginFrame(*this, instrumentation_enabled_); @@ -63,6 +63,11 @@ CompositorContext::ScopedFrame::~ScopedFrame() { bool CompositorContext::ScopedFrame::Raster(flow::LayerTree& layer_tree, bool ignore_raster_cache) { layer_tree.Preroll(*this, ignore_raster_cache); + // Clearing canvas after preroll reduces one render target switch when preroll + // paints some raster cache. + if (canvas()) { + canvas()->clear(SK_ColorTRANSPARENT); + } layer_tree.Paint(*this, ignore_raster_cache); return true; } diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 522c5359934ff..f3a885ccedd3c 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include #include +#include "flutter/flow/embedded_views.h" #include "flutter/flow/instrumentation.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/texture.h" @@ -26,6 +27,7 @@ class CompositorContext { ScopedFrame(CompositorContext& context, GrContext* gr_context, SkCanvas* canvas, + ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled); @@ -33,6 +35,8 @@ class CompositorContext { SkCanvas* canvas() { return canvas_; } + ExternalViewEmbedder* view_embedder() { return view_embedder_; } + CompositorContext& context() const { return context_; } const SkMatrix& root_surface_transformation() const { @@ -47,6 +51,7 @@ class CompositorContext { CompositorContext& context_; GrContext* gr_context_; SkCanvas* canvas_; + ExternalViewEmbedder* view_embedder_; const SkMatrix& root_surface_transformation_; const bool instrumentation_enabled_; @@ -60,6 +65,7 @@ class CompositorContext { virtual std::unique_ptr AcquireFrame( GrContext* gr_context, SkCanvas* canvas, + ExternalViewEmbedder* view_embedder, const SkMatrix& root_surface_transformation, bool instrumentation_enabled); diff --git a/flow/debug_print.cc b/flow/debug_print.cc index a811e4abea875..a41171313540d 100644 --- a/flow/debug_print.cc +++ b/flow/debug_print.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/debug_print.h b/flow/debug_print.h index 4db0748bc31cb..34621e314ad2a 100644 --- a/flow/debug_print.h +++ b/flow/debug_print.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/embedded_views.cc b/flow/embedded_views.cc new file mode 100644 index 0000000000000..8dc634ecdd62d --- /dev/null +++ b/flow/embedded_views.cc @@ -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. + +#include "flutter/flow/embedded_views.h" + +namespace flow { + +bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { + return false; +}; +} // namespace flow diff --git a/flow/embedded_views.h b/flow/embedded_views.h new file mode 100644 index 0000000000000..0fd474adb0812 --- /dev/null +++ b/flow/embedded_views.h @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter 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_EMBEDDED_VIEWS_H_ +#define FLUTTER_FLOW_EMBEDDED_VIEWS_H_ + +#include + +#include "flutter/fml/memory/ref_counted.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flow { + +class EmbeddedViewParams { + public: + SkPoint offsetPixels; + SkSize sizePoints; +}; + +// This is only used on iOS when running in a non headless mode, +// in this case ExternalViewEmbedder is a reference to the +// FlutterPlatformViewsController which is owned by FlutterViewController. +class ExternalViewEmbedder { + public: + ExternalViewEmbedder() = default; + + virtual void BeginFrame(SkISize frame_size) = 0; + + virtual void PrerollCompositeEmbeddedView(int view_id) = 0; + + virtual std::vector GetCurrentCanvases() = 0; + + // Must be called on the UI thread. + virtual SkCanvas* CompositeEmbeddedView(int view_id, + const EmbeddedViewParams& params) = 0; + + virtual bool SubmitFrame(GrContext* context); + + virtual ~ExternalViewEmbedder() = default; + + FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); +}; + +} // namespace flow + +#endif // FLUTTER_FLOW_EMBEDDED_VIEWS_H_ diff --git a/flow/export_node.cc b/flow/export_node.cc index e837f525f35c4..acf7e42da3215 100644 --- a/flow/export_node.cc +++ b/flow/export_node.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/export_node.h b/flow/export_node.h index c7a3db98aacc6..50df904bc4116 100644 --- a/flow/export_node.h +++ b/flow/export_node.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,7 +7,7 @@ #include -#include +#include #include "dart-pkg/zircon/sdk_ext/handle.h" #include "flutter/flow/scene_update_context.h" diff --git a/flow/flow_run_all_unittests.cc b/flow/flow_run_all_unittests.cc new file mode 100644 index 0000000000000..c1755e639b037 --- /dev/null +++ b/flow/flow_run_all_unittests.cc @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Google, Inc. + * + * 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. + */ + +#include "flutter/fml/command_line.h" +#include "flutter/fml/logging.h" +#include "gtest/gtest.h" + +#include "flow_test_utils.h" + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + fml::CommandLine cmd = fml::CommandLineFromArgcArgv(argc, argv); + flow::SetGoldenDir( + cmd.GetOptionValueWithDefault("golden-dir", "flutter/testing/resources")); + flow::SetFontFile(cmd.GetOptionValueWithDefault( + "font-file", + "flutter/third_party/txt/third_party/fonts/Roboto-Regular.ttf")); + return RUN_ALL_TESTS(); +} diff --git a/flow/flow_test_utils.cc b/flow/flow_test_utils.cc new file mode 100644 index 0000000000000..0bf6d4c31d4d9 --- /dev/null +++ b/flow/flow_test_utils.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2017 Google, Inc. + * + * 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. + */ + +#include + +namespace flow { + +static std::string gGoldenDir; +static std::string gFontFile; + +const std::string& GetGoldenDir() { + return gGoldenDir; +} + +void SetGoldenDir(const std::string& dir) { + gGoldenDir = dir; +} + +const std::string& GetFontFile() { + return gFontFile; +} + +void SetFontFile(const std::string& file) { + gFontFile = file; +} + +} // namespace flow diff --git a/flow/flow_test_utils.h b/flow/flow_test_utils.h new file mode 100644 index 0000000000000..58da75b3f4dde --- /dev/null +++ b/flow/flow_test_utils.h @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Google, Inc. + * + * 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. + */ + +#include + +namespace flow { + +const std::string& GetGoldenDir(); + +void SetGoldenDir(const std::string& dir); + +const std::string& GetFontFile(); + +void SetFontFile(const std::string& dir); + +} // namespace flow diff --git a/flow/instrumentation.cc b/flow/instrumentation.cc index 3b9c95a331481..ae9846c557f4c 100644 --- a/flow/instrumentation.cc +++ b/flow/instrumentation.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -63,6 +63,14 @@ fml::TimeDelta Stopwatch::MaxDelta() const { return max_delta; } +fml::TimeDelta Stopwatch::AverageDelta() const { + fml::TimeDelta sum; // default to 0 + for (size_t i = 0; i < kMaxSamples; i++) { + sum = sum + laps_[i]; + } + return sum / kMaxSamples; +} + // Initialize the SkSurface for drawing into. Draws the base background and any // timing data from before the initial Visualize() call. void Stopwatch::InitVisualizeSurface(const SkRect& rect) const { diff --git a/flow/instrumentation.h b/flow/instrumentation.h index 009786ba37cd1..a991af5b583da 100644 --- a/flow/instrumentation.h +++ b/flow/instrumentation.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,8 @@ namespace flow { +// DEPRECATED +// The frame per second FPS could be different than 60 (e.g., 120). static const double kOneFrameMS = 1e3 / 60.0; class Stopwatch { @@ -28,6 +30,8 @@ class Stopwatch { fml::TimeDelta MaxDelta() const; + fml::TimeDelta AverageDelta() const; + void InitVisualizeSurface(const SkRect& rect) const; void Visualize(SkCanvas& canvas, const SkRect& rect) const; diff --git a/flow/layers/backdrop_filter_layer.cc b/flow/layers/backdrop_filter_layer.cc index 27e48dc0a5966..f45c28b7d2381 100644 --- a/flow/layers/backdrop_filter_layer.cc +++ b/flow/layers/backdrop_filter_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/backdrop_filter_layer.h b/flow/layers/backdrop_filter_layer.h index 348afc5090b90..39bd64c33b23e 100644 --- a/flow/layers/backdrop_filter_layer.h +++ b/flow/layers/backdrop_filter_layer.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index 744aee5e87888..162a8d1a4df50 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/child_scene_layer.h b/flow/layers/child_scene_layer.h index b52ceb28b2d4f..89fd367a0e69b 100644 --- a/flow/layers/child_scene_layer.h +++ b/flow/layers/child_scene_layer.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 3c71823c48503..d119ea606d758 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,24 +6,31 @@ #if defined(OS_FUCHSIA) -#include "lib/ui/scenic/fidl_helpers.h" // nogncheck +#include "lib/ui/scenic/cpp/commands.h" #endif // defined(OS_FUCHSIA) namespace flow { ClipPathLayer::ClipPathLayer(Clip clip_behavior) - : clip_behavior_(clip_behavior) {} + : clip_behavior_(clip_behavior) { + FML_DCHECK(clip_behavior != Clip::none); +} ClipPathLayer::~ClipPathLayer() = default; void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); - - if (child_paint_bounds.intersect(clip_path_.getBounds())) { - set_paint_bounds(child_paint_bounds); + SkRect previous_cull_rect = context->cull_rect; + SkRect clip_path_bounds = clip_path_.getBounds(); + if (context->cull_rect.intersect(clip_path_bounds)) { + 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); + } } + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) @@ -50,14 +57,15 @@ void ClipPathLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipPathLayer::Paint"); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(&context.canvas, true); - context.canvas.clipPath(clip_path_, clip_behavior_ != Clip::hardEdge); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->clipPath(clip_path_, + clip_behavior_ != Clip::hardEdge); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.saveLayer(paint_bounds(), nullptr); + context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); } PaintChildren(context); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.restore(); + context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_path_layer.h b/flow/layers/clip_path_layer.h index 33b043029ba10..7b6de1f60f516 100644 --- a/flow/layers/clip_path_layer.h +++ b/flow/layers/clip_path_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 733acfcebd072..73c76bff33118 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,17 +7,23 @@ namespace flow { ClipRectLayer::ClipRectLayer(Clip clip_behavior) - : clip_behavior_(clip_behavior) {} + : clip_behavior_(clip_behavior) { + FML_DCHECK(clip_behavior != Clip::none); +} ClipRectLayer::~ClipRectLayer() = default; void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - 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); + SkRect previous_cull_rect = context->cull_rect; + if (context->cull_rect.intersect(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); + } } + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) @@ -41,14 +47,15 @@ void ClipRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRectLayer::Paint"); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(&context.canvas, true); - context.canvas.clipRect(paint_bounds(), clip_behavior_ != Clip::hardEdge); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->clipRect(clip_rect_, + clip_behavior_ != Clip::hardEdge); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.saveLayer(paint_bounds(), nullptr); + context.internal_nodes_canvas->saveLayer(clip_rect_, nullptr); } PaintChildren(context); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.restore(); + context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_rect_layer.h b/flow/layers/clip_rect_layer.h index 576e6a5d029a7..76e10e49bb7ea 100644 --- a/flow/layers/clip_rect_layer.h +++ b/flow/layers/clip_rect_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 046a0d45404ef..72092110cf789 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,17 +7,24 @@ namespace flow { ClipRRectLayer::ClipRRectLayer(Clip clip_behavior) - : clip_behavior_(clip_behavior) {} + : clip_behavior_(clip_behavior) { + FML_DCHECK(clip_behavior != Clip::none); +} ClipRRectLayer::~ClipRRectLayer() = default; void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); + SkRect previous_cull_rect = context->cull_rect; + SkRect clip_rrect_bounds = clip_rrect_.getBounds(); + if (context->cull_rect.intersect(clip_rrect_bounds)) { + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_rrect_.getBounds())) { - set_paint_bounds(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(OS_FUCHSIA) @@ -25,7 +32,7 @@ void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { void ClipRRectLayer::UpdateScene(SceneUpdateContext& context) { FML_DCHECK(needs_system_composite()); - // TODO(MZ-137): Need to be able to express the radii as vectors. + // TODO(SCN-137): Need to be able to express the radii as vectors. scenic::RoundedRectangle shape( context.session(), // session clip_rrect_.width(), // width @@ -48,14 +55,15 @@ void ClipRRectLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ClipRRectLayer::Paint"); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(&context.canvas, true); - context.canvas.clipRRect(clip_rrect_, clip_behavior_ != Clip::hardEdge); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->clipRRect(clip_rrect_, + clip_behavior_ != Clip::hardEdge); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.saveLayer(paint_bounds(), nullptr); + context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); } PaintChildren(context); if (clip_behavior_ == Clip::antiAliasWithSaveLayer) { - context.canvas.restore(); + context.internal_nodes_canvas->restore(); } } diff --git a/flow/layers/clip_rrect_layer.h b/flow/layers/clip_rrect_layer.h index 7e8f0934a337f..7b3ac85beb65b 100644 --- a/flow/layers/clip_rrect_layer.h +++ b/flow/layers/clip_rrect_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/color_filter_layer.cc b/flow/layers/color_filter_layer.cc index 287507d179f0f..3b140ad7ceb5e 100644 --- a/flow/layers/color_filter_layer.cc +++ b/flow/layers/color_filter_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/color_filter_layer.h b/flow/layers/color_filter_layer.h index e129bad66f000..358788fdd4486 100644 --- a/flow/layers/color_filter_layer.h +++ b/flow/layers/color_filter_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 01ced5fd6a8aa..f6ca64005fe6b 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/container_layer.h b/flow/layers/container_layer.h index aa73bb9d59c4a..780fd64b46a94 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/layer.cc b/flow/layers/layer.cc index c1d91872127ab..c3653ed52d0cf 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,13 +26,13 @@ Layer::AutoSaveLayer::AutoSaveLayer(const PaintContext& paint_context, const SkRect& bounds, const SkPaint* paint) : paint_context_(paint_context), bounds_(bounds) { - paint_context_.canvas.saveLayer(bounds_, paint); + paint_context_.internal_nodes_canvas->saveLayer(bounds_, paint); } Layer::AutoSaveLayer::AutoSaveLayer(const PaintContext& paint_context, const SkCanvas::SaveLayerRec& layer_rec) : paint_context_(paint_context), bounds_(*layer_rec.fBounds) { - paint_context_.canvas.saveLayer(layer_rec); + paint_context_.internal_nodes_canvas->saveLayer(layer_rec); } Layer::AutoSaveLayer Layer::AutoSaveLayer::Create( @@ -50,9 +50,9 @@ Layer::AutoSaveLayer Layer::AutoSaveLayer::Create( Layer::AutoSaveLayer::~AutoSaveLayer() { if (paint_context_.checkerboard_offscreen_layers) { - DrawCheckerboard(&paint_context_.canvas, bounds_); + DrawCheckerboard(paint_context_.internal_nodes_canvas, bounds_); } - paint_context_.canvas.restore(); + paint_context_.internal_nodes_canvas->restore(); } } // namespace flow diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 50fd3b63b277d..e846d353e52e9 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ #include #include +#include "flutter/flow/embedded_views.h" #include "flutter/flow/instrumentation.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/texture.h" @@ -24,6 +25,7 @@ #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" #if defined(OS_FUCHSIA) @@ -35,6 +37,8 @@ namespace flow { +static constexpr SkRect kGiantRect = SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + // This should be an exact copy of the Clip enum in painting.dart. enum Clip { none, hardEdge, antiAlias, antiAliasWithSaveLayer }; @@ -43,8 +47,9 @@ class ContainerLayer; struct PrerollContext { RasterCache* raster_cache; GrContext* gr_context; + ExternalViewEmbedder* view_embedder; SkColorSpace* dst_color_space; - SkRect child_paint_bounds; + SkRect cull_rect; // The following allows us to paint in the end of subtree preroll const Stopwatch& frame_time; @@ -63,7 +68,19 @@ class Layer { virtual void Preroll(PrerollContext* context, const SkMatrix& matrix); struct PaintContext { - SkCanvas& canvas; + // When splitting the scene into multiple canvases (e.g when embedding + // a platform view on iOS) during the paint traversal we apply the non leaf + // flow layers to all canvases, and leaf layers just to the "current" + // canvas. Applying the non leaf layers to all canvases ensures that when + // we switch a canvas (when painting a PlatformViewLayer) the next canvas + // has the exact same state as the current canvas. + // The internal_nodes_canvas is a SkNWayCanvas which is used by non leaf + // and applies the operations to all canvases. + // The leaf_nodes_canvas is the "current" canvas and is used by leaf + // layers. + SkCanvas* internal_nodes_canvas; + SkCanvas* leaf_nodes_canvas; + ExternalViewEmbedder* view_embedder; const Stopwatch& frame_time; const Stopwatch& engine_time; TextureRegistry& texture_registry; diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index de28918193576..fbe3c2178e75f 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,7 @@ #include "flutter/flow/layers/layer.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" namespace flow { @@ -28,8 +29,9 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, PrerollContext context = { ignore_raster_cache ? nullptr : &frame.context().raster_cache(), frame.gr_context(), + frame.view_embedder(), color_space, - SkRect::MakeEmpty(), + kGiantRect, frame.context().frame_time(), frame.context().engine_time(), frame.context().texture_registry(), @@ -57,7 +59,7 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, root_layer_->UpdateScene(context); } if (root_layer_->needs_painting()) { - frame.AddPaintedLayer(root_layer_.get()); + frame.AddPaintLayer(root_layer_.get()); } container.AddChild(transform.entity_node()); } @@ -66,8 +68,20 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, void LayerTree::Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) const { TRACE_EVENT0("flutter", "LayerTree::Paint"); + SkISize canvas_size = frame.canvas()->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(frame.canvas()); + if (frame.view_embedder() != nullptr) { + auto overlay_canvases = frame.view_embedder()->GetCurrentCanvases(); + for (size_t i = 0; i < overlay_canvases.size(); i++) { + internal_nodes_canvas.addCanvas(overlay_canvases[i]); + } + } + Layer::PaintContext context = { - *frame.canvas(), + (SkCanvas*)&internal_nodes_canvas, + frame.canvas(), + frame.view_embedder(), frame.context().frame_time(), frame.context().engine_time(), frame.context().texture_registry(), @@ -82,7 +96,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { TRACE_EVENT0("flutter", "LayerTree::Flatten"); SkPictureRecorder recorder; - auto canvas = recorder.beginRecording(bounds); + auto* canvas = recorder.beginRecording(bounds); if (!canvas) { return nullptr; @@ -97,16 +111,23 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { PrerollContext preroll_context{ nullptr, // raster_cache (don't consult the cache) nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder nullptr, // SkColorSpace* dst_color_space - SkRect::MakeEmpty(), // SkRect child_paint_bounds + kGiantRect, // SkRect cull_rect unused_stopwatch, // frame time (dont care) unused_stopwatch, // engine time (dont care) unused_texture_registry, // texture registry (not supported) false, // checkerboard_offscreen_layers }; + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); + Layer::PaintContext paint_context = { - *canvas, // canvas + (SkCanvas*)&internal_nodes_canvas, + canvas, // canvas + nullptr, unused_stopwatch, // frame time (dont care) unused_stopwatch, // engine time (dont care) unused_texture_registry, // texture registry (not supported) diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index e8591b49661c7..f3bfe3a322594 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 202b53bdb9983..c774ab9fb3d94 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,10 +11,14 @@ OpacityLayer::OpacityLayer() = default; OpacityLayer::~OpacityLayer() = default; void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - ContainerLayer::Preroll(context, matrix); - if (context->raster_cache && layers().size() == 1) { + SkMatrix child_matrix = matrix; + child_matrix.postTranslate(offset_.fX, offset_.fY); + ContainerLayer::Preroll(context, child_matrix); + set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); + if (context->raster_cache && layers().size() == 1 && + SkRect::Intersects(context->cull_rect, paint_bounds())) { Layer* child = layers()[0].get(); - SkMatrix ctm = matrix; + SkMatrix ctm = child_matrix; #ifndef SUPPORT_FRACTIONAL_TRANSLATION ctm = RasterCache::GetIntegralTransCTM(ctm); #endif @@ -29,25 +33,45 @@ void OpacityLayer::Paint(PaintContext& context) const { SkPaint paint; paint.setAlpha(alpha_); - SkAutoCanvasRestore save(&context.canvas, true); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); #ifndef SUPPORT_FRACTIONAL_TRANSLATION - context.canvas.setMatrix( - RasterCache::GetIntegralTransCTM(context.canvas.getTotalMatrix())); + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); #endif - if (layers().size() == 1 && context.raster_cache) { - const SkMatrix& ctm = context.canvas.getTotalMatrix(); + // Embedded platform views are changing the canvas in the middle of the paint + // traversal. To make sure we paint on the right canvas, when the embedded + // platform views preview is enabled (context.view_embedded is not null) we + // don't use the cache. + if (context.view_embedder == nullptr && layers().size() == 1 && + context.raster_cache) { + const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); RasterCacheResult child_cache = context.raster_cache->Get(layers()[0].get(), ctm); if (child_cache.is_valid()) { - child_cache.draw(context.canvas, &paint); + child_cache.draw(*context.leaf_nodes_canvas, &paint); return; } } + // Skia may clip the content with saveLayerBounds (although it's not a + // guaranteed clip). So we have to provide a big enough saveLayerBounds. To do + // so, we first remove the offset from paint bounds since it's already in the + // matrix. Then we round out the bounds because of our + // RasterCache::GetIntegralTransCTM optimization. + // + // Note that the following lines are only accessible when the raster cache is + // not available (e.g., when we're using the software backend in golden + // tests). + SkRect saveLayerBounds; + paint_bounds() + .makeOffset(-offset_.fX, -offset_.fY) + .roundOut(&saveLayerBounds); + Layer::AutoSaveLayer save_layer = - Layer::AutoSaveLayer::Create(context, paint_bounds(), &paint); + Layer::AutoSaveLayer::Create(context, saveLayerBounds, &paint); PaintChildren(context); } diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 4733d13645233..5662f85a64980 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,6 +15,7 @@ class OpacityLayer : public ContainerLayer { ~OpacityLayer() override; void set_alpha(int alpha) { alpha_ = alpha; } + void set_offset(const SkPoint& offset) { offset_ = offset; } void Preroll(PrerollContext* context, const SkMatrix& matrix) override; @@ -25,6 +26,7 @@ class OpacityLayer : public ContainerLayer { private: int alpha_; + SkPoint offset_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index 7d871af33b94f..e7f40058c3bfb 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,7 @@ #include #include "flutter/flow/layers/performance_overlay_layer.h" +#include "third_party/skia/include/core/SkFont.h" namespace flow { namespace { @@ -14,12 +15,18 @@ namespace { void DrawStatisticsText(SkCanvas& canvas, const std::string& string, int x, - int y) { + int y, + const std::string& font_path) { + SkFont font; + if (font_path != "") { + font = SkFont(SkTypeface::MakeFromFile(font_path.c_str())); + } + font.setSize(15); + font.setLinearMetrics(false); SkPaint paint; - paint.setTextSize(15); - paint.setLinearText(false); paint.setColor(SK_ColorGRAY); - canvas.drawText(string.c_str(), string.size(), x, y, paint); + canvas.drawSimpleText(string.c_str(), string.size(), kUTF8_SkTextEncoding, x, + y, font, paint); } void VisualizeStopWatch(SkCanvas& canvas, @@ -30,7 +37,8 @@ void VisualizeStopWatch(SkCanvas& canvas, SkScalar height, bool show_graph, bool show_labels, - const std::string& label_prefix) { + const std::string& label_prefix, + const std::string& font_path) { const int label_x = 8; // distance from x const int label_y = -10; // distance from y+height @@ -40,27 +48,28 @@ void VisualizeStopWatch(SkCanvas& canvas, } if (show_labels) { - double ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); - double fps; - if (ms_per_frame < kOneFrameMS) { - fps = 1e3 / kOneFrameMS; - } else { - fps = 1e3 / ms_per_frame; - } - + double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF(); + double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF(); std::stringstream stream; stream.setf(std::ios::fixed | std::ios::showpoint); stream << std::setprecision(1); - stream << label_prefix << " " << fps << " fps " << ms_per_frame - << "ms/frame"; - DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y); + stream << label_prefix << " " + << "max " << max_ms_per_frame << " ms/frame, " + << "avg " << average_ms_per_frame << " ms/frame"; + DrawStatisticsText(canvas, stream.str(), x + label_x, y + height + label_y, + font_path); } } } // namespace -PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options) - : options_(options) {} +PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options, + const char* font_path) + : options_(options) { + if (font_path != nullptr) { + font_path_ = font_path; + } +} void PerformanceOverlayLayer::Paint(PaintContext& context) const { const int padding = 8; @@ -73,16 +82,17 @@ void PerformanceOverlayLayer::Paint(PaintContext& context) const { SkScalar y = paint_bounds().y() + padding; SkScalar width = paint_bounds().width() - (padding * 2); SkScalar height = paint_bounds().height() / 2; - SkAutoCanvasRestore save(&context.canvas, true); + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); - VisualizeStopWatch(context.canvas, context.frame_time, x, y, width, - height - padding, - options_ & kVisualizeRasterizerStatistics, - options_ & kDisplayRasterizerStatistics, "GPU"); + VisualizeStopWatch( + *context.leaf_nodes_canvas, context.frame_time, x, y, width, + height - padding, options_ & kVisualizeRasterizerStatistics, + options_ & kDisplayRasterizerStatistics, "GPU", font_path_); - VisualizeStopWatch(context.canvas, context.engine_time, x, y + height, width, - height - padding, options_ & kVisualizeEngineStatistics, - options_ & kDisplayEngineStatistics, "UI"); + VisualizeStopWatch(*context.leaf_nodes_canvas, context.engine_time, x, + y + height, width, height - padding, + options_ & kVisualizeEngineStatistics, + options_ & kDisplayEngineStatistics, "UI", font_path_); } } // namespace flow diff --git a/flow/layers/performance_overlay_layer.h b/flow/layers/performance_overlay_layer.h index 3296a308bddbd..a47b836c49f3a 100644 --- a/flow/layers/performance_overlay_layer.h +++ b/flow/layers/performance_overlay_layer.h @@ -1,10 +1,12 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_PERFORMANCE_OVERLAY_LAYER_H_ #define FLUTTER_FLOW_LAYERS_PERFORMANCE_OVERLAY_LAYER_H_ +#include + #include "flutter/flow/layers/layer.h" #include "flutter/fml/macros.h" @@ -17,12 +19,14 @@ const int kVisualizeEngineStatistics = 1 << 3; class PerformanceOverlayLayer : public Layer { public: - explicit PerformanceOverlayLayer(uint64_t options); + explicit PerformanceOverlayLayer(uint64_t options, + const char* font_path = nullptr); void Paint(PaintContext& context) const override; private: int options_; + std::string font_path_; FML_DISALLOW_COPY_AND_ASSIGN(PerformanceOverlayLayer); }; diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc new file mode 100644 index 0000000000000..cee659f925bbd --- /dev/null +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter 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/flow_test_utils.h" +#include "flutter/flow/layers/performance_overlay_layer.h" +#include "flutter/flow/raster_cache.h" + +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/utils/SkBase64.h" + +#include "gtest/gtest.h" + +// To get the size of kMockedTimes in compile time. +template +constexpr int size(const T (&array)[N]) noexcept { + return N; +} + +constexpr int kMockedTimes[] = {17, 1, 4, 24, 4, 25, 30, 4, 13, 34, + 14, 0, 18, 9, 32, 36, 26, 23, 5, 8, + 32, 18, 29, 16, 29, 18, 0, 36, 33, 10}; + +// Relative to the flutter/src/engine/flutter directory +const char* kGoldenFileName = "performance_overlay_gold.png"; + +// Relative to the flutter/src/engine/flutter directory +const char* kNewGoldenFileName = "performance_overlay_gold_new.png"; + +TEST(PerformanceOverlayLayer, Gold) { + const std::string& golden_dir = flow::GetGoldenDir(); + // This unit test should only be run on Linux (not even on Mac since it's a + // golden test). Hence we don't have to worry about the "/" vs. "\". + std::string golden_file_path = golden_dir + "/" + kGoldenFileName; + std::string new_golden_file_path = golden_dir + "/" + kNewGoldenFileName; + + flow::Stopwatch mock_stopwatch; + for (int i = 0; i < size(kMockedTimes); ++i) { + mock_stopwatch.SetLapTime( + fml::TimeDelta::FromMilliseconds(kMockedTimes[i])); + } + + const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); + sk_sp surface = SkSurface::MakeRaster(image_info); + + ASSERT_TRUE(surface != nullptr); + + flow::TextureRegistry unused_texture_registry; + + flow::Layer::PaintContext paintContext = { + nullptr, surface->getCanvas(), nullptr, mock_stopwatch, + mock_stopwatch, unused_texture_registry, nullptr, false}; + + // Specify font file to ensure the same font across different operation + // systems. + flow::PerformanceOverlayLayer layer(flow::kDisplayRasterizerStatistics | + flow::kVisualizeRasterizerStatistics | + flow::kDisplayEngineStatistics | + flow::kVisualizeEngineStatistics, + flow::GetFontFile().c_str()); + layer.set_paint_bounds(SkRect::MakeWH(1000, 400)); + surface->getCanvas()->clear(SK_ColorTRANSPARENT); + layer.Paint(paintContext); + + sk_sp snapshot = surface->makeImageSnapshot(); + sk_sp snapshot_data = snapshot->encodeToData(); + + sk_sp golden_data = + SkData::MakeFromFileName(golden_file_path.c_str()); + EXPECT_TRUE(golden_data != nullptr) + << "Golden file not found: " << golden_file_path << ".\n" + << "Please either set --golden-dir, or make sure that the unit test is " + << "run from the right directory (e.g., flutter/engine/src)."; + + const bool golden_data_matches = golden_data->equals(snapshot_data.get()); + if (!golden_data_matches) { + SkFILEWStream wstream(new_golden_file_path.c_str()); + wstream.write(snapshot_data->data(), snapshot_data->size()); + wstream.flush(); + + size_t b64_size = + SkBase64::Encode(snapshot_data->data(), snapshot_data->size(), nullptr); + sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); + char* b64_char = static_cast(b64_data->writable_data()); + SkBase64::Encode(snapshot_data->data(), snapshot_data->size(), b64_char); + b64_char[b64_size] = 0; // make it null terminated for printing + + EXPECT_TRUE(golden_data_matches) + << "Golden file mismatch. Please check " + << "the difference between " << kGoldenFileName << " and " + << kNewGoldenFileName << ", and replace the former " + << "with the latter if the difference looks good.\n\n" + << "See also the base64 encoded " << kNewGoldenFileName << ":\n" + << b64_char; + } +} diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index e10caaa56feed..93bd2664a916c 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +9,9 @@ namespace flow { +const SkScalar kLightHeight = 600; +const SkScalar kLightRadius = 800; + PhysicalShapeLayer::PhysicalShapeLayer(Clip clip_behavior) : isRect_(false), clip_behavior_(clip_behavior) {} @@ -51,11 +54,46 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, set_needs_system_composite(true); #else // Add some margin to the paint bounds to leave space for the shadow. - // The margin is hardcoded to an arbitrary maximum for now because Skia - // doesn't provide a way to calculate it. We fill this whole region - // and clip children to it so we don't need to join the child paint bounds. + // 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 SkRect bounds(path_.getBounds()); - bounds.outset(20.0, 20.0); + // tangent for x + double tx = (kLightRadius * device_pixel_ratio_ + bounds.width() * 0.5) / + kLightHeight; + // tangent for y + double ty = (kLightRadius * device_pixel_ratio_ + bounds.height() * 0.5) / + kLightHeight; + bounds.outset(elevation_ * tx, elevation_ * ty); set_paint_bounds(bounds); #endif // defined(OS_FUCHSIA) } @@ -66,10 +104,24 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { FML_DCHECK(needs_system_composite()); - SceneUpdateContext::Frame frame(context, frameRRect_, color_, elevation_); + // Retained rendering: speedup by reusing a retained entity node if possible. + // When an entity node is reused, no paint layer is added to the frame so we + // won't call PhysicalShapeLayer::Paint. + LayerRasterCacheKey key(this, context.Matrix()); + if (context.HasRetainedNode(key)) { + const scenic::EntityNode& retained_node = context.GetRetainedNode(key); + FML_DCHECK(context.top_entity()); + FML_DCHECK(retained_node.session() == context.session()); + context.top_entity()->entity_node().AddChild(retained_node); + return; + } + + // If we can't find an existing retained surface, create one. + SceneUpdateContext::Frame frame(context, frameRRect_, color_, elevation_, + this); for (auto& layer : layers()) { if (layer->needs_painting()) { - frame.AddPaintedLayer(layer.get()); + frame.AddPaintLayer(layer.get()); } } @@ -83,28 +135,29 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { FML_DCHECK(needs_painting()); if (elevation_ != 0) { - DrawShadow(&context.canvas, path_, shadow_color_, elevation_, + DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_, SkColorGetA(color_) != 0xff, device_pixel_ratio_); } // Call drawPath without clip if possible for better performance. SkPaint paint; paint.setColor(color_); + paint.setAntiAlias(true); if (clip_behavior_ != Clip::antiAliasWithSaveLayer) { - context.canvas.drawPath(path_, paint); + context.leaf_nodes_canvas->drawPath(path_, paint); } - int saveCount = context.canvas.save(); + int saveCount = context.internal_nodes_canvas->save(); switch (clip_behavior_) { case Clip::hardEdge: - context.canvas.clipPath(path_, false); + context.internal_nodes_canvas->clipPath(path_, false); break; case Clip::antiAlias: - context.canvas.clipPath(path_, true); + context.internal_nodes_canvas->clipPath(path_, true); break; case Clip::antiAliasWithSaveLayer: - context.canvas.clipPath(path_, true); - context.canvas.saveLayer(paint_bounds(), nullptr); + context.internal_nodes_canvas->clipPath(path_, true); + context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); break; case Clip::none: break; @@ -115,12 +168,12 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { // (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. - context.canvas.drawPaint(paint); + context.leaf_nodes_canvas->drawPaint(paint); } PaintChildren(context); - context.canvas.restoreToCount(saveCount); + context.internal_nodes_canvas->restoreToCount(saveCount); } void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, @@ -131,8 +184,6 @@ void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas, SkScalar dpr) { const SkScalar kAmbientAlpha = 0.039f; const SkScalar kSpotAlpha = 0.25f; - const SkScalar kLightHeight = 600; - const SkScalar kLightRadius = 800; SkShadowFlags flags = transparentOccluder ? SkShadowFlags::kTransparentOccluder_ShadowFlag diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index 344656c662b24..45ba68e86d9b3 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 9dcef880b7313..cef3321df4d19 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,7 +15,7 @@ PictureLayer::~PictureLayer() = default; void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkPicture* sk_picture = picture(); - if (auto cache = context->raster_cache) { + if (auto* cache = context->raster_cache) { SkMatrix ctm = matrix; ctm.postTranslate(offset_.x(), offset_.y()); #ifndef SUPPORT_FRACTIONAL_TRANSLATION @@ -34,22 +34,22 @@ void PictureLayer::Paint(PaintContext& context) const { FML_DCHECK(picture_.get()); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(&context.canvas, true); - context.canvas.translate(offset_.x(), offset_.y()); + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); + context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); #ifndef SUPPORT_FRACTIONAL_TRANSLATION - context.canvas.setMatrix( - RasterCache::GetIntegralTransCTM(context.canvas.getTotalMatrix())); + context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); #endif if (context.raster_cache) { - const SkMatrix& ctm = context.canvas.getTotalMatrix(); + const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); RasterCacheResult result = context.raster_cache->Get(*picture(), ctm); if (result.is_valid()) { - result.draw(context.canvas); + result.draw(*context.leaf_nodes_canvas); return; } } - context.canvas.drawPicture(picture()); + context.leaf_nodes_canvas->drawPicture(picture()); } } // namespace flow diff --git a/flow/layers/picture_layer.h b/flow/layers/picture_layer.h index 8b6f74c313839..fc50e8f3bf6dd 100644 --- a/flow/layers/picture_layer.h +++ b/flow/layers/picture_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 0a6a17244e0df..d10bd2ac4f309 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,8 +14,29 @@ void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); + + if (context->view_embedder == nullptr) { + FML_LOG(ERROR) << "Trying to embed a platform view but the PrerollContext " + "does not support embedding"; + return; + } + context->view_embedder->PrerollCompositeEmbeddedView(view_id_); } -void PlatformViewLayer::Paint(PaintContext& context) const {} +void PlatformViewLayer::Paint(PaintContext& context) const { + if (context.view_embedder == nullptr) { + FML_LOG(ERROR) << "Trying to embed a platform view but the PaintContext " + "does not support embedding"; + return; + } + EmbeddedViewParams params; + SkMatrix transform = context.leaf_nodes_canvas->getTotalMatrix(); + params.offsetPixels = + SkPoint::Make(transform.getTranslateX(), transform.getTranslateY()); + params.sizePoints = size_; + SkCanvas* canvas = + context.view_embedder->CompositeEmbeddedView(view_id_, params); + context.leaf_nodes_canvas = canvas; +} } // namespace flow diff --git a/flow/layers/platform_view_layer.h b/flow/layers/platform_view_layer.h index 13b7f5d0f1dae..f3e0c48449caf 100644 --- a/flow/layers/platform_view_layer.h +++ b/flow/layers/platform_view_layer.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,11 +6,8 @@ #define FLUTTER_FLOW_LAYERS_PLATFORM_VIEW_LAYER_H_ #include "flutter/flow/layers/layer.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrContext.h" -#include "third_party/skia/include/gpu/GrTexture.h" -#include "third_party/skia/include/gpu/GrTypes.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkSize.h" namespace flow { diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index 6cb73bbf64855..5c13f56e8b9a1 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,8 +21,8 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { SkPaint paint; paint.setBlendMode(blend_mode_); paint.setShader(shader_); - context.canvas.translate(mask_rect_.left(), mask_rect_.top()); - context.canvas.drawRect( + context.leaf_nodes_canvas->translate(mask_rect_.left(), mask_rect_.top()); + context.leaf_nodes_canvas->drawRect( SkRect::MakeWH(mask_rect_.width(), mask_rect_.height()), paint); } diff --git a/flow/layers/shader_mask_layer.h b/flow/layers/shader_mask_layer.h index ed059722fea55..2b4b3f052ae7b 100644 --- a/flow/layers/shader_mask_layer.h +++ b/flow/layers/shader_mask_layer.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index 52ebdc37dbcab..df845caf80215 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -23,7 +23,7 @@ void TextureLayer::Paint(PaintContext& context) const { if (!texture) { return; } - texture->Paint(context.canvas, paint_bounds(), freeze_); + texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_); } } // namespace flow diff --git a/flow/layers/texture_layer.h b/flow/layers/texture_layer.h index bf2a17fd27ac7..f761b5d02d053 100644 --- a/flow/layers/texture_layer.h +++ b/flow/layers/texture_layer.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,11 +6,8 @@ #define FLUTTER_FLOW_LAYERS_TEXTURE_LAYER_H_ #include "flutter/flow/layers/layer.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrContext.h" -#include "third_party/skia/include/gpu/GrTexture.h" -#include "third_party/skia/include/gpu/GrTypes.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkSize.h" namespace flow { diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index a70cc299dd9a4..8abb550b95935 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,11 +14,21 @@ void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); + SkRect previous_cull_rect = context->cull_rect; + SkMatrix inverse_transform_; + if (transform_.invert(&inverse_transform_)) { + inverse_transform_.mapRect(&context->cull_rect); + } else { + context->cull_rect = kGiantRect; + } + SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, child_matrix, &child_paint_bounds); transform_.mapRect(&child_paint_bounds); set_paint_bounds(child_paint_bounds); + + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) @@ -36,8 +46,8 @@ void TransformLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "TransformLayer::Paint"); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(&context.canvas, true); - context.canvas.concat(transform_); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->concat(transform_); PaintChildren(context); } diff --git a/flow/layers/transform_layer.h b/flow/layers/transform_layer.h index f8209302aa170..b1fb830bf2b9d 100644 --- a/flow/layers/transform_layer.h +++ b/flow/layers/transform_layer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/matrix_decomposition.cc b/flow/matrix_decomposition.cc index 0cc035cbba216..c710908dc651c 100644 --- a/flow/matrix_decomposition.cc +++ b/flow/matrix_decomposition.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/matrix_decomposition.h b/flow/matrix_decomposition.h index d65d12a77e674..2cf3c0faa4298 100644 --- a/flow/matrix_decomposition.h +++ b/flow/matrix_decomposition.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc index 3b9f8ed815ff6..6c2ba6737372d 100644 --- a/flow/matrix_decomposition_unittests.cc +++ b/flow/matrix_decomposition_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/paint_utils.cc b/flow/paint_utils.cc index e2ea306c913f5..22b0735b8939f 100644 --- a/flow/paint_utils.cc +++ b/flow/paint_utils.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/paint_utils.h b/flow/paint_utils.h index 63a4b162b1c1c..352dee124fc91 100644 --- a/flow/paint_utils.h +++ b/flow/paint_utils.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/raster_cache.cc b/flow/raster_cache.cc index a9246227fa107..9f10e47de250c 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,13 +11,22 @@ #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkSurface.h" namespace flow { +RasterCacheResult::RasterCacheResult() {} + +RasterCacheResult::RasterCacheResult(const RasterCacheResult& other) = default; + +RasterCacheResult::~RasterCacheResult() = default; + +RasterCacheResult::RasterCacheResult(sk_sp image, + const SkRect& logical_rect) + : image_(std::move(image)), logical_rect_(logical_rect) {} + void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { SkAutoCanvasRestore auto_restore(&canvas, true); SkIRect bounds = @@ -27,8 +36,11 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); } -RasterCache::RasterCache(size_t threshold) - : threshold_(threshold), checkerboard_images_(false), weak_factory_(this) {} +RasterCache::RasterCache(size_t threshold, size_t picture_cache_limit_per_frame) + : threshold_(threshold), + picture_cache_limit_per_frame_(picture_cache_limit_per_frame), + checkerboard_images_(false), + weak_factory_(this) {} RasterCache::~RasterCache() = default; @@ -75,7 +87,7 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, // 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 picture->approximateOpCount() > 10; + return picture->approximateOpCount() > 5; } static RasterCacheResult Rasterize( @@ -85,10 +97,11 @@ static RasterCacheResult Rasterize( bool checkerboard, const SkRect& logical_rect, std::function draw_function) { + TRACE_EVENT0("flutter", "RasterCachePopulate"); SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm); - const SkImageInfo image_info = - SkImageInfo::MakeN32Premul(cache_rect.width(), cache_rect.height()); + const SkImageInfo image_info = SkImageInfo::MakeN32Premul( + cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space)); sk_sp surface = context @@ -100,15 +113,6 @@ static RasterCacheResult Rasterize( } SkCanvas* canvas = surface->getCanvas(); - std::unique_ptr xformCanvas; - if (dst_color_space) { - xformCanvas = SkCreateColorSpaceXformCanvas(surface->getCanvas(), - sk_ref_sp(dst_color_space)); - if (xformCanvas) { - canvas = xformCanvas.get(); - } - } - canvas->clear(SK_ColorTRANSPARENT); canvas->translate(-cache_rect.left(), -cache_rect.top()); canvas->concat(ctm); @@ -126,8 +130,6 @@ RasterCacheResult RasterizePicture(SkPicture* picture, const SkMatrix& ctm, SkColorSpace* dst_color_space, bool checkerboard) { - TRACE_EVENT0("flutter", "RasterCachePopulate"); - return Rasterize(context, ctm, dst_color_space, checkerboard, picture->cullRect(), [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); @@ -156,14 +158,22 @@ void RasterCache::Prepare(PrerollContext* context, entry.image = Rasterize(context->gr_context, ctm, context->dst_color_space, checkerboard_images_, layer->paint_bounds(), [layer, context](SkCanvas* canvas) { + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas( + canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); Layer::PaintContext paintContext = { - *canvas, + (SkCanvas*)&internal_nodes_canvas, + canvas, + nullptr, context->frame_time, context->engine_time, context->texture_registry, context->raster_cache, context->checkerboard_offscreen_layers}; - layer->Paint(paintContext); + if (layer->needs_painting()) { + layer->Paint(paintContext); + } }); } } @@ -174,6 +184,9 @@ bool RasterCache::Prepare(GrContext* context, SkColorSpace* dst_color_space, bool is_complex, bool will_change) { + if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) { + return false; + } if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) { // We only deal with pictures that are worthy of rasterization. return false; @@ -203,6 +216,7 @@ bool RasterCache::Prepare(GrContext* context, entry.image = RasterizePicture(picture, context, transformation_matrix, dst_color_space, checkerboard_images_); } + picture_cached_this_frame_++; return true; } @@ -224,10 +238,13 @@ void RasterCache::SweepAfterFrame() { using LayerCache = LayerRasterCacheKey::Map; SweepOneCacheAfterFrame(picture_cache_); SweepOneCacheAfterFrame(layer_cache_); + picture_cached_this_frame_ = 0; + TraceStatsToTimeline(); } void RasterCache::Clear() { picture_cache_.clear(); + layer_cache_.clear(); } void RasterCache::SetCheckboardCacheImages(bool checkerboard) { @@ -242,4 +259,35 @@ void RasterCache::SetCheckboardCacheImages(bool checkerboard) { Clear(); } +void RasterCache::TraceStatsToTimeline() const { +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + + size_t layer_cache_count = 0; + size_t layer_cache_bytes = 0; + size_t picture_cache_count = 0; + size_t picture_cache_bytes = 0; + + for (const auto& item : layer_cache_) { + const auto dimensions = item.second.image.image_dimensions(); + layer_cache_count++; + layer_cache_bytes += dimensions.width() * dimensions.height() * 4; + } + + for (const auto& item : picture_cache_) { + const auto dimensions = item.second.image.image_dimensions(); + picture_cache_count++; + picture_cache_bytes += dimensions.width() * dimensions.height() * 4; + } + + FML_TRACE_COUNTER("flutter", "RasterCache", + reinterpret_cast(this), // + "LayerCount", layer_cache_count, // + "LayerMBytes", layer_cache_bytes * 1e-6, // + "PictureCount", picture_cache_count, // + "PictureMBytes", picture_cache_bytes * 1e-6 // + ); + +#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE +} + } // namespace flow diff --git a/flow/raster_cache.h b/flow/raster_cache.h index b9535a8e4352f..f0382c9c92872 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,10 +19,13 @@ namespace flow { class RasterCacheResult { public: - RasterCacheResult() {} + RasterCacheResult(); - RasterCacheResult(sk_sp image, const SkRect& logical_rect) - : image_(std::move(image)), logical_rect_(logical_rect) {} + RasterCacheResult(const RasterCacheResult& other); + + ~RasterCacheResult(); + + RasterCacheResult(sk_sp image, const SkRect& logical_rect); operator bool() const { return static_cast(image_); } @@ -30,6 +33,10 @@ class RasterCacheResult { void draw(SkCanvas& canvas, const SkPaint* paint = nullptr) const; + SkISize image_dimensions() const { + return image_ ? image_->dimensions() : SkISize::Make(0, 0); + }; + private: sk_sp image_; SkRect logical_rect_; @@ -39,7 +46,15 @@ struct PrerollContext; class RasterCache { public: - explicit RasterCache(size_t threshold = 3); + // The default max number of picture raster caches to be generated per frame. + // Generating too many caches in one frame may cause jank on that frame. This + // limit allows us to throttle the cache and distribute the work across + // multiple frames. + static constexpr int kDefaultPictureCacheLimitPerFrame = 3; + + explicit RasterCache( + size_t threshold = 3, + size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame); ~RasterCache(); @@ -64,6 +79,8 @@ class RasterCache { // 1. The picture is not worth rasterizing // 2. The matrix is singular // 3. The picture is accessed too few times + // 4. There are too many pictures to be cached in the current frame. + // (See also kDefaultPictureCacheLimitPerFrame.) bool Prepare(GrContext* context, SkPicture* picture, const SkMatrix& transformation_matrix, @@ -74,6 +91,7 @@ class RasterCache { void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); RasterCacheResult Get(const SkPicture& picture, const SkMatrix& ctm) const; + RasterCacheResult Get(Layer* layer, const SkMatrix& ctm) const; void SweepAfterFrame(); @@ -107,11 +125,15 @@ class RasterCache { } const size_t threshold_; + const size_t picture_cache_limit_per_frame_; + size_t picture_cached_this_frame_ = 0; PictureRasterCacheKey::Map picture_cache_; LayerRasterCacheKey::Map layer_cache_; bool checkerboard_images_; fml::WeakPtrFactory weak_factory_; + void TraceStatsToTimeline() const; + FML_DISALLOW_COPY_AND_ASSIGN(RasterCache); }; diff --git a/flow/raster_cache_key.cc b/flow/raster_cache_key.cc index a75bfecfee8b2..4dfef752f6974 100644 --- a/flow/raster_cache_key.cc +++ b/flow/raster_cache_key.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/raster_cache_key.h b/flow/raster_cache_key.h index a5fcbf3b2ce54..5fb6cc279c511 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,9 +8,6 @@ #include #include "flutter/flow/matrix_decomposition.h" #include "flutter/fml/logging.h" -#include "flutter/fml/macros.h" -#include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/core/SkPicture.h" namespace flow { diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 5d6c89b48e23d..3fd73d5bca55e 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/scene_update_context.cc b/flow/scene_update_context.cc index a89df0ff7ae9a..d0e9f5b930b69 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -40,13 +40,56 @@ void SceneUpdateContext::RemoveExportNode(ExportNode* export_node) { export_nodes_.erase(export_node); } -void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, - const SkRRect& rrect, - SkColor color, - const SkRect& paint_bounds, - std::vector paint_layers) { +// Helper function to generate clip planes for a scenic::EntityNode. +static 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 SceneUpdateContext::CreateFrame( + std::unique_ptr entity_node, + const SkRRect& rrect, + SkColor color, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer) { // Frames always clip their children. - entity_node.SetClip(0u, true /* clip to self */); + SetEntityNodeClipPlanes(entity_node.get(), rrect.getBounds()); + // TODO(SCN-1274): AddPart() and SetClip() will be deleted. + entity_node->SetClip(0u, true /* clip to self */); // We don't need a shape if the frame is zero size. if (rrect.isEmpty()) @@ -70,7 +113,8 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(), shape_bounds.height() * 0.5f + shape_bounds.top(), 0.f); - entity_node.AddPart(shape_node); + // TODO(SCN-1274): AddPart() and SetClip() will be deleted. + entity_node->AddPart(shape_node); // Check whether the painted layers will be visible. if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds)) @@ -83,8 +127,8 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, } // Apply current metrics and transformation scale factors. - const float scale_x = metrics_->scale_x * top_scale_x_; - const float scale_y = metrics_->scale_y * top_scale_y_; + const float scale_x = ScaleX(); + const float scale_y = ScaleY(); // If the painted area only covers a portion of the frame then we can // reduce the texture size by drawing just that smaller area. @@ -100,15 +144,17 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, inner_node.SetTranslation(inner_bounds.width() * 0.5f + inner_bounds.left(), inner_bounds.height() * 0.5f + inner_bounds.top(), 0.f); - entity_node.AddPart(inner_node); + entity_node->AddPart(inner_node); SetShapeTextureOrColor(inner_node, color, scale_x, scale_y, inner_bounds, - std::move(paint_layers)); + std::move(paint_layers), layer, + std::move(entity_node)); return; } // Apply a texture to the whole shape. SetShapeTextureOrColor(shape_node, color, scale_x, scale_y, shape_bounds, - std::move(paint_layers)); + std::move(paint_layers), layer, + std::move(entity_node)); } void SceneUpdateContext::SetShapeTextureOrColor( @@ -117,9 +163,12 @@ void SceneUpdateContext::SetShapeTextureOrColor( SkScalar scale_x, SkScalar scale_y, const SkRect& paint_bounds, - std::vector paint_layers) { + std::vector paint_layers, + Layer* layer, + std::unique_ptr entity_node) { scenic::Image* image = GenerateImageIfNeeded( - color, scale_x, scale_y, paint_bounds, std::move(paint_layers)); + color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer, + std::move(entity_node)); if (image != nullptr) { scenic::Material material(session_); material.SetTexture(*image); @@ -146,7 +195,9 @@ scenic::Image* SceneUpdateContext::GenerateImageIfNeeded( SkScalar scale_x, SkScalar scale_y, const SkRect& paint_bounds, - std::vector paint_layers) { + std::vector paint_layers, + Layer* layer, + std::unique_ptr entity_node) { // Bail if there's nothing to paint. if (paint_layers.empty()) return nullptr; @@ -158,7 +209,10 @@ scenic::Image* SceneUpdateContext::GenerateImageIfNeeded( return nullptr; // Acquire a surface from the surface producer and register the paint tasks. - auto surface = surface_producer_->ProduceSurface(physical_size); + std::unique_ptr surface = + surface_producer_->ProduceSurface(physical_size, + LayerRasterCacheKey(layer, Matrix()), + std::move(entity_node)); if (!surface) { FML_LOG(ERROR) << "Could not acquire a surface from the surface producer " @@ -187,7 +241,9 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { for (auto& task : paint_tasks_) { FML_DCHECK(task.surface); SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas(); - Layer::PaintContext context = {*canvas, + Layer::PaintContext context = {canvas, + canvas, + nullptr, frame.context().frame_time(), frame.context().engine_time(), frame.context().texture_registry(), @@ -208,11 +264,10 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { } SceneUpdateContext::Entity::Entity(SceneUpdateContext& context) - : context_(context), - previous_entity_(context.top_entity_), - entity_node_(context.session()) { + : context_(context), previous_entity_(context.top_entity_) { + entity_node_ptr_ = std::make_unique(context.session()); if (previous_entity_) - previous_entity_->entity_node_.AddChild(entity_node_); + previous_entity_->entity_node_ptr_->AddChild(*entity_node_ptr_); context.top_entity_ = this; } @@ -231,8 +286,11 @@ SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, shape_bounds.height() * 0.5f + shape_bounds.top(), 0.f); + // TODO(SCN-1274): AddPart() and SetClip() will be deleted. entity_node().AddPart(shape_node); entity_node().SetClip(0u, true /* clip to self */); + + SetEntityNodeClipPlanes(&entity_node(), shape_bounds); } SceneUpdateContext::Clip::~Clip() = default; @@ -290,21 +348,23 @@ SceneUpdateContext::Transform::~Transform() { SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, - float elevation) + float elevation, + Layer* layer) : Entity(context), rrect_(rrect), color_(color), - paint_bounds_(SkRect::MakeEmpty()) { + paint_bounds_(SkRect::MakeEmpty()), + layer_(layer) { if (elevation != 0.0) - entity_node().SetTranslation(0.f, 0.f, elevation); + entity_node().SetTranslation(0.f, 0.f, -elevation); } SceneUpdateContext::Frame::~Frame() { - context().CreateFrame(entity_node(), rrect_, color_, paint_bounds_, - std::move(paint_layers_)); + context().CreateFrame(std::move(entity_node_ptr()), rrect_, color_, + paint_bounds_, std::move(paint_layers_), layer_); } -void SceneUpdateContext::Frame::AddPaintedLayer(Layer* layer) { +void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { FML_DCHECK(layer->needs_painting()); paint_layers_.push_back(layer); paint_bounds_.join(layer->paint_bounds()); diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 8436ad2f263bb..378af4211d507 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include #include "flutter/flow/compositor_context.h" +#include "flutter/flow/raster_cache_key.h" #include "flutter/fml/build_config.h" #include "flutter/fml/compiler_specific.h" #include "flutter/fml/logging.h" @@ -50,8 +51,19 @@ class SceneUpdateContext { public: virtual ~SurfaceProducer() = default; + // The produced surface owns the entity_node and has a layer_key for + // retained rendering. The surface will only be retained if the layer_key + // has a non-null layer pointer (layer_key.id()). virtual std::unique_ptr ProduceSurface( - const SkISize& size) = 0; + const SkISize& size, + const LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) = 0; + + // Query a retained entity node (owned by a retained surface) for retained + // rendering. + virtual bool HasRetainedNode(const LayerRasterCacheKey& key) const = 0; + virtual const scenic::EntityNode& GetRetainedNode( + const LayerRasterCacheKey& key) = 0; virtual void SubmitSurface( std::unique_ptr surface) = 0; @@ -63,13 +75,16 @@ class SceneUpdateContext { ~Entity(); SceneUpdateContext& context() { return context_; } - scenic::EntityNode& entity_node() { return entity_node_; } + scenic::EntityNode& entity_node() { return *entity_node_ptr_; } + std::unique_ptr& entity_node_ptr() { + return entity_node_ptr_; + } private: SceneUpdateContext& context_; Entity* const previous_entity_; - scenic::EntityNode entity_node_; + std::unique_ptr entity_node_ptr_; }; class Clip : public Entity { @@ -96,13 +111,18 @@ class SceneUpdateContext { 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, - float elevation); + float elevation, + Layer* layer = nullptr); + ~Frame(); - void AddPaintedLayer(Layer* layer); + void AddPaintLayer(Layer* layer); private: const SkRRect& rrect_; @@ -110,6 +130,7 @@ class SceneUpdateContext { std::vector paint_layers_; SkRect paint_bounds_; + Layer* layer_; }; SceneUpdateContext(scenic::Session* session, @@ -119,6 +140,8 @@ class SceneUpdateContext { scenic::Session* session() { return session_; } + Entity* top_entity() { return top_entity_; } + bool has_metrics() const { return !!metrics_; } void set_metrics(fuchsia::ui::gfx::MetricsPtr metrics) { metrics_ = std::move(metrics); @@ -147,6 +170,20 @@ class SceneUpdateContext { std::vector> ExecutePaintTasks( CompositorContext::ScopedFrame& frame); + float ScaleX() const { return metrics_->scale_x * top_scale_x_; } + float ScaleY() const { return metrics_->scale_y * top_scale_y_; } + + // The transformation matrix of the current context. It's used to construct + // the LayerRasterCacheKey for a given layer. + SkMatrix Matrix() const { return SkMatrix::MakeScale(ScaleX(), ScaleY()); } + + bool HasRetainedNode(const LayerRasterCacheKey& key) const { + return surface_producer_->HasRetainedNode(key); + } + const scenic::EntityNode& GetRetainedNode(const LayerRasterCacheKey& key) { + return surface_producer_->GetRetainedNode(key); + } + private: struct PaintTask { std::unique_ptr surface; @@ -158,23 +195,35 @@ class SceneUpdateContext { std::vector layers; }; - void CreateFrame(scenic::EntityNode& entity_node, + // Setup the entity_node as a frame that materialize all the paint_layers. In + // most cases, this creates a VulkanSurface (SurfaceProducerSurface) by + // calling SetShapeTextureOrColor and GenerageImageIfNeeded. Such surface will + // own the associated entity_node. If the layer pointer isn't nullptr, the + // surface (and thus the entity_node) will be retained for that layer to + // improve the performance. + void CreateFrame(std::unique_ptr entity_node, const SkRRect& rrect, SkColor color, const SkRect& paint_bounds, - std::vector paint_layers); + std::vector paint_layers, + Layer* layer); void SetShapeTextureOrColor(scenic::ShapeNode& shape_node, SkColor color, SkScalar scale_x, SkScalar scale_y, const SkRect& paint_bounds, - std::vector paint_layers); + std::vector paint_layers, + Layer* layer, + std::unique_ptr entity_node); void SetShapeColor(scenic::ShapeNode& shape_node, SkColor color); - scenic::Image* GenerateImageIfNeeded(SkColor color, - SkScalar scale_x, - SkScalar scale_y, - const SkRect& paint_bounds, - std::vector paint_layers); + scenic::Image* GenerateImageIfNeeded( + SkColor color, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer, + std::unique_ptr entity_node); Entity* top_entity_ = nullptr; float top_scale_x_ = 1.f; diff --git a/flow/skia_gpu_object.cc b/flow/skia_gpu_object.cc index bf9d9a8ec599f..1f789212e6ba9 100644 --- a/flow/skia_gpu_object.cc +++ b/flow/skia_gpu_object.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 7379b7936ec42..c7d814bfcb5fc 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/texture.cc b/flow/texture.cc index d5cc3ae228aa7..bef0de01b1cbd 100644 --- a/flow/texture.cc +++ b/flow/texture.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flow/texture.h b/flow/texture.h index 0af62ccbad2a1..414a11dc820e8 100644 --- a/flow/texture.h +++ b/flow/texture.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/flutter_kernel_transformers/BUILD.gn b/flutter_kernel_transformers/BUILD.gn index af252d30307d2..0411b4b9976c2 100644 --- a/flutter_kernel_transformers/BUILD.gn +++ b/flutter_kernel_transformers/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart b/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart index 27654bb062baa..78c361e98f516 100644 --- a/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart +++ b/flutter_kernel_transformers/lib/track_widget_constructor_locations.dart @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -144,7 +144,7 @@ class _WidgetCallSiteTransformer extends Transformer { /// locations of the parameters passed in from the source location of the /// constructor call but it is convenient to bundle the location and names /// of the parameters passed in so that tools can show parameter locations - /// without reparsing the source code. + /// without re-parsing the source code. ConstructorInvocation _constructLocation( Location location, { String name, @@ -399,7 +399,7 @@ class WidgetCreatorTracker implements ProgramTransformer { new VariableGet(variable), )); // TODO(jacobr): add an assert verifying the locationField is not - // null. Curently we cannot safely add this assert because we do not + // null. Currently, we cannot safely add this assert because we do not // handle Widget classes with optional positional arguments. There are // no Widget classes in the flutter repo with optional positional // arguments but it is possible users could add classes with optional diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 25e065c8b1fe9..ca0b9ad9fec77 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -13,7 +13,6 @@ source_set("fml") { "command_line.h", "compiler_specific.h", "eintr_wrapper.h", - "export.h", "file.cc", "file.h", "icu_util.cc", @@ -46,14 +45,18 @@ source_set("fml") { "paths.h", "string_view.cc", "string_view.h", + "synchronization/atomic_object.h", + "synchronization/count_down_latch.cc", + "synchronization/count_down_latch.h", + "synchronization/shared_mutex.h", "synchronization/thread_annotations.h", - "synchronization/thread_checker.h", "synchronization/waitable_event.cc", "synchronization/waitable_event.h", "task_runner.cc", "task_runner.h", "thread.cc", "thread.h", + "thread_local.cc", "thread_local.h", "time/time_delta.h", "time/time_point.cc", @@ -80,6 +83,12 @@ source_set("fml") { libs = [] + if (is_ios || is_mac) { + sources += [ "platform/posix/shared_mutex_posix.cc" ] + } else { + sources += [ "synchronization/shared_mutex_std.cc" ] + } + if (is_ios || is_mac) { sources += [ "platform/darwin/cf_utils.cc", @@ -138,7 +147,7 @@ source_set("fml") { if (is_fuchsia) { sources += [ "platform/fuchsia/paths_fuchsia.cc" ] - public_deps += [ "//zircon/public/lib/trace-provider" ] + public_deps += [ "//zircon/public/lib/trace" ] } if (is_win) { @@ -176,8 +185,8 @@ executable("fml_unittests") { "message_unittests.cc", "paths_unittests.cc", "string_view_unittest.cc", + "synchronization/count_down_latch_unittests.cc", "synchronization/thread_annotations_unittest.cc", - "synchronization/thread_checker_unittest.cc", "synchronization/waitable_event_unittest.cc", "thread_local_unittests.cc", "thread_unittests.cc", diff --git a/fml/arraysize.h b/fml/arraysize.h index 8457369884a97..636939812bed7 100644 --- a/fml/arraysize.h +++ b/fml/arraysize.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/base32.cc b/fml/base32.cc index 1836ffe084f4d..957d26ee93b15 100644 --- a/fml/base32.cc +++ b/fml/base32.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/base32.h b/fml/base32.h index c5bfa8f58a754..f85bdf653864f 100644 --- a/fml/base32.h +++ b/fml/base32.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/base32_unittest.cc b/fml/base32_unittest.cc index 4d4ab529dac0b..efd2543396ae0 100644 --- a/fml/base32_unittest.cc +++ b/fml/base32_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/build_config.h b/fml/build_config.h index ca9a91a382c7e..2714f46d1cebf 100644 --- a/fml/build_config.h +++ b/fml/build_config.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/closure.h b/fml/closure.h index f40c0b2d0cbaf..fa0afe0104271 100644 --- a/fml/closure.h +++ b/fml/closure.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/command_line.cc b/fml/command_line.cc index 24012867edc84..27c62086ac14e 100644 --- a/fml/command_line.cc +++ b/fml/command_line.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/command_line.h b/fml/command_line.h index 84b84dfe415cc..e1d06e6e6bed5 100644 --- a/fml/command_line.h +++ b/fml/command_line.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/command_line_unittest.cc b/fml/command_line_unittest.cc index 0cafaa52c899c..c04619e483e28 100644 --- a/fml/command_line_unittest.cc +++ b/fml/command_line_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/compiler_specific.h b/fml/compiler_specific.h index 68fbefe1b9153..ac367f2e70213 100644 --- a/fml/compiler_specific.h +++ b/fml/compiler_specific.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/eintr_wrapper.h b/fml/eintr_wrapper.h index 66c8ae0cd0ea3..0f3f953188fed 100644 --- a/fml/eintr_wrapper.h +++ b/fml/eintr_wrapper.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/export.h b/fml/export.h deleted file mode 100644 index 6173bbb71c314..0000000000000 --- a/fml/export.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 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. - -#ifndef FLUTTER_FML_EXPORT_H_ -#define FLUTTER_FML_EXPORT_H_ - -#include "flutter/fml/build_config.h" - -#if OS_WIN -#define FML_EXPORT __declspec(dllimport) -#else -#define FML_EXPORT __attribute__((visibility("default"))) -#endif - -#endif // FLUTTER_FML_EXPORT_H_ diff --git a/fml/file.cc b/fml/file.cc index e29abd62cc653..8deb76c91da0f 100644 --- a/fml/file.cc +++ b/fml/file.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -43,4 +43,19 @@ fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, return CreateDirectory(base_directory, components, permission, 0); } +ScopedTemporaryDirectory::ScopedTemporaryDirectory() { + path_ = CreateTemporaryDirectory(); + if (path_ != "") { + dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead); + } +} + +ScopedTemporaryDirectory::~ScopedTemporaryDirectory() { + if (path_ != "") { + if (!UnlinkDirectory(path_.c_str())) { + FML_LOG(ERROR) << "Could not remove directory: " << path_; + } + } +} + } // namespace fml diff --git a/fml/file.h b/fml/file.h index 699dd95409dfb..068ddc84c999d 100644 --- a/fml/file.h +++ b/fml/file.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -75,20 +75,9 @@ bool WriteAtomically(const fml::UniqueFD& base_directory, class ScopedTemporaryDirectory { public: - ScopedTemporaryDirectory() { - path_ = CreateTemporaryDirectory(); - if (path_ != "") { - dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead); - } - } - - ~ScopedTemporaryDirectory() { - if (path_ != "") { - if (!UnlinkDirectory(path_.c_str())) { - FML_LOG(ERROR) << "Could not remove directory: " << path_; - } - } - } + ScopedTemporaryDirectory(); + + ~ScopedTemporaryDirectory(); const UniqueFD& fd() { return dir_fd_; } diff --git a/fml/file_unittest.cc b/fml/file_unittest.cc index b44dca09f2218..c1ed8f63731c4 100644 --- a/fml/file_unittest.cc +++ b/fml/file_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/icu_util.cc b/fml/icu_util.cc index 28402e84e3f73..fa0cc02b9c20d 100644 --- a/fml/icu_util.cc +++ b/fml/icu_util.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "third_party/icu/source/common/unicode/udata.h" @@ -22,6 +23,10 @@ class ICUContext { valid_ = SetupMapping(icu_data_path) && SetupICU(); } + ICUContext(std::unique_ptr mapping) : mapping_(std::move(mapping)) { + valid_ = SetupICU(); + } + ~ICUContext() = default; bool SetupMapping(const std::string& icu_data_path) { @@ -99,5 +104,17 @@ void InitializeICU(const std::string& icu_data_path) { [&icu_data_path]() { InitializeICUOnce(icu_data_path); }); } +void InitializeICUFromMappingOnce(std::unique_ptr mapping) { + static ICUContext* context = new ICUContext(std::move(mapping)); + FML_CHECK(context->IsValid()) + << "Unable to initialize the ICU context from a mapping."; +} + +void InitializeICUFromMapping(std::unique_ptr mapping) { + std::call_once(g_icu_init_flag, [mapping = std::move(mapping)]() mutable { + InitializeICUFromMappingOnce(std::move(mapping)); + }); +} + } // namespace icu } // namespace fml diff --git a/fml/icu_util.h b/fml/icu_util.h index 1d20945ee015d..22b7a906efce0 100644 --- a/fml/icu_util.h +++ b/fml/icu_util.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,12 +8,15 @@ #include #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" namespace fml { namespace icu { void InitializeICU(const std::string& icu_data_path = ""); +void InitializeICUFromMapping(std::unique_ptr mapping); + } // namespace icu } // namespace fml diff --git a/fml/log_level.h b/fml/log_level.h index 737e80db2ff68..2c9a85305b53d 100644 --- a/fml/log_level.h +++ b/fml/log_level.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,6 +16,13 @@ constexpr LogSeverity LOG_ERROR = 2; constexpr LogSeverity LOG_FATAL = 3; constexpr LogSeverity LOG_NUM_SEVERITIES = 4; +// One of the Windows headers defines ERROR to 0. This makes the token +// concatenation in FML_LOG(ERROR) to resolve to LOG_0. We define this back to +// the appropriate log level. +#ifdef _WIN32 +#define LOG_0 LOG_ERROR +#endif + // LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode #ifdef NDEBUG const LogSeverity LOG_DFATAL = LOG_ERROR; diff --git a/fml/log_settings.cc b/fml/log_settings.cc index 80d0e22fc99d6..d70525a72e089 100644 --- a/fml/log_settings.cc +++ b/fml/log_settings.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/log_settings.h b/fml/log_settings.h index ea3381dfbcabb..2cd14e028c313 100644 --- a/fml/log_settings.h +++ b/fml/log_settings.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/log_settings_state.cc b/fml/log_settings_state.cc index 148c0a43e877b..a0936e6b5b382 100644 --- a/fml/log_settings_state.cc +++ b/fml/log_settings_state.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/logging.cc b/fml/logging.cc index 7503c8e700409..3cf7c86964392 100644 --- a/fml/logging.cc +++ b/fml/logging.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -34,7 +34,7 @@ const char* StripDots(const char* path) { } const char* StripPath(const char* path) { - auto p = strrchr(path, '/'); + auto* p = strrchr(path, '/'); if (p) return p + 1; else diff --git a/fml/logging.h b/fml/logging.h index 178f9b91c0129..20cb887cb20df 100644 --- a/fml/logging.h +++ b/fml/logging.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/macros.h b/fml/macros.h index 79a5479bbf979..0158d0e18c73e 100644 --- a/fml/macros.h +++ b/fml/macros.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/make_copyable.h b/fml/make_copyable.h index bb5d043af3607..0c4f0bb938822 100644 --- a/fml/make_copyable.h +++ b/fml/make_copyable.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/mapping.cc b/fml/mapping.cc index 332341f2fbc06..11882b6662bbf 100644 --- a/fml/mapping.cc +++ b/fml/mapping.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,4 +22,12 @@ const uint8_t* DataMapping::GetMapping() const { return data_.data(); } +size_t NonOwnedMapping::GetSize() const { + return size_; +} + +const uint8_t* NonOwnedMapping::GetMapping() const { + return data_; +} + } // namespace fml diff --git a/fml/mapping.h b/fml/mapping.h index a2a58851e585c..05894a5c219a4 100644 --- a/fml/mapping.h +++ b/fml/mapping.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -84,9 +84,9 @@ class NonOwnedMapping : public Mapping { NonOwnedMapping(const uint8_t* data, size_t size) : data_(data), size_(size) {} - size_t GetSize() const override { return size_; } + size_t GetSize() const override; - const uint8_t* GetMapping() const override { return data_; } + const uint8_t* GetMapping() const override; private: const uint8_t* const data_; diff --git a/fml/memory/ref_counted.h b/fml/memory/ref_counted.h index 269b8eff4551f..badb45d32b53c 100644 --- a/fml/memory/ref_counted.h +++ b/fml/memory/ref_counted.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/ref_counted_internal.h b/fml/memory/ref_counted_internal.h index 2cef256b3442c..a5c5fe04d6280 100644 --- a/fml/memory/ref_counted_internal.h +++ b/fml/memory/ref_counted_internal.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/ref_counted_unittest.cc b/fml/memory/ref_counted_unittest.cc index 5b6f68e1de6d1..13fc37e257c28 100644 --- a/fml/memory/ref_counted_unittest.cc +++ b/fml/memory/ref_counted_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/ref_ptr.h b/fml/memory/ref_ptr.h index 3c92f5042a1b7..51ebccc5e77bb 100644 --- a/fml/memory/ref_ptr.h +++ b/fml/memory/ref_ptr.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/ref_ptr_internal.h b/fml/memory/ref_ptr_internal.h index a72488cad6a86..b38f943751854 100644 --- a/fml/memory/ref_ptr_internal.h +++ b/fml/memory/ref_ptr_internal.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/thread_checker.h b/fml/memory/thread_checker.h index 8101dd4f8403b..4cfd700636fc2 100644 --- a/fml/memory/thread_checker.h +++ b/fml/memory/thread_checker.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/weak_ptr.h b/fml/memory/weak_ptr.h index e0f5f427b3fd7..09cc10a116b35 100644 --- a/fml/memory/weak_ptr.h +++ b/fml/memory/weak_ptr.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/weak_ptr_internal.cc b/fml/memory/weak_ptr_internal.cc index c06db4672b5d6..02e73afa590c9 100644 --- a/fml/memory/weak_ptr_internal.cc +++ b/fml/memory/weak_ptr_internal.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/weak_ptr_internal.h b/fml/memory/weak_ptr_internal.h index 246a2d851da87..28d1fca6d4f57 100644 --- a/fml/memory/weak_ptr_internal.h +++ b/fml/memory/weak_ptr_internal.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/memory/weak_ptr_unittest.cc b/fml/memory/weak_ptr_unittest.cc index 3e0ecdd834bbc..1d8fd9c390781 100644 --- a/fml/memory/weak_ptr_unittest.cc +++ b/fml/memory/weak_ptr_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/message.cc b/fml/message.cc index 7ee778fc1ffa8..4ef7ab3b32274 100644 --- a/fml/message.cc +++ b/fml/message.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,10 @@ namespace fml { +size_t MessageSerializable::GetSerializableTag() const { + return 0; +}; + Message::Message() = default; Message::~Message() = default; @@ -96,7 +100,7 @@ uint8_t* Message::PrepareDecode(size_t size) { if ((size + size_read_) > buffer_length_) { return nullptr; } - auto buffer = buffer_ + size_read_; + auto* buffer = buffer_ + size_read_; size_read_ += size; return buffer; } diff --git a/fml/message.h b/fml/message.h index 607faee048fa6..f9d6bf804ed85 100644 --- a/fml/message.h +++ b/fml/message.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -47,7 +47,7 @@ class MessageSerializable { virtual bool Deserialize(Message& message) = 0; - virtual size_t GetSerializableTag() const { return 0; }; + virtual size_t GetSerializableTag() const; }; // The traits passed to the encode/decode calls that accept traits should be @@ -88,7 +88,7 @@ class Message { template ::value>> FML_WARN_UNUSED_RESULT bool Encode(const T& value) { - if (auto buffer = PrepareEncode(sizeof(T))) { + if (auto* buffer = PrepareEncode(sizeof(T))) { ::memcpy(buffer, &value, sizeof(T)); return true; } @@ -131,7 +131,7 @@ class Message { template ::value>> FML_WARN_UNUSED_RESULT bool Decode(T& value) { - if (auto buffer = PrepareDecode(sizeof(T))) { + if (auto* buffer = PrepareDecode(sizeof(T))) { ::memcpy(&value, buffer, sizeof(T)); return true; } diff --git a/fml/message_loop.cc b/fml/message_loop.cc index 9c2275e219a00..469d47207566a 100644 --- a/fml/message_loop.cc +++ b/fml/message_loop.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,7 @@ FML_THREAD_LOCAL ThreadLocal tls_message_loop([](intptr_t value) { }); MessageLoop& MessageLoop::GetCurrent() { - auto loop = reinterpret_cast(tls_message_loop.Get()); + auto* loop = reinterpret_cast(tls_message_loop.Get()); FML_CHECK(loop != nullptr) << "MessageLoop::EnsureInitializedForCurrentThread was not called on " "this thread prior to message loop use."; diff --git a/fml/message_loop.h b/fml/message_loop.h index 8deaca2aab1a3..6ca54a94c18a3 100644 --- a/fml/message_loop.h +++ b/fml/message_loop.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 8a1d17ba76614..4d2c5bf4ff614 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -145,4 +145,13 @@ void MessageLoopImpl::RunExpiredTasks() { } } +MessageLoopImpl::DelayedTask::DelayedTask(size_t p_order, + fml::closure p_task, + fml::TimePoint p_target_time) + : order(p_order), task(std::move(p_task)), target_time(p_target_time) {} + +MessageLoopImpl::DelayedTask::DelayedTask(const DelayedTask& other) = default; + +MessageLoopImpl::DelayedTask::~DelayedTask() = default; + } // namespace fml diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index 523ea7fa59536..9dab218d0d1db 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -57,8 +57,11 @@ class MessageLoopImpl : public fml::RefCountedThreadSafe { DelayedTask(size_t p_order, fml::closure p_task, - fml::TimePoint p_target_time) - : order(p_order), task(std::move(p_task)), target_time(p_target_time) {} + fml::TimePoint p_target_time); + + DelayedTask(const DelayedTask& other); + + ~DelayedTask(); }; struct DelayedTaskCompare { diff --git a/fml/message_loop_unittests.cc b/fml/message_loop_unittests.cc index e52a7bf93cd0c..d751071b9e8c8 100644 --- a/fml/message_loop_unittests.cc +++ b/fml/message_loop_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/message_unittests.cc b/fml/message_unittests.cc index 284c90512239c..5ac57fffdd80e 100644 --- a/fml/message_unittests.cc +++ b/fml/message_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/native_library.h b/fml/native_library.h index 6ec79d9ba0281..403fe5f531445 100644 --- a/fml/native_library.h +++ b/fml/native_library.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/paths.cc b/fml/paths.cc index 563595ad147a4..87a4ef27fd0f6 100644 --- a/fml/paths.cc +++ b/fml/paths.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/paths.h b/fml/paths.h index 5bb6f8affe3bd..69408317a24b5 100644 --- a/fml/paths.h +++ b/fml/paths.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/paths_unittests.cc b/fml/paths_unittests.cc index a1df4ea214e0f..41abbd3117587 100644 --- a/fml/paths_unittests.cc +++ b/fml/paths_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/jni_util.cc b/fml/platform/android/jni_util.cc index 86884327beb25..b84a351f1c913 100644 --- a/fml/platform/android/jni_util.cc +++ b/fml/platform/android/jni_util.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/jni_util.h b/fml/platform/android/jni_util.h index bfd58ae0cdf20..9fe32242c503c 100644 --- a/fml/platform/android/jni_util.h +++ b/fml/platform/android/jni_util.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/jni_weak_ref.cc b/fml/platform/android/jni_weak_ref.cc index ffd2b3c855738..70ff1b27ccab2 100644 --- a/fml/platform/android/jni_weak_ref.cc +++ b/fml/platform/android/jni_weak_ref.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/jni_weak_ref.h b/fml/platform/android/jni_weak_ref.h index cab86b429a70a..8162c3f462089 100644 --- a/fml/platform/android/jni_weak_ref.h +++ b/fml/platform/android/jni_weak_ref.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/message_loop_android.cc b/fml/platform/android/message_loop_android.cc index 131f4f092258a..c45160d6d6d14 100644 --- a/fml/platform/android/message_loop_android.cc +++ b/fml/platform/android/message_loop_android.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/message_loop_android.h b/fml/platform/android/message_loop_android.h index 8d562c70aad7c..b73b06e3db2c7 100644 --- a/fml/platform/android/message_loop_android.h +++ b/fml/platform/android/message_loop_android.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/paths_android.cc b/fml/platform/android/paths_android.cc index a6104601cdddc..36f5844a15c3b 100644 --- a/fml/platform/android/paths_android.cc +++ b/fml/platform/android/paths_android.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/paths_android.h b/fml/platform/android/paths_android.h index b9ebe2ebf7e64..99c2defdfa867 100644 --- a/fml/platform/android/paths_android.h +++ b/fml/platform/android/paths_android.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/scoped_java_ref.cc b/fml/platform/android/scoped_java_ref.cc index f8d68c0f0df79..2900379219206 100644 --- a/fml/platform/android/scoped_java_ref.cc +++ b/fml/platform/android/scoped_java_ref.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/android/scoped_java_ref.h b/fml/platform/android/scoped_java_ref.h index 5bc9f1019b8f2..7fe8828e216c4 100644 --- a/fml/platform/android/scoped_java_ref.h +++ b/fml/platform/android/scoped_java_ref.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/cf_utils.cc b/fml/platform/darwin/cf_utils.cc index 9fede1f1238b9..e7009992cf68d 100644 --- a/fml/platform/darwin/cf_utils.cc +++ b/fml/platform/darwin/cf_utils.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/cf_utils.h b/fml/platform/darwin/cf_utils.h index b8e64372e605c..f6be04abaaa71 100644 --- a/fml/platform/darwin/cf_utils.h +++ b/fml/platform/darwin/cf_utils.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/message_loop_darwin.h b/fml/platform/darwin/message_loop_darwin.h index 6d53e9000f821..94005ae4c931a 100644 --- a/fml/platform/darwin/message_loop_darwin.h +++ b/fml/platform/darwin/message_loop_darwin.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/message_loop_darwin.mm b/fml/platform/darwin/message_loop_darwin.mm index e258d38519b6b..ca714c81a4bbd 100644 --- a/fml/platform/darwin/message_loop_darwin.mm +++ b/fml/platform/darwin/message_loop_darwin.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/paths_darwin.mm b/fml/platform/darwin/paths_darwin.mm index 025eec73a103d..72e6f35a80949 100644 --- a/fml/platform/darwin/paths_darwin.mm +++ b/fml/platform/darwin/paths_darwin.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/platform_version.h b/fml/platform/darwin/platform_version.h index bec2ed7ed6346..0978a40d5ac06 100644 --- a/fml/platform/darwin/platform_version.h +++ b/fml/platform/darwin/platform_version.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/platform_version.mm b/fml/platform/darwin/platform_version.mm index 152c217c7714f..61274044f7d76 100644 --- a/fml/platform/darwin/platform_version.mm +++ b/fml/platform/darwin/platform_version.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/scoped_block.h b/fml/platform/darwin/scoped_block.h index 758bb0886da7c..17dee5833cb24 100644 --- a/fml/platform/darwin/scoped_block.h +++ b/fml/platform/darwin/scoped_block.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/scoped_block.mm b/fml/platform/darwin/scoped_block.mm index faad92641dce0..3969ed48861bd 100644 --- a/fml/platform/darwin/scoped_block.mm +++ b/fml/platform/darwin/scoped_block.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/darwin/scoped_nsobject.h b/fml/platform/darwin/scoped_nsobject.h index 525185fa6df0b..c2580f22b7724 100644 --- a/fml/platform/darwin/scoped_nsobject.h +++ b/fml/platform/darwin/scoped_nsobject.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -42,12 +42,10 @@ class scoped_nsprotocol { public: explicit scoped_nsprotocol(NST object = nil) : object_(object) {} - scoped_nsprotocol(const scoped_nsprotocol& that) - : object_([that.object_ retain]) {} + scoped_nsprotocol(const scoped_nsprotocol& that) : object_([that.object_ retain]) {} template - scoped_nsprotocol(const scoped_nsprotocol& that) - : object_([that.get() retain]) {} + scoped_nsprotocol(const scoped_nsprotocol& that) : object_([that.get() retain]) {} ~scoped_nsprotocol() { [object_ release]; } @@ -113,15 +111,12 @@ bool operator!=(C p1, const scoped_nsprotocol& p2) { template class scoped_nsobject : public scoped_nsprotocol { public: - explicit scoped_nsobject(NST* object = nil) - : scoped_nsprotocol(object) {} + explicit scoped_nsobject(NST* object = nil) : scoped_nsprotocol(object) {} - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} + scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} template - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} + scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} scoped_nsobject& operator=(const scoped_nsobject& that) { scoped_nsprotocol::operator=(that); @@ -135,12 +130,10 @@ class scoped_nsobject : public scoped_nsprotocol { public: explicit scoped_nsobject(id object = nil) : scoped_nsprotocol(object) {} - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} + scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} template - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} + scoped_nsobject(const scoped_nsobject& that) : scoped_nsprotocol(that) {} scoped_nsobject& operator=(const scoped_nsobject& that) { scoped_nsprotocol::operator=(that); diff --git a/fml/platform/darwin/scoped_nsobject.mm b/fml/platform/darwin/scoped_nsobject.mm index a82b1ce8a8726..8ff8d2934f57d 100644 --- a/fml/platform/darwin/scoped_nsobject.mm +++ b/fml/platform/darwin/scoped_nsobject.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/fuchsia/paths_fuchsia.cc b/fml/platform/fuchsia/paths_fuchsia.cc index 62a2fb0854c23..1b033079ec776 100644 --- a/fml/platform/fuchsia/paths_fuchsia.cc +++ b/fml/platform/fuchsia/paths_fuchsia.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/linux/message_loop_linux.cc b/fml/platform/linux/message_loop_linux.cc index 251a0a8c3bb80..483efe59af348 100644 --- a/fml/platform/linux/message_loop_linux.cc +++ b/fml/platform/linux/message_loop_linux.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/linux/message_loop_linux.h b/fml/platform/linux/message_loop_linux.h index 26cba078d5dcf..32bdb6442789e 100644 --- a/fml/platform/linux/message_loop_linux.h +++ b/fml/platform/linux/message_loop_linux.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/linux/paths_linux.cc b/fml/platform/linux/paths_linux.cc index a6b178970782e..5749bf2ba7807 100644 --- a/fml/platform/linux/paths_linux.cc +++ b/fml/platform/linux/paths_linux.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/linux/timerfd.cc b/fml/platform/linux/timerfd.cc index d5be484702830..0a2cc5311d846 100644 --- a/fml/platform/linux/timerfd.cc +++ b/fml/platform/linux/timerfd.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/linux/timerfd.h b/fml/platform/linux/timerfd.h index 4a56af5553828..6d5ab1a939434 100644 --- a/fml/platform/linux/timerfd.h +++ b/fml/platform/linux/timerfd.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/posix/file_posix.cc b/fml/platform/posix/file_posix.cc index 99a202586d541..d30a9c2f1d2df 100644 --- a/fml/platform/posix/file_posix.cc +++ b/fml/platform/posix/file_posix.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,7 @@ namespace fml { std::string CreateTemporaryDirectory() { char directory_name[] = "/tmp/flutter_XXXXXXXX"; - auto result = ::mkdtemp(directory_name); + auto* result = ::mkdtemp(directory_name); if (result == nullptr) { return ""; } diff --git a/fml/platform/posix/mapping_posix.cc b/fml/platform/posix/mapping_posix.cc index 273eb0e668f2e..bc5adbd107544 100644 --- a/fml/platform/posix/mapping_posix.cc +++ b/fml/platform/posix/mapping_posix.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -69,7 +69,7 @@ FileMapping::FileMapping(const fml::UniqueFD& handle, const auto is_writable = IsWritable(protection); - auto mapping = + auto* mapping = ::mmap(nullptr, stat_buffer.st_size, ToPosixProtectionFlags(protection), is_writable ? MAP_SHARED : MAP_PRIVATE, handle.get(), 0); diff --git a/fml/platform/posix/native_library_posix.cc b/fml/platform/posix/native_library_posix.cc index 16ce46d08a3f7..558061e739673 100644 --- a/fml/platform/posix/native_library_posix.cc +++ b/fml/platform/posix/native_library_posix.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -58,7 +58,7 @@ fml::RefPtr NativeLibrary::CreateForCurrentProcess() { } const uint8_t* NativeLibrary::ResolveSymbol(const char* symbol) { - auto resolved_symbol = static_cast(::dlsym(handle_, symbol)); + auto* resolved_symbol = static_cast(::dlsym(handle_, symbol)); if (resolved_symbol == nullptr) { FML_DLOG(INFO) << "Could not resolve symbol in library: " << symbol; } diff --git a/fml/platform/posix/paths_posix.cc b/fml/platform/posix/paths_posix.cc index 6b93d6f97a8aa..b21f9e4956350 100644 --- a/fml/platform/posix/paths_posix.cc +++ b/fml/platform/posix/paths_posix.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/posix/shared_mutex_posix.cc b/fml/platform/posix/shared_mutex_posix.cc new file mode 100644 index 0000000000000..1435088f09f52 --- /dev/null +++ b/fml/platform/posix/shared_mutex_posix.cc @@ -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. + +#include "flutter/fml/platform/posix/shared_mutex_posix.h" +#include "flutter/fml/logging.h" + +namespace fml { + +SharedMutex* SharedMutex::Create() { + return new SharedMutexPosix(); +} + +SharedMutexPosix::SharedMutexPosix() { + FML_CHECK(pthread_rwlock_init(&rwlock_, nullptr) == 0); +} + +void SharedMutexPosix::Lock() { + pthread_rwlock_wrlock(&rwlock_); +} + +void SharedMutexPosix::LockShared() { + pthread_rwlock_rdlock(&rwlock_); +} + +void SharedMutexPosix::Unlock() { + pthread_rwlock_unlock(&rwlock_); +} + +void SharedMutexPosix::UnlockShared() { + pthread_rwlock_unlock(&rwlock_); +} + +} // namespace fml diff --git a/fml/platform/posix/shared_mutex_posix.h b/fml/platform/posix/shared_mutex_posix.h new file mode 100644 index 0000000000000..a0ab683b47eba --- /dev/null +++ b/fml/platform/posix/shared_mutex_posix.h @@ -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. + +#ifndef FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_POSIX_H_ +#define FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_POSIX_H_ + +#include +#include "flutter/fml/synchronization/shared_mutex.h" + +namespace fml { + +class SharedMutexPosix : public SharedMutex { + public: + virtual void Lock(); + virtual void LockShared(); + virtual void Unlock(); + virtual void UnlockShared(); + + private: + friend SharedMutex* SharedMutex::Create(); + SharedMutexPosix(); + + pthread_rwlock_t rwlock_; +}; + +} // namespace fml + +#endif // FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_POSIX_H_ diff --git a/fml/platform/win/errors_win.cc b/fml/platform/win/errors_win.cc index b19b2b5e52c04..7370ee86ff425 100644 --- a/fml/platform/win/errors_win.cc +++ b/fml/platform/win/errors_win.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/errors_win.h b/fml/platform/win/errors_win.h index 2ddf32936f7a3..4ec3c1d4ffe88 100644 --- a/fml/platform/win/errors_win.h +++ b/fml/platform/win/errors_win.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/file_win.cc b/fml/platform/win/file_win.cc index cef530e8045e2..bad9d052fb942 100644 --- a/fml/platform/win/file_win.cc +++ b/fml/platform/win/file_win.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -316,6 +316,10 @@ bool TruncateFile(const fml::UniqueFD& file, size_t size) { return true; } +bool FileExists(const fml::UniqueFD& base_directory, const char* path) { + return IsFile(GetAbsolutePath(base_directory, path).c_str()); +} + bool WriteAtomically(const fml::UniqueFD& base_directory, const char* file_name, const Mapping& mapping) { diff --git a/fml/platform/win/mapping_win.cc b/fml/platform/win/mapping_win.cc index aaacdb4b8d76f..0b9427451e08e 100644 --- a/fml/platform/win/mapping_win.cc +++ b/fml/platform/win/mapping_win.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/message_loop_win.cc b/fml/platform/win/message_loop_win.cc index 58f1569dd1679..7f529d1deaff5 100644 --- a/fml/platform/win/message_loop_win.cc +++ b/fml/platform/win/message_loop_win.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/message_loop_win.h b/fml/platform/win/message_loop_win.h index aeb784b45e490..6317959a9cb3d 100644 --- a/fml/platform/win/message_loop_win.h +++ b/fml/platform/win/message_loop_win.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/native_library_win.cc b/fml/platform/win/native_library_win.cc index 1ff997219bfb4..3a741893966eb 100644 --- a/fml/platform/win/native_library_win.cc +++ b/fml/platform/win/native_library_win.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/paths_win.cc b/fml/platform/win/paths_win.cc index 156ac70f986b9..56130f7408877 100644 --- a/fml/platform/win/paths_win.cc +++ b/fml/platform/win/paths_win.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/platform/win/wstring_conversion.h b/fml/platform/win/wstring_conversion.h index 35908fe5a222e..8258ba59f722c 100644 --- a/fml/platform/win/wstring_conversion.h +++ b/fml/platform/win/wstring_conversion.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/string_view.cc b/fml/string_view.cc index 52af03b1309f6..5f217c439f50c 100644 --- a/fml/string_view.cc +++ b/fml/string_view.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -84,7 +84,7 @@ size_t StringView::find(StringView s, size_t pos) const { if (s.empty()) return pos; - auto result = std::search(begin() + pos, end(), s.begin(), s.end()); + auto* result = std::search(begin() + pos, end(), s.begin(), s.end()); if (result == end()) return npos; return result - begin(); @@ -94,7 +94,7 @@ size_t StringView::find(char c, size_t pos) const { if (pos > size_) return npos; - auto result = std::find(begin() + pos, end(), c); + auto* result = std::find(begin() + pos, end(), c); if (result == end()) return npos; return result - begin(); @@ -106,8 +106,8 @@ size_t StringView::rfind(StringView s, size_t pos) const { if (s.empty()) return std::min(pos, size_); - auto last = begin() + std::min(size_ - s.size(), pos) + s.size(); - auto result = std::find_end(begin(), last, s.begin(), s.end()); + auto* last = begin() + std::min(size_ - s.size(), pos) + s.size(); + auto* result = std::find_end(begin(), last, s.begin(), s.end()); if (result == last) return npos; return result - begin(); diff --git a/fml/string_view.h b/fml/string_view.h index d4f34a45dfc60..226b84f21479a 100644 --- a/fml/string_view.h +++ b/fml/string_view.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/string_view_unittest.cc b/fml/string_view_unittest.cc index 8dab1500ff7ca..dadf3c669d9fe 100644 --- a/fml/string_view_unittest.cc +++ b/fml/string_view_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/synchronization/atomic_object.h b/fml/synchronization/atomic_object.h new file mode 100644 index 0000000000000..2a06792ff4a24 --- /dev/null +++ b/fml/synchronization/atomic_object.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_SYNCHRONIZATION_ATOMIC_OBJECT_H_ +#define FLUTTER_FML_SYNCHRONIZATION_ATOMIC_OBJECT_H_ + +#include + +namespace fml { + +// A wrapper for an object instance that can be read or written atomically. +template +class AtomicObject { + public: + AtomicObject() = default; + AtomicObject(T object) : object_(object) {} + + T Load() const { + std::lock_guard lock(mutex_); + return object_; + } + + void Store(const T& object) { + std::lock_guard lock(mutex_); + object_ = object; + } + + private: + mutable std::mutex mutex_; + T object_; +}; + +} // namespace fml + +#endif // FLUTTER_FML_SYNCHRONIZATION_ATOMIC_OBJECT_H_ diff --git a/fml/synchronization/count_down_latch.cc b/fml/synchronization/count_down_latch.cc new file mode 100644 index 0000000000000..6273eacb6ad54 --- /dev/null +++ b/fml/synchronization/count_down_latch.cc @@ -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. + +#include "flutter/fml/synchronization/count_down_latch.h" + +#include "flutter/fml/logging.h" + +namespace fml { + +CountDownLatch::CountDownLatch(size_t count) : count_(count) { + if (count_ == 0) { + waitable_event_.Signal(); + } +} + +CountDownLatch::~CountDownLatch() = default; + +void CountDownLatch::Wait() { + waitable_event_.Wait(); +} + +void CountDownLatch::CountDown() { + if (--count_ == 0) { + waitable_event_.Signal(); + } +} + +} // namespace fml diff --git a/fml/synchronization/count_down_latch.h b/fml/synchronization/count_down_latch.h new file mode 100644 index 0000000000000..2036e82a3f8fd --- /dev/null +++ b/fml/synchronization/count_down_latch.h @@ -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. + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/synchronization/waitable_event.h" + +namespace fml { + +class CountDownLatch { + public: + CountDownLatch(size_t count); + + ~CountDownLatch(); + + void Wait(); + + void CountDown(); + + private: + std::atomic_size_t count_; + ManualResetWaitableEvent waitable_event_; + + FML_DISALLOW_COPY_AND_ASSIGN(CountDownLatch); +}; + +} // namespace fml diff --git a/fml/synchronization/count_down_latch_unittests.cc b/fml/synchronization/count_down_latch_unittests.cc new file mode 100644 index 0000000000000..c23fc82638f96 --- /dev/null +++ b/fml/synchronization/count_down_latch_unittests.cc @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/thread.h" +#include "flutter/testing/testing.h" + +namespace fml { + +TEST(CountDownLatchTest, CanWaitOnZero) { + CountDownLatch latch(0); + latch.Wait(); +} + +TEST(CountDownLatchTest, CanWait) { + fml::Thread thread("test_thread"); + const size_t count = 100; + size_t current_count = 0; + CountDownLatch latch(count); + auto decrement_latch_on_thread = [runner = thread.GetTaskRunner(), &latch, + ¤t_count]() { + runner->PostTask([&latch, ¤t_count]() { + std::this_thread::sleep_for(std::chrono::microseconds(100)); + current_count++; + latch.CountDown(); + }); + }; + for (size_t i = 0; i < count; ++i) { + decrement_latch_on_thread(); + } + latch.Wait(); + ASSERT_EQ(current_count, count); +} + +} // namespace fml diff --git a/fml/synchronization/shared_mutex.h b/fml/synchronization/shared_mutex.h new file mode 100644 index 0000000000000..a5f343d2d5e49 --- /dev/null +++ b/fml/synchronization/shared_mutex.h @@ -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. + +#ifndef FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_H_ +#define FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_H_ + +namespace fml { + +// Interface for a reader/writer lock. +class SharedMutex { + public: + static SharedMutex* Create(); + virtual ~SharedMutex() = default; + + virtual void Lock() = 0; + virtual void LockShared() = 0; + virtual void Unlock() = 0; + virtual void UnlockShared() = 0; +}; + +// RAII wrapper that does a shared acquire of a SharedMutex. +class SharedLock { + public: + explicit SharedLock(SharedMutex& shared_mutex) : shared_mutex_(shared_mutex) { + shared_mutex_.LockShared(); + } + + ~SharedLock() { shared_mutex_.UnlockShared(); } + + private: + SharedMutex& shared_mutex_; +}; + +// RAII wrapper that does an exclusive acquire of a SharedMutex. +class UniqueLock { + public: + explicit UniqueLock(SharedMutex& shared_mutex) : shared_mutex_(shared_mutex) { + shared_mutex_.Lock(); + } + + ~UniqueLock() { shared_mutex_.Unlock(); } + + private: + SharedMutex& shared_mutex_; +}; + +} // namespace fml + +#endif // FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_H_ diff --git a/fml/synchronization/shared_mutex_std.cc b/fml/synchronization/shared_mutex_std.cc new file mode 100644 index 0000000000000..41754446abd1f --- /dev/null +++ b/fml/synchronization/shared_mutex_std.cc @@ -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. + +#include "flutter/fml/synchronization/shared_mutex_std.h" + +namespace fml { + +SharedMutex* SharedMutex::Create() { + return new SharedMutexStd(); +} + +void SharedMutexStd::Lock() { + mutex_.lock(); +} + +void SharedMutexStd::LockShared() { + mutex_.lock_shared(); +} + +void SharedMutexStd::Unlock() { + mutex_.unlock(); +} + +void SharedMutexStd::UnlockShared() { + mutex_.unlock_shared(); +} + +} // namespace fml diff --git a/fml/synchronization/shared_mutex_std.h b/fml/synchronization/shared_mutex_std.h new file mode 100644 index 0000000000000..a9b1b9338a598 --- /dev/null +++ b/fml/synchronization/shared_mutex_std.h @@ -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. + +#ifndef FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_STD_H_ +#define FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_STD_H_ + +#include +#include "flutter/fml/synchronization/shared_mutex.h" + +namespace fml { + +class SharedMutexStd : public SharedMutex { + public: + virtual void Lock(); + virtual void LockShared(); + virtual void Unlock(); + virtual void UnlockShared(); + + private: + friend SharedMutex* SharedMutex::Create(); + SharedMutexStd() = default; + + std::shared_timed_mutex mutex_; +}; + +} // namespace fml + +#endif // FLUTTER_FML_SYNCHRONIZATION_SHARED_MUTEX_STD_H_ diff --git a/fml/synchronization/thread_annotations.h b/fml/synchronization/thread_annotations.h index 1b7bf6ecc6903..f5ae885782b5c 100644 --- a/fml/synchronization/thread_annotations.h +++ b/fml/synchronization/thread_annotations.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/synchronization/thread_annotations_unittest.cc b/fml/synchronization/thread_annotations_unittest.cc index 12769451dcf36..7462614ba608f 100644 --- a/fml/synchronization/thread_annotations_unittest.cc +++ b/fml/synchronization/thread_annotations_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/synchronization/thread_checker.h b/fml/synchronization/thread_checker.h deleted file mode 100644 index 13a3e6824d0d3..0000000000000 --- a/fml/synchronization/thread_checker.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 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. - -// A class for checking that the current thread is/isn't the same as an initial -// thread. - -#ifndef FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ -#define FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ - -#include "flutter/fml/build_config.h" - -#if defined(OS_WIN) -#include -#else -#include -#endif - -#include "flutter/fml/logging.h" -#include "flutter/fml/macros.h" - -namespace fml { - -// A simple class that records the identity of the thread that it was created -// on, and at later points can tell if the current thread is the same as its -// creation thread. This class is thread-safe. -// -// Note: Unlike Chromium's |base::ThreadChecker|, this is *not* Debug-only (so -// #ifdef it out if you want something Debug-only). (Rationale: Having a -// |CalledOnValidThread()| that lies in Release builds seems bad. Moreover, -// there's a small space cost to having even an empty class. ) -class ThreadChecker final { - public: -#if defined(OS_WIN) - ThreadChecker() : self_(GetCurrentThreadId()) {} - ~ThreadChecker() {} - - bool IsCreationThreadCurrent() const { return GetCurrentThreadId() == self_; } - - private: - const DWORD self_; - -#else - ThreadChecker() : self_(pthread_self()) {} - ~ThreadChecker() {} - - // Returns true if the current thread is the thread this object was created - // on and false otherwise. - bool IsCreationThreadCurrent() const { - return !!pthread_equal(pthread_self(), self_); - } - - private: - const pthread_t self_; -#endif - - FML_DISALLOW_COPY_AND_ASSIGN(ThreadChecker); -}; - -#ifndef NDEBUG -#define FML_DECLARE_THREAD_CHECKER(c) fml::ThreadChecker c -#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) \ - FML_DCHECK((c).IsCreationThreadCurrent()) -#else -#define FML_DECLARE_THREAD_CHECKER(c) -#define FML_DCHECK_CREATION_THREAD_IS_CURRENT(c) ((void)0) -#endif - -} // namespace fml - -#endif // FLUTTER_FML_SYNCHRONIZATION_THREAD_CHECKER_H_ diff --git a/fml/synchronization/thread_checker_unittest.cc b/fml/synchronization/thread_checker_unittest.cc deleted file mode 100644 index 46203e43adba4..0000000000000 --- a/fml/synchronization/thread_checker_unittest.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 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. - -#include "flutter/fml/synchronization/thread_checker.h" - -#include - -#include "gtest/gtest.h" - -namespace fml { -namespace { - -TEST(ThreadCheckerTest, SameThread) { - ThreadChecker checker; - EXPECT_TRUE(checker.IsCreationThreadCurrent()); -} - -// Note: This test depends on |std::thread| being compatible with -// |pthread_self()|. -TEST(ThreadCheckerTest, DifferentThreads) { - ThreadChecker checker1; - EXPECT_TRUE(checker1.IsCreationThreadCurrent()); - - std::thread thread([&checker1]() { - ThreadChecker checker2; - EXPECT_TRUE(checker2.IsCreationThreadCurrent()); - EXPECT_FALSE(checker1.IsCreationThreadCurrent()); - }); - thread.join(); - - // Note: Without synchronization, we can't look at |checker2| from the main - // thread. -} - -} // namespace -} // namespace fml diff --git a/fml/synchronization/waitable_event.cc b/fml/synchronization/waitable_event.cc index 04052c60ed79d..7064f5db6903c 100644 --- a/fml/synchronization/waitable_event.cc +++ b/fml/synchronization/waitable_event.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/synchronization/waitable_event.h b/fml/synchronization/waitable_event.h index 60306aaa7897e..33640ded23466 100644 --- a/fml/synchronization/waitable_event.h +++ b/fml/synchronization/waitable_event.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/synchronization/waitable_event_unittest.cc b/fml/synchronization/waitable_event_unittest.cc index 92f40577dc9e5..aa420910d72fe 100644 --- a/fml/synchronization/waitable_event_unittest.cc +++ b/fml/synchronization/waitable_event_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/task_runner.cc b/fml/task_runner.cc index 573c1bb8b7b34..2c4cfe4b638a2 100644 --- a/fml/task_runner.cc +++ b/fml/task_runner.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/task_runner.h b/fml/task_runner.h index 0a196604fac81..72c4219029f34 100644 --- a/fml/task_runner.h +++ b/fml/task_runner.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,8 @@ class MessageLoopImpl; class TaskRunner : public fml::RefCountedThreadSafe { public: + virtual ~TaskRunner(); + virtual void PostTask(fml::closure task); virtual void PostTaskForTime(fml::closure task, fml::TimePoint target_time); @@ -25,8 +27,6 @@ class TaskRunner : public fml::RefCountedThreadSafe { virtual bool RunsTasksOnCurrentThread(); - virtual ~TaskRunner(); - static void RunNowOrPostTask(fml::RefPtr runner, fml::closure task); diff --git a/fml/thread.cc b/fml/thread.cc index 5690d73d51f17..b6a4802b48728 100644 --- a/fml/thread.cc +++ b/fml/thread.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/thread.h b/fml/thread.h index aced4bbebdb80..19a6e5ec3ba43 100644 --- a/fml/thread.h +++ b/fml/thread.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/thread_local.cc b/fml/thread_local.cc new file mode 100644 index 0000000000000..d0afbc00186a0 --- /dev/null +++ b/fml/thread_local.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/fml/thread_local.h" + +namespace fml { + +#if FML_THREAD_LOCAL_PTHREADS + +ThreadLocal::ThreadLocal() : ThreadLocal(nullptr) {} + +ThreadLocal::ThreadLocal(ThreadLocalDestroyCallback destroy) + : destroy_(destroy) { + auto callback = + reinterpret_cast(&ThreadLocal::ThreadLocalDestroy); + FML_CHECK(pthread_key_create(&_key, callback) == 0); +} + +ThreadLocal::~ThreadLocal() { + // This will NOT call the destroy callbacks on thread local values still + // active in other threads. Those must be cleared manually. The usage + // of this class should be similar to the thread_local keyword but with + // with a static storage specifier + + // Collect the container + delete reinterpret_cast(pthread_getspecific(_key)); + + // Finally, collect the key + FML_CHECK(pthread_key_delete(_key) == 0); +} + +ThreadLocal::Box::Box(ThreadLocalDestroyCallback destroy, intptr_t value) + : destroy_(destroy), value_(value) {} + +ThreadLocal::Box::~Box() = default; + +#else // FML_THREAD_LOCAL_PTHREADS + +ThreadLocal::ThreadLocal() : ThreadLocal(nullptr) {} + +ThreadLocal::ThreadLocal(ThreadLocalDestroyCallback destroy) + : destroy_(destroy), value_(0) {} + +void ThreadLocal::Set(intptr_t value) { + if (value_ == value) { + return; + } + + if (value_ != 0 && destroy_) { + destroy_(value_); + } + + value_ = value; +} + +intptr_t ThreadLocal::Get() { + return value_; +} + +ThreadLocal::~ThreadLocal() { + if (value_ != 0 && destroy_) { + destroy_(value_); + } +} + +#endif // FML_THREAD_LOCAL_PTHREADS + +} // namespace fml diff --git a/fml/thread_local.h b/fml/thread_local.h index a36d80601f6e6..4bf518244a6e9 100644 --- a/fml/thread_local.h +++ b/fml/thread_local.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,8 +31,9 @@ class ThreadLocal { private: class Box { public: - Box(ThreadLocalDestroyCallback destroy, intptr_t value) - : destroy_(destroy), value_(value) {} + Box(ThreadLocalDestroyCallback destroy, intptr_t value); + + ~Box(); intptr_t Value() const { return value_; } @@ -60,22 +61,18 @@ class ThreadLocal { static inline void ThreadLocalDestroy(void* value) { FML_CHECK(value != nullptr); - auto box = reinterpret_cast(value); + auto* box = reinterpret_cast(value); box->DestroyValue(); delete box; } public: - ThreadLocal() : ThreadLocal(nullptr) {} + ThreadLocal(); - ThreadLocal(ThreadLocalDestroyCallback destroy) : destroy_(destroy) { - auto callback = - reinterpret_cast(&ThreadLocal::ThreadLocalDestroy); - FML_CHECK(pthread_key_create(&_key, callback) == 0); - } + ThreadLocal(ThreadLocalDestroyCallback destroy); void Set(intptr_t value) { - auto box = reinterpret_cast(pthread_getspecific(_key)); + auto* box = reinterpret_cast(pthread_getspecific(_key)); if (box == nullptr) { box = new Box(destroy_, value); FML_CHECK(pthread_setspecific(_key, box) == 0); @@ -85,22 +82,11 @@ class ThreadLocal { } intptr_t Get() { - auto box = reinterpret_cast(pthread_getspecific(_key)); + auto* box = reinterpret_cast(pthread_getspecific(_key)); return box != nullptr ? box->Value() : 0; } - ~ThreadLocal() { - // This will NOT call the destroy callbacks on thread local values still - // active in other threads. Those must be cleared manually. The usage - // of this class should be similar to the thread_local keyword but with - // with a static storage specifier - - // Collect the container - delete reinterpret_cast(pthread_getspecific(_key)); - - // Finally, collect the key - FML_CHECK(pthread_key_delete(_key) == 0); - } + ~ThreadLocal(); private: pthread_key_t _key; @@ -115,30 +101,15 @@ class ThreadLocal { class ThreadLocal { public: - ThreadLocal() : ThreadLocal(nullptr) {} + ThreadLocal(); - ThreadLocal(ThreadLocalDestroyCallback destroy) - : destroy_(destroy), value_(0) {} + ThreadLocal(ThreadLocalDestroyCallback destroy); - void Set(intptr_t value) { - if (value_ == value) { - return; - } + void Set(intptr_t value); - if (value_ != 0 && destroy_) { - destroy_(value_); - } - - value_ = value; - } + intptr_t Get(); - intptr_t Get() { return value_; } - - ~ThreadLocal() { - if (value_ != 0 && destroy_) { - destroy_(value_); - } - } + ~ThreadLocal(); private: ThreadLocalDestroyCallback destroy_; diff --git a/fml/thread_local_unittests.cc b/fml/thread_local_unittests.cc index 9ae5f82d35e16..7f48cfcd532f6 100644 --- a/fml/thread_local_unittests.cc +++ b/fml/thread_local_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/thread_unittests.cc b/fml/thread_unittests.cc index c4ee56779dab4..f48922d84485e 100644 --- a/fml/thread_unittests.cc +++ b/fml/thread_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_delta.h b/fml/time/time_delta.h index 1b7caa281084f..914fc738751cb 100644 --- a/fml/time/time_delta.h +++ b/fml/time/time_delta.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_delta_unittest.cc b/fml/time/time_delta_unittest.cc index 992b4f4a8b888..342006e5a6dca 100644 --- a/fml/time/time_delta_unittest.cc +++ b/fml/time/time_delta_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_point.cc b/fml/time/time_point.cc index 36ca1582372df..f0a912e92fdee 100644 --- a/fml/time/time_point.cc +++ b/fml/time/time_point.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_point.h b/fml/time/time_point.h index 500de54302a0b..4b6e44429f941 100644 --- a/fml/time/time_point.h +++ b/fml/time/time_point.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_point_unittest.cc b/fml/time/time_point_unittest.cc index 2de07b0582442..965cf81d487f7 100644 --- a/fml/time/time_point_unittest.cc +++ b/fml/time/time_point_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/time/time_unittest.cc b/fml/time/time_unittest.cc index eab352d947773..535d79ec5a824 100644 --- a/fml/time/time_unittest.cc +++ b/fml/time/time_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/trace_event.cc b/fml/trace_event.cc index 628b3b785e865..826e37f189eca 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -1,30 +1,75 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/trace_event.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" +#include +#include +#include + +#include "flutter/fml/build_config.h" +#include "flutter/fml/logging.h" + +#if OS_MACOSX && !defined(NDEBUG) + +#include + +#define DCHECK_LITERAL(x) \ + ({ \ + do { \ + FML_DCHECK(malloc_size((x)) == 0) \ + << "Timeline string must not be on the heap."; \ + } while (0); \ + ((x)); \ + }) + +#else // OS_MACOSX + +#define DCHECK_LITERAL(x) ((x)) + +#endif // OS_MACOSX namespace fml { namespace tracing { -void TraceCounter(TraceArg category_group, TraceArg name, TraceIDArg count) { - auto count_string = std::to_string(count); - const char* arg_names[] = {name}; - const char* arg_values[] = {count_string.c_str()}; - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_Counter, // event type - 1, // argument_count - arg_names, // argument_names - arg_values // argument_values +size_t TraceNonce() { + static std::atomic_size_t gLastItem; + return ++gLastItem; +} + +void TraceTimelineEvent(TraceArg category_group, + TraceArg name, + TraceIDArg identifier, + Dart_Timeline_Event_Type type, + const std::vector& c_names, + const std::vector& values) { + const auto argument_count = std::min(c_names.size(), values.size()); + + std::vector c_values; + c_values.resize(argument_count, nullptr); + + for (size_t i = 0; i < argument_count; i++) { +#if !defined(NDEBUG) + DCHECK_LITERAL(c_names[i]); +#endif // !defined(NDEBUG) + + c_values[i] = values[i].c_str(); + } + + Dart_TimelineEvent( + DCHECK_LITERAL(name), // label + Dart_TimelineGetMicros(), // timestamp0 + identifier, // timestamp1_or_async_id + type, // event type + argument_count, // argument_count + const_cast(c_names.data()), // argument_names + c_values.data() // argument_values ); } void TraceEvent0(TraceArg category_group, TraceArg name) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 0, // timestamp1_or_async_id Dart_Timeline_Event_Begin, // event type @@ -38,9 +83,9 @@ void TraceEvent1(TraceArg category_group, TraceArg name, TraceArg arg1_name, TraceArg arg1_val) { - const char* arg_names[] = {arg1_name}; + const char* arg_names[] = {DCHECK_LITERAL(arg1_name)}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 0, // timestamp1_or_async_id Dart_Timeline_Event_Begin, // event type @@ -56,9 +101,10 @@ void TraceEvent2(TraceArg category_group, TraceArg arg1_val, TraceArg arg2_name, TraceArg arg2_val) { - const char* arg_names[] = {arg1_name, arg2_name}; + const char* arg_names[] = {DCHECK_LITERAL(arg1_name), + DCHECK_LITERAL(arg2_name)}; const char* arg_values[] = {arg1_val, arg2_val}; - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 0, // timestamp1_or_async_id Dart_Timeline_Event_Begin, // event type @@ -69,7 +115,7 @@ void TraceEvent2(TraceArg category_group, } void TraceEventEnd(TraceArg name) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 0, // timestamp1_or_async_id Dart_Timeline_Event_End, // event type @@ -79,10 +125,38 @@ void TraceEventEnd(TraceArg name) { ); } +void TraceEventAsyncComplete(TraceArg category_group, + TraceArg name, + TimePoint begin, + TimePoint end) { + auto identifier = TraceNonce(); + + if (begin > end) { + std::swap(begin, end); + } + + Dart_TimelineEvent(DCHECK_LITERAL(name), // label + begin.ToEpochDelta().ToMicroseconds(), // timestamp0 + identifier, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); + Dart_TimelineEvent(DCHECK_LITERAL(name), // label + end.ToEpochDelta().ToMicroseconds(), // timestamp0 + identifier, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values + ); +} + void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Async_Begin, // event type @@ -95,7 +169,7 @@ void TraceEventAsyncBegin0(TraceArg category_group, void TraceEventAsyncEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Async_End, // event type @@ -110,9 +184,9 @@ void TraceEventAsyncBegin1(TraceArg category_group, TraceIDArg id, TraceArg arg1_name, TraceArg arg1_val) { - const char* arg_names[] = {arg1_name}; + const char* arg_names[] = {DCHECK_LITERAL(arg1_name)}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Async_Begin, // event type @@ -127,9 +201,9 @@ void TraceEventAsyncEnd1(TraceArg category_group, TraceIDArg id, TraceArg arg1_name, TraceArg arg1_val) { - const char* arg_names[] = {arg1_name}; + const char* arg_names[] = {DCHECK_LITERAL(arg1_name)}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Async_End, // event type @@ -140,7 +214,7 @@ void TraceEventAsyncEnd1(TraceArg category_group, } void TraceEventInstant0(TraceArg category_group, TraceArg name) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 0, // timestamp1_or_async_id Dart_Timeline_Event_Instant, // event type @@ -153,7 +227,7 @@ void TraceEventInstant0(TraceArg category_group, TraceArg name) { void TraceEventFlowBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Flow_Begin, // event type @@ -166,7 +240,7 @@ void TraceEventFlowBegin0(TraceArg category_group, void TraceEventFlowStep0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Flow_Step, // event type @@ -177,7 +251,7 @@ void TraceEventFlowStep0(TraceArg category_group, } void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label + Dart_TimelineEvent(DCHECK_LITERAL(name), // label Dart_TimelineGetMicros(), // timestamp0 id, // timestamp1_or_async_id Dart_Timeline_Event_Flow_End, // event type diff --git a/fml/trace_event.h b/fml/trace_event.h index 9dd094acaa2b3..a036292a01d9a 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,8 +13,11 @@ #include -#define FML_TRACE_COUNTER(category_group, name, count) \ - TRACE_COUNTER(category_group, name, 0u, name, count) +// TODO(DNO-448): This is disabled because the Fuchsia counter id json parsing +// only handles ints whereas this can produce ints or strings. +#define FML_TRACE_COUNTER(a, b, c, args...) +#define FML_TRACE_EVENT(a, b, args...) TRACE_DURATION(a, b) + #define TRACE_EVENT0(a, b) TRACE_DURATION(a, b) #define TRACE_EVENT1(a, b, c, d) TRACE_DURATION(a, b, c, d) #define TRACE_EVENT2(a, b, c, d, e, f) TRACE_DURATION(a, b, c, d, e, f) @@ -22,14 +25,19 @@ #define TRACE_EVENT_ASYNC_END0(a, b, c) TRACE_ASYNC_END(a, b, c) #define TRACE_EVENT_ASYNC_BEGIN1(a, b, c, d, e) TRACE_ASYNC_BEGIN(a, b, c, d, e) #define TRACE_EVENT_ASYNC_END1(a, b, c, d, e) TRACE_ASYNC_END(a, b, c, d, e) +#define TRACE_EVENT_INSTANT0(a, b) TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD) #endif // defined(OS_FUCHSIA) #include #include #include +#include +#include #include "flutter/fml/macros.h" +#include "flutter/fml/time/time_point.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" #if !defined(OS_FUCHSIA) @@ -45,8 +53,13 @@ // from trace/event.h on Fuchsia. // // TODO(chinmaygarde): All macros here should have the FML prefix. -#define FML_TRACE_COUNTER(category_group, name, count) \ - ::fml::tracing::TraceCounter(category_group, name, count); +#define FML_TRACE_COUNTER(category_group, name, counter_id, arg1, ...) \ + ::fml::tracing::TraceCounter((category_group), (name), (counter_id), (arg1), \ + __VA_ARGS__); + +#define FML_TRACE_EVENT(category_group, name, ...) \ + ::fml::tracing::TraceEvent((category_group), (name), __VA_ARGS__); \ + __FML__AUTO_TRACE_END(name) #define TRACE_EVENT0(category_group, name) \ ::fml::tracing::TraceEvent0(category_group, name); \ @@ -99,7 +112,76 @@ namespace tracing { using TraceArg = const char*; using TraceIDArg = int64_t; -void TraceCounter(TraceArg category_group, TraceArg name, TraceIDArg count); +void TraceTimelineEvent(TraceArg category_group, + TraceArg name, + TraceIDArg id, + Dart_Timeline_Event_Type type, + const std::vector& names, + const std::vector& values); + +inline std::string TraceToString(const char* string) { + return std::string{string}; +} + +inline std::string TraceToString(std::string string) { + return string; +} + +inline std::string TraceToString(TimePoint point) { + return std::to_string(point.ToEpochDelta().ToNanoseconds()); +} + +template ::value>> +std::string TraceToString(T string) { + return std::to_string(string); +} + +inline void SplitArgumentsCollect(std::vector& keys, + std::vector& values) {} + +template +void SplitArgumentsCollect(std::vector& keys, + std::vector& values, + Key key, + Value value, + Args... args) { + keys.emplace_back(key); + values.emplace_back(TraceToString(value)); + SplitArgumentsCollect(keys, values, args...); +} + +inline std::pair, std::vector> +SplitArguments() { + return {}; +} + +template +std::pair, std::vector> +SplitArguments(Key key, Value value, Args... args) { + std::vector keys; + std::vector values; + SplitArgumentsCollect(keys, values, key, value, args...); + return std::make_pair(std::move(keys), std::move(values)); +} + +size_t TraceNonce(); + +template +void TraceCounter(TraceArg category, + TraceArg name, + TraceIDArg identifier, + Args... args) { + auto split = SplitArguments(args...); + TraceTimelineEvent(category, name, identifier, Dart_Timeline_Event_Counter, + split.first, split.second); +} + +template +void TraceEvent(TraceArg category, TraceArg name, Args... args) { + auto split = SplitArguments(args...); + TraceTimelineEvent(category, name, 0, Dart_Timeline_Event_Begin, split.first, + split.second); +} void TraceEvent0(TraceArg category_group, TraceArg name); @@ -117,6 +199,11 @@ void TraceEvent2(TraceArg category_group, void TraceEventEnd(TraceArg name); +void TraceEventAsyncComplete(TraceArg category_group, + TraceArg name, + TimePoint begin, + TimePoint end); + void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, TraceIDArg id); @@ -147,12 +234,12 @@ void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id); class ScopedInstantEnd { public: - ScopedInstantEnd(std::string str) : label_(std::move(str)) {} + ScopedInstantEnd(const char* str) : label_(str) {} - ~ScopedInstantEnd() { TraceEventEnd(label_.c_str()); } + ~ScopedInstantEnd() { TraceEventEnd(label_); } private: - const std::string label_; + const char* label_; FML_DISALLOW_COPY_AND_ASSIGN(ScopedInstantEnd); }; diff --git a/fml/unique_fd.cc b/fml/unique_fd.cc index 09226c60149b7..dd115c2008b9e 100644 --- a/fml/unique_fd.cc +++ b/fml/unique_fd.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/unique_fd.h b/fml/unique_fd.h index 05dee99018f33..17f1a3cdaa216 100644 --- a/fml/unique_fd.h +++ b/fml/unique_fd.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/unique_object.h b/fml/unique_object.h index e5f2f8690a644..407502a0703b8 100644 --- a/fml/unique_object.h +++ b/fml/unique_object.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,10 +14,10 @@ namespace fml { // struct UniqueFooTraits { -// // This function should be fast an inline. +// // This function should be fast and inline. // static int InvalidValue() { return 0; } // -// // This function should be fast an inline. +// // This function should be fast and inline. // static bool IsValid(const T& value) { return value != InvalidValue(); } // // // This free function will not be called if f == InvalidValue()! diff --git a/frontend_server/BUILD.gn b/frontend_server/BUILD.gn index 06e2b2795e3a8..96a92ed0a04fa 100644 --- a/frontend_server/BUILD.gn +++ b/frontend_server/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/frontend_server/lib/server.dart b/frontend_server/lib/server.dart index fed3766d7c142..e82b855e6c728 100644 --- a/frontend_server/lib/server.dart +++ b/frontend_server/lib/server.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -32,8 +32,8 @@ class _FlutterFrontendCompiler implements frontend.CompilerInterface{ } @override - Future recompileDelta({String filename}) async { - return _compiler.recompileDelta(filename: filename); + Future recompileDelta({String entryPoint}) async { + return _compiler.recompileDelta(entryPoint: entryPoint); } @override diff --git a/lib/io/BUILD.gn b/lib/io/BUILD.gn index 5db1cf3563421..56c37705ef659 100644 --- a/lib/io/BUILD.gn +++ b/lib/io/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/lib/io/dart_io.cc b/lib/io/dart_io.cc index 6dbf65eb7bf2b..1b1866b4e8569 100644 --- a/lib/io/dart_io.cc +++ b/lib/io/dart_io.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/io/dart_io.h b/lib/io/dart_io.h index 73203cfdf1bdb..6d2a36abbbf45 100644 --- a/lib/io/dart_io.h +++ b/lib/io/dart_io.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn index 8d850c5c3997e..595f73994f3fd 100644 --- a/lib/snapshot/BUILD.gn +++ b/lib/snapshot/BUILD.gn @@ -1,18 +1,10 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_root/lib/ui/dart_ui.gni") import("//build/compiled_action.gni") import("//third_party/dart/utils/compile_platform.gni") - -if (is_fuchsia) { - import("//build/dart/dart_library.gni") - import("//build/dart/toolchain.gni") - import("//topaz/public/dart-pkg/fuchsia/sdk_ext.gni") - import("//topaz/public/dart-pkg/zircon/sdk_ext.gni") - import("//topaz/public/lib/ui/flutter/sdk_ext/sdk_ext.gni") -} +import("$flutter_root/lib/ui/dart_ui.gni") bindings_output_dir = "$root_gen_dir/sky/bindings" @@ -53,9 +45,6 @@ compiled_action("generate_snapshot_bin") { ] args = [ - "--strong", - "--sync-async", - "--reify-generic-functions", "--snapshot_kind=core", "--enable_mirrors=false", "--vm_snapshot_data=" + rebase_path(vm_snapshot_data), @@ -63,17 +52,15 @@ compiled_action("generate_snapshot_bin") { "--isolate_snapshot_data=" + rebase_path(isolate_snapshot_data), "--isolate_snapshot_instructions=" + rebase_path(isolate_snapshot_instructions), - rebase_path(platform_kernel), ] if (is_debug) { args += [ "--enable_asserts", - "--enable_type_checks", - "--error_on_bad_type", - "--error_on_bad_override", ] } + + args += [rebase_path(platform_kernel)] } # Generates an assembly file defining a given symbol with the bytes from a @@ -82,23 +69,31 @@ compiled_action("generate_snapshot_bin") { template("bin_to_assembly") { assert(defined(invoker.deps), "Must define deps") assert(defined(invoker.input), "Must define input binary file") - assert(defined(invoker.output), "Must define output assembly file") assert(defined(invoker.symbol), "Must define symbol name") assert(defined(invoker.executable), "Must define boolean executable") action(target_name) { deps = invoker.deps script = "//third_party/dart/runtime/tools/bin_to_assembly.py" + output = invoker.input + ".S" args = [ "--input", rebase_path(invoker.input), "--output", - rebase_path(invoker.output), + rebase_path(output), "--symbol_name", invoker.symbol, "--target_os", current_os, ] + if (defined(invoker.size_symbol)) { + args += [ + "--size_symbol_name", + invoker.size_symbol, + "--target_arch", + current_cpu, + ] + } if (invoker.executable) { args += [ "--executable" ] } @@ -107,64 +102,132 @@ template("bin_to_assembly") { invoker.input, ] outputs = [ - invoker.output, + output, ] } } -bin_to_assembly("vm_snapshot_data_assembly") { +# Generates an object file defining a given symbol with the bytes from a +# binary file. Places the symbol in the read-only data section. +template("bin_to_coff") { + assert(defined(invoker.deps), "Must define deps") + assert(defined(invoker.input), "Must define input binary file") + assert(defined(invoker.symbol), "Must define symbol name") + assert(defined(invoker.executable), "Must define executable") + + action(target_name) { + deps = invoker.deps + script = "//third_party/dart/runtime/tools/bin_to_coff.py" + output = invoker.input + ".o" + args = [ + "--input", + rebase_path(invoker.input), + "--output", + rebase_path(output), + "--symbol_name", + invoker.symbol, + ] + + if (defined(invoker.size_symbol)) { + args += [ + "--size_symbol_name", + invoker.size_symbol, + ] + } + + if (invoker.executable) { + args += [ "--executable" ] + } + + if (current_cpu == "x64") { + args += [ "--64-bit" ] + } + inputs = [ + invoker.input, + ] + outputs = [ + output, + ] + } +} + +template("bin_to_linkable") { + assert(defined(invoker.deps), "Must define deps") + assert(defined(invoker.input), "Must define input binary file") + assert(defined(invoker.symbol), "Must define symbol name") + target_type = "bin_to_assembly" + if (is_win) { + target_type = "bin_to_coff" + } + + target(target_type, target_name) { + forward_variables_from(invoker, "*") + } +} + +bin_to_linkable("vm_snapshot_data_linkable") { deps = [ ":generate_snapshot_bin", ] input = "$target_gen_dir/vm_isolate_snapshot.bin" - output = "$target_gen_dir/vm_snapshot_data.S" symbol = "kDartVmSnapshotData" executable = false } -bin_to_assembly("vm_snapshot_instructions_assembly") { +bin_to_linkable("vm_snapshot_instructions_linkable") { deps = [ ":generate_snapshot_bin", ] input = "$target_gen_dir/vm_snapshot_instructions.bin" - output = "$target_gen_dir/vm_snapshot_instructions.S" symbol = "kDartVmSnapshotInstructions" executable = true } -bin_to_assembly("isolate_snapshot_data_assembly") { +bin_to_linkable("isolate_snapshot_data_linkable") { deps = [ ":generate_snapshot_bin", ] input = "$target_gen_dir/isolate_snapshot.bin" - output = "$target_gen_dir/isolate_snapshot_data.S" symbol = "kDartIsolateSnapshotData" executable = false } -bin_to_assembly("isolate_snapshot_instructions_assembly") { +bin_to_linkable("isolate_snapshot_instructions_linkable") { deps = [ ":generate_snapshot_bin", ] input = "$target_gen_dir/isolate_snapshot_instructions.bin" - output = "$target_gen_dir/isolate_snapshot_instructions.S" symbol = "kDartIsolateSnapshotInstructions" executable = true } -source_set("snapshot") { +bin_to_linkable("platform_strong_dill_linkable") { deps = [ - ":isolate_snapshot_data_assembly", - ":isolate_snapshot_instructions_assembly", - ":vm_snapshot_data_assembly", - ":vm_snapshot_instructions_assembly", + ":kernel_platform_files", ] - sources = [ - "$target_gen_dir/isolate_snapshot_data.S", - "$target_gen_dir/isolate_snapshot_instructions.S", - "$target_gen_dir/vm_snapshot_data.S", - "$target_gen_dir/vm_snapshot_instructions.S", + if (is_fuchsia || is_fuchsia_host) { + input = "$root_out_dir/flutter_runner_patched_sdk/platform_strong.dill" + } else { + input = "$root_out_dir/flutter_patched_sdk/platform_strong.dill" + } + symbol = "kPlatformStrongDill" + size_symbol = "kPlatformStrongDillSize" + executable = false +} + +source_set("snapshot") { + deps = [ + ":isolate_snapshot_data_linkable", + ":isolate_snapshot_instructions_linkable", + ":vm_snapshot_data_linkable", + ":vm_snapshot_instructions_linkable", + ":platform_strong_dill_linkable", ] + sources = get_target_outputs(":isolate_snapshot_data_linkable") + + get_target_outputs(":isolate_snapshot_instructions_linkable") + + get_target_outputs(":vm_snapshot_data_linkable") + + get_target_outputs(":vm_snapshot_instructions_linkable") + + get_target_outputs(":platform_strong_dill_linkable") } compile_platform("non_strong_platform") { diff --git a/lib/snapshot/libraries.json b/lib/snapshot/libraries.json index a59ae6c6624f6..99ef4cd778f8c 100644 --- a/lib/snapshot/libraries.json +++ b/lib/snapshot/libraries.json @@ -57,6 +57,14 @@ ], "uri": "../../../third_party/dart/sdk/lib/collection/collection.dart" }, + "ffi": { + "patches": [ + "../../../third_party/dart/runtime/lib/ffi_dynamic_library_patch.dart", + "../../../third_party/dart/runtime/lib/ffi_native_type_patch.dart", + "../../../third_party/dart/runtime/lib/ffi_patch.dart" + ], + "uri": "../../../third_party/dart/sdk/lib/ffi/ffi.dart" + }, "typed_data": { "patches": "../../../third_party/dart/runtime/lib/typed_data_patch.dart", "uri": "../../../third_party/dart/sdk/lib/typed_data/typed_data.dart" diff --git a/lib/snapshot/libraries.yaml b/lib/snapshot/libraries.yaml index 26c3277054c7d..c4244eaff8719 100644 --- a/lib/snapshot/libraries.yaml +++ b/lib/snapshot/libraries.yaml @@ -84,6 +84,13 @@ flutter: - "../../../third_party/dart/runtime/lib/profiler.dart" - "../../../third_party/dart/runtime/lib/timeline.dart" + ffi: + uri: "../../../third_party/dart/sdk/lib/ffi/ffi.dart" + patches: + - "../../../third_party/dart/runtime/lib/ffi_dynamic_library_patch.dart" + - "../../../third_party/dart/runtime/lib/ffi_native_type_patch.dart" + - "../../../third_party/dart/runtime/lib/ffi_patch.dart" + _http: uri: "../../../third_party/dart/sdk/lib/_http/http.dart" diff --git a/lib/snapshot/pubspec.yaml b/lib/snapshot/pubspec.yaml index 1b1139ebc4c3d..54585c8caccc8 100644 --- a/lib/snapshot/pubspec.yaml +++ b/lib/snapshot/pubspec.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/lib/snapshot/snapshot.dart b/lib/snapshot/snapshot.dart deleted file mode 100644 index 48ed29258750d..0000000000000 --- a/lib/snapshot/snapshot.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015 The Chromium 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:collection'; -import 'dart:convert'; -import 'dart:core'; -import 'dart:developer'; -import 'dart:io'; -import 'dart:isolate'; -import 'dart:math'; -import 'dart:typed_data'; -import 'dart:ui'; diff --git a/lib/snapshot/snapshot.h b/lib/snapshot/snapshot.h index 2bb6f0a5d1650..6a9ebfe4ea6e7 100644 --- a/lib/snapshot/snapshot.h +++ b/lib/snapshot/snapshot.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/snapshot/snapshot_fuchsia.dart b/lib/snapshot/snapshot_fuchsia.dart deleted file mode 100644 index 1a9057d07bbee..0000000000000 --- a/lib/snapshot/snapshot_fuchsia.dart +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015 The Chromium 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:collection'; -import 'dart:convert'; -import 'dart:core'; -import 'dart:developer'; -import 'dart:fuchsia'; -import 'dart:io'; -import 'dart:isolate'; -import 'dart:math'; -import 'dart:mozart.internal'; -import 'dart:typed_data'; -import 'dart:ui'; -import 'dart:zircon'; diff --git a/lib/stub_ui/compositing.dart b/lib/stub_ui/compositing.dart new file mode 100644 index 0000000000000..1575db3a06a51 --- /dev/null +++ b/lib/stub_ui/compositing.dart @@ -0,0 +1,328 @@ +// Copyright 2013 The Flutter 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 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. +class Scene { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To create a Scene object, use a [SceneBuilder]. + 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. + Future toImage(int width, int height) { + throw UnimplementedError(); + } + + /// Releases the resources used by this scene. + /// + /// After calling this function, the scene is cannot be used further. + void dispose() { + throw UnimplementedError(); + } +} + +/// Builds a [Scene] containing the given visuals. +/// +/// A [Scene] can then be rendered using [Window.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 { + SceneBuilder(); + + /// Pushes a transform operation onto the operation stack. + /// + /// The objects are transformed by the given matrix before rasterization. + /// + /// See [pop] for details about the operation stack. + EngineLayer pushTransform(Float64List matrix4) { + throw UnimplementedError(); + } + + /// Pushes an offset operation onto the operation stack. + /// + /// This is equivalent to [pushTransform] with a matrix with only translation. + /// + /// See [pop] for details about the operation stack. + EngineLayer pushOffset(double dx, double dy) { + throw UnimplementedError(); + } + + /// Pushes a rectangular clip operation onto the operation stack. + /// + /// Rasterization outside the given rectangle is discarded. + /// + /// 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]). + EngineLayer pushClipRect(Rect rect, {Clip clipBehavior = Clip.antiAlias}) { + throw UnimplementedError(); + } + + /// Pushes a rounded-rectangular clip operation onto the operation stack. + /// + /// Rasterization outside the given rounded rectangle is discarded. + /// + /// 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]). + EngineLayer pushClipRRect(RRect rrect, {Clip clipBehavior = Clip.antiAlias}) { + throw UnimplementedError(); + } + + /// Pushes a path clip operation onto the operation stack. + /// + /// Rasterization outside the given path is discarded. + /// + /// 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]). + EngineLayer pushClipPath(Path path, {Clip clipBehavior = Clip.antiAlias}) { + throw UnimplementedError(); + } + + /// Pushes an opacity operation onto the operation stack. + /// + /// The given alpha value is blended into the alpha value of the objects' + /// rasterization. An alpha value of 0 makes the objects entirely invisible. + /// An alpha value of 255 has no effect (i.e., the objects retain the current + /// opacity). + /// + /// See [pop] for details about the operation stack. + EngineLayer pushOpacity(int alpha, {Offset offset = Offset.zero}) { + throw UnimplementedError(); + } + + /// Pushes a color filter operation onto the operation stack. + /// + /// The given color is applied to the objects' rasterization using the given + /// blend mode. + /// + /// See [pop] for details about the operation stack. + EngineLayer pushColorFilter(Color color, BlendMode blendMode) { + throw UnimplementedError(); + } + + /// 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. + /// + /// See [pop] for details about the operation stack. + EngineLayer pushBackdropFilter(ImageFilter filter) { + throw UnimplementedError(); + } + + /// Pushes a shader mask operation onto the operation stack. + /// + /// The given shader is applied to the object's rasterization in the given + /// rectangle using the given blend mode. + /// + /// See [pop] for details about the operation stack. + EngineLayer pushShaderMask(Shader shader, Rect maskRect, BlendMode blendMode) { + throw UnimplementedError(); + } + + /// Pushes a physical layer operation for an arbitrary shape onto the + /// operation stack. + /// + /// By default, the layer's content will not be clipped (clip = [Clip.none]). + /// If clip equals [Clip.hardEdge], [Clip.antiAlias], or [Clip.antiAliasWithSaveLayer], + /// then the content is clipped to the given shape defined by [path]. + /// + /// If [elevation] is greater than 0.0, then a shadow is drawn around the layer. + /// [shadowColor] defines the color of the shadow if present and [color] defines the + /// color of the layer background. + /// + /// See [pop] for details about the operation stack, and [Clip] for different clip modes. + // ignore: deprecated_member_use + EngineLayer pushPhysicalShape({ Path path, double elevation, Color color, Color shadowColor, Clip clipBehavior = Clip.none}) { + throw UnimplementedError(); + } + + /// Ends the effect of the most recently pushed operation. + /// + /// Internally the scene builder maintains a stack of operations. Each of the + /// operations in the stack applies to each of the objects added to the scene. + /// Calling this function removes the most recently added operation from the + /// stack. + void pop() { + throw UnimplementedError(); + } + + /// Add a retained engine layer subtree from previous frames. + /// + /// All the engine layers that are in the subtree of the retained layer will + /// be automatically appended to the current engine layer tree. + /// + /// Therefore, when implementing a subclass of the [Layer] concept defined in + /// the rendering layer of Flutter's framework, once this is called, there's + /// no need to call [addToScene] for its children layers. + EngineLayer addRetained(EngineLayer retainedLayer) { + throw UnimplementedError(); + } + + /// Adds an object to the scene that displays performance statistics. + /// + /// Useful during development to assess the performance of the application. + /// The enabledOptions controls which statistics are displayed. The bounds + /// controls where the statistics are displayed. + /// + /// enabledOptions is a bit field with the following bits defined: + /// - 0x01: displayRasterizerStatistics - show GPU thread frame time + /// - 0x02: visualizeRasterizerStatistics - graph GPU thread frame times + /// - 0x04: displayEngineStatistics - show UI thread frame time + /// - 0x08: visualizeEngineStatistics - graph UI thread frame times + /// Set enabledOptions to 0x0F to enable all the currently defined features. + /// + /// The "UI thread" is the thread that includes all the execution of + /// the main Dart isolate (the isolate that can call + /// [Window.render]). The UI thread frame time is the total time + /// spent executing the [Window.onBeginFrame] callback. The "GPU + /// 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. + // Values above must match constants in //engine/src/sky/compositor/performance_overlay_layer.h + void addPerformanceOverlay(int enabledOptions, Rect bounds) { + throw UnimplementedError(); + } + + /// Adds a [Picture] to the scene. + /// + /// The picture is rasterized at the given offset. + void addPicture(Offset offset, Picture picture, { bool isComplexHint: false, bool willChangeHint: false }) { + throw UnimplementedError(); + } + + /// Adds a backend texture to the scene. + /// + /// The texture is scaled to the given size and rasterized at the given offset. + /// + /// If `freeze` is true the texture that is added to the scene will not + /// be updated with new frames. `freeze` is used when resizing an embedded + /// Android view: When resizing an Android view there is a short period during + /// which the framework cannot tell if the newest texture frame has the + /// previous or new size, to workaround this the framework "freezes" the + /// texture just before resizing the Android view and un-freezes it when it is + /// certain that a frame with the new size is ready. + void addTexture(int textureId, { Offset offset: Offset.zero, double width: 0.0, double height: 0.0 , bool freeze: false}) { + throw UnimplementedError(); + } + + /// Adds a platform view (e.g an iOS UIView) to the scene. + /// + /// Only supported on iOS, this is currently a no-op on other platforms. + /// + /// On iOS this layer splits the current output surface into two surfaces, one for the scene nodes + /// preceding the platform view, and one for the scene nodes following the platform view. + /// + /// ## Performance impact + /// + /// Adding an additional surface doubles the amount of graphics memory directly used by Flutter + /// for output buffers. Quartz might allocated extra buffers for compositing the Flutter surfaces + /// and the platform view. + /// + /// With a platform view in the scene, Quartz has to composite the two Flutter surfaces and the + /// embedded UIView. In addition to that, on iOS versions greater than 9, the Flutter frames are + /// synchronized with the UIView frames adding additional performance overhead. + void addPlatformView(int viewId, { Offset offset: Offset.zero, double width: 0.0, double height: 0.0}) { + throw UnimplementedError(); + } + + /// (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, + SceneHost sceneHost, + bool hitTestable: true + }) { + throw UnimplementedError(); + } + + /// Sets a threshold after which additional debugging information should be recorded. + /// + /// Currently this interface is difficult to use by end-developers. If you're + /// interested in using this feature, please contact [flutter-dev](https://groups.google.com/forum/#!forum/flutter-dev). + /// We'll hopefully be able to figure out how to make this feature more useful + /// to you. + void setRasterizerTracingThreshold(int frameInterval) { + throw UnimplementedError(); + } + + /// Sets whether the raster cache should checkerboard cached entries. This is + /// only useful for debugging purposes. + /// + /// The compositor can sometimes decide to cache certain portions of the + /// widget hierarchy. Such portions typically don't change often from frame to + /// frame and are expensive to render. This can speed up overall rendering. However, + /// there is certain upfront cost to constructing these cache entries. And, if + /// the cache entries are not used very often, this cost may not be worth the + /// speedup in rendering of subsequent frames. If the developer wants to be certain + /// that populating the raster cache is not causing stutters, this option can be + /// set. Depending on the observations made, hints can be provided to the compositor + /// that aid it in making better decisions about caching. + /// + /// Currently this interface is difficult to use by end-developers. If you're + /// interested in using this feature, please contact [flutter-dev](https://groups.google.com/forum/#!forum/flutter-dev). + void setCheckerboardRasterCacheImages(bool checkerboard) { + throw UnimplementedError(); + } + + /// Sets whether the compositor should checkerboard layers that are rendered + /// to offscreen bitmaps. + /// + /// This is only useful for debugging purposes. + void setCheckerboardOffscreenLayers(bool checkerboard) { + throw UnimplementedError(); + } + + /// 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]. + /// + /// After calling this function, the scene builder object is invalid and + /// cannot be used further. + Scene build() { + throw UnimplementedError(); + } +} + +/// (Fuchsia-only) Hosts content provided by another application. +class SceneHost { + /// Creates a host for a child scene. + /// + /// The export token is bound to a scene graph node which acts as a container + /// for the child's content. The creator of the scene host is responsible for + /// sending the corresponding import token (the other endpoint of the event pair) + /// to the child. + /// + /// The export 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 scene host takes ownership of the provided export token handle. + SceneHost(dynamic exportTokenHandle); + + /// Releases the resources associated with the child scene host. + /// + /// After calling this function, the child scene host cannot be used further. + void dispose() { + throw UnimplementedError(); + } +} diff --git a/lib/stub_ui/geometry.dart b/lib/stub_ui/geometry.dart new file mode 100644 index 0000000000000..0bc96c2b1eb3a --- /dev/null +++ b/lib/stub_ui/geometry.dart @@ -0,0 +1,1681 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +/// Base class for [Size] and [Offset], which are both ways to describe +/// a distance as a two-dimensional axis-aligned vector. +abstract class OffsetBase { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + /// + /// The first argument sets the horizontal component, and the second the + /// vertical component. + const OffsetBase(this._dx, this._dy); + + final double _dx; + final double _dy; + + /// Returns true if either component is [double.infinity], and false if both + /// are finite (or negative infinity, or NaN). + /// + /// This is different than comparing for equality with an instance that has + /// _both_ components set to [double.infinity]. + /// + /// See also: + /// + /// * [isFinite], which is true if both components are finite (and not NaN). + bool get isInfinite => _dx >= double.infinity || _dy >= double.infinity; + + /// Whether both components are finite (neither infinite nor NaN). + /// + /// See also: + /// + /// * [isInfinite], which returns true if either component is equal to + /// positive infinity. + bool get isFinite => _dx.isFinite && _dy.isFinite; + + /// Less-than operator. Compares an [Offset] or [Size] to another [Offset] or + /// [Size], and returns true if both the horizontal and vertical values of the + /// left-hand-side operand are smaller than the horizontal and vertical values + /// of the right-hand-side operand respectively. Returns false otherwise. + /// + /// This is a partial ordering. It is possible for two values to be neither + /// less, nor greater than, nor equal to, another. + bool operator <(OffsetBase other) => _dx < other._dx && _dy < other._dy; + + /// Less-than-or-equal-to operator. Compares an [Offset] or [Size] to another + /// [Offset] or [Size], and returns true if both the horizontal and vertical + /// values of the left-hand-side operand are smaller than or equal to the + /// horizontal and vertical values of the right-hand-side operand + /// respectively. Returns false otherwise. + /// + /// This is a partial ordering. It is possible for two values to be neither + /// less, nor greater than, nor equal to, another. + bool operator <=(OffsetBase other) => _dx <= other._dx && _dy <= other._dy; + + /// Greater-than operator. Compares an [Offset] or [Size] to another [Offset] + /// or [Size], and returns true if both the horizontal and vertical values of + /// the left-hand-side operand are bigger than the horizontal and vertical + /// values of the right-hand-side operand respectively. Returns false + /// otherwise. + /// + /// This is a partial ordering. It is possible for two values to be neither + /// less, nor greater than, nor equal to, another. + bool operator >(OffsetBase other) => _dx > other._dx && _dy > other._dy; + + /// Greater-than-or-equal-to operator. Compares an [Offset] or [Size] to + /// another [Offset] or [Size], and returns true if both the horizontal and + /// vertical values of the left-hand-side operand are bigger than or equal to + /// the horizontal and vertical values of the right-hand-side operand + /// respectively. Returns false otherwise. + /// + /// This is a partial ordering. It is possible for two values to be neither + /// less, nor greater than, nor equal to, another. + bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy; + + /// Equality operator. Compares an [Offset] or [Size] to another [Offset] or + /// [Size], and returns true if the horizontal and vertical values of the + /// left-hand-side operand are equal to the horizontal and vertical values of + /// the right-hand-side operand respectively. Returns false otherwise. + @override + bool operator ==(dynamic other) { + if (other is! OffsetBase) + return false; + final OffsetBase typedOther = other; + return _dx == typedOther._dx && + _dy == typedOther._dy; + } + + @override + int get hashCode => hashValues(_dx, _dy); + + @override + String toString() => '$runtimeType(${_dx?.toStringAsFixed(1)}, ${_dy?.toStringAsFixed(1)})'; +} + +/// An immutable 2D floating-point offset. +/// +/// Generally speaking, Offsets can be interpreted in two ways: +/// +/// 1. As representing a point in Cartesian space a specified distance from a +/// separately-maintained origin. For example, the top-left position of +/// children in the [RenderBox] protocol is typically represented as an +/// [Offset] from the top left of the parent box. +/// +/// 2. As a vector that can be applied to coordinates. For example, when +/// painting a [RenderObject], the parent is passed an [Offset] from the +/// screen's origin which it can add to the offsets of its children to find +/// the [Offset] from the screen's origin to each of the children. +/// +/// Because a particular [Offset] can be interpreted as one sense at one time +/// then as the other sense at a later time, the same class is used for both +/// senses. +/// +/// See also: +/// +/// * [Size], which represents a vector describing the size of a rectangle. +class Offset extends OffsetBase { + /// Creates an offset. The first argument sets [dx], the horizontal component, + /// and the second sets [dy], the vertical component. + const Offset(double dx, double dy) : super(dx, dy); + + /// Creates an offset from its [direction] and [distance]. + /// + /// The direction is in radians clockwise from the positive x-axis. + /// + /// The distance can be omitted, to create a unit vector (distance = 1.0). + factory Offset.fromDirection(double direction, [ double distance = 1.0 ]) { + return new Offset(distance * math.cos(direction), distance * math.sin(direction)); + } + + /// The x component of the offset. + /// + /// The y component is given by [dy]. + double get dx => _dx; + + /// The y component of the offset. + /// + /// The x component is given by [dx]. + double get dy => _dy; + + /// The magnitude of the offset. + /// + /// If you need this value to compare it to another [Offset]'s distance, + /// consider using [distanceSquared] instead, since it is cheaper to compute. + double get distance => math.sqrt(dx * dx + dy * dy); + + /// The square of the magnitude of the offset. + /// + /// This is cheaper than computing the [distance] itself. + double get distanceSquared => dx * dx + dy * dy; + + /// The angle of this offset as radians clockwise from the positive x-axis, in + /// the range -[pi] to [pi], assuming positive values of the x-axis go to the + /// left and positive values of the y-axis go down. + /// + /// Zero means that [dy] is zero and [dx] is zero or positive. + /// + /// Values from zero to [pi]/2 indicate positive values of [dx] and [dy], the + /// bottom-right quadrant. + /// + /// Values from [pi]/2 to [pi] indicate negative values of [dx] and positive + /// values of [dy], the bottom-left quadrant. + /// + /// Values from zero to -[pi]/2 indicate positive values of [dx] and negative + /// values of [dy], the top-right quadrant. + /// + /// Values from -[pi]/2 to -[pi] indicate negative values of [dx] and [dy], + /// the top-left quadrant. + /// + /// When [dy] is zero and [dx] is negative, the [direction] is [pi]. + /// + /// When [dx] is zero, [direction] is [pi]/2 if [dy] is positive and -[pi]/2 + /// if [dy] is negative. + /// + /// See also: + /// + /// * [distance], to compute the magnitude of the vector. + /// * [Canvas.rotate], which uses the same convention for its angle. + double get direction => math.atan2(dy, dx); + + /// An offset with zero magnitude. + /// + /// This can be used to represent the origin of a coordinate space. + static const Offset zero = const Offset(0.0, 0.0); + + /// An offset with infinite x and y components. + /// + /// See also: + /// + /// * [isInfinite], which checks whether either component is infinite. + /// * [isFinite], which checks whether both components are finite. + // This is included for completeness, because [Size.infinite] exists. + static const Offset infinite = const Offset(double.infinity, double.infinity); + + /// Returns a new offset with the x component scaled by `scaleX` and the y + /// component scaled by `scaleY`. + /// + /// If the two scale arguments are the same, consider using the `*` operator + /// instead: + /// + /// ```dart + /// Offset a = const Offset(10.0, 10.0); + /// Offset b = a * 2.0; // same as: a.scale(2.0, 2.0) + /// ``` + /// + /// If the two arguments are -1, consider using the unary `-` operator + /// instead: + /// + /// ```dart + /// Offset a = const Offset(10.0, 10.0); + /// Offset b = -a; // same as: a.scale(-1.0, -1.0) + /// ``` + Offset scale(double scaleX, double scaleY) => new Offset(dx * scaleX, dy * scaleY); + + /// Returns a new offset with translateX added to the x component and + /// translateY added to the y component. + /// + /// If the arguments come from another [Offset], consider using the `+` or `-` + /// operators instead: + /// + /// ```dart + /// Offset a = const Offset(10.0, 10.0); + /// Offset b = const Offset(10.0, 10.0); + /// Offset c = a + b; // same as: a.translate(b.dx, b.dy) + /// Offset d = a - b; // same as: a.translate(-b.dx, -b.dy) + /// ``` + Offset translate(double translateX, double translateY) => new Offset(dx + translateX, dy + translateY); + + /// Unary negation operator. + /// + /// Returns an offset with the coordinates negated. + /// + /// If the [Offset] represents an arrow on a plane, this operator returns the + /// same arrow but pointing in the reverse direction. + Offset operator -() => new Offset(-dx, -dy); + + /// Binary subtraction operator. + /// + /// Returns an offset whose [dx] value is the left-hand-side operand's [dx] + /// minus the right-hand-side operand's [dx] and whose [dy] value is the + /// left-hand-side operand's [dy] minus the right-hand-side operand's [dy]. + /// + /// See also [translate]. + Offset operator -(Offset other) => new Offset(dx - other.dx, dy - other.dy); + + /// Binary addition operator. + /// + /// Returns an offset whose [dx] value is the sum of the [dx] values of the + /// two operands, and whose [dy] value is the sum of the [dy] values of the + /// two operands. + /// + /// See also [translate]. + Offset operator +(Offset other) => new Offset(dx + other.dx, dy + other.dy); + + /// Multiplication operator. + /// + /// Returns an offset whose coordinates are the coordinates of the + /// left-hand-side operand (an Offset) multiplied by the scalar + /// right-hand-side operand (a double). + /// + /// See also [scale]. + Offset operator *(double operand) => new Offset(dx * operand, dy * operand); + + /// Division operator. + /// + /// Returns an offset whose coordinates are the coordinates of the + /// left-hand-side operand (an Offset) divided by the scalar right-hand-side + /// operand (a double). + /// + /// See also [scale]. + Offset operator /(double operand) => new Offset(dx / operand, dy / operand); + + /// Integer (truncating) division operator. + /// + /// Returns an offset whose coordinates are the coordinates of the + /// left-hand-side operand (an Offset) divided by the scalar right-hand-side + /// operand (a double), rounded towards zero. + Offset operator ~/(double operand) => new Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); + + /// Modulo (remainder) operator. + /// + /// Returns an offset whose coordinates are the remainder of dividing the + /// coordinates of the left-hand-side operand (an Offset) by the scalar + /// right-hand-side operand (a double). + Offset operator %(double operand) => new Offset(dx % operand, dy % operand); + + /// Rectangle constructor operator. + /// + /// Combines an [Offset] and a [Size] to form a [Rect] whose top-left + /// coordinate is the point given by adding this offset, the left-hand-side + /// operand, to the origin, and whose size is the right-hand-side operand. + /// + /// ```dart + /// Rect myRect = Offset.zero & const Size(100.0, 100.0); + /// // same as: new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0) + /// ``` + Rect operator &(Size other) => new Rect.fromLTWH(dx, dy, other.width, other.height); + + /// Linearly interpolate between two offsets. + /// + /// If either offset is null, this function interpolates from [Offset.zero]. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) + return b * t; + if (b == null) + return a * (1.0 - t); + return new Offset(lerpDouble(a.dx, b.dx, t), lerpDouble(a.dy, b.dy, t)); + } + + /// Compares two Offsets for equality. + @override + bool operator ==(dynamic other) { + if (other is! Offset) + return false; + final Offset typedOther = other; + return dx == typedOther.dx && + dy == typedOther.dy; + } + + @override + int get hashCode => hashValues(dx, dy); + + @override + String toString() => 'Offset(${dx?.toStringAsFixed(1)}, ${dy?.toStringAsFixed(1)})'; +} + +/// Holds a 2D floating-point size. +/// +/// You can think of this as an [Offset] from the origin. +class Size extends OffsetBase { + /// Creates a [Size] with the given [width] and [height]. + const Size(double width, double height) : super(width, height); + + /// Creates an instance of [Size] that has the same values as another. + // Used by the rendering library's _DebugSize hack. + Size.copy(Size source) : super(source.width, source.height); + + /// Creates a square [Size] whose [width] and [height] are the given dimension. + /// + /// See also: + /// + /// * [new Size.fromRadius], which is more convenient when the available size + /// is the radius of a circle. + const Size.square(double dimension) : super(dimension, dimension); + + /// Creates a [Size] with the given [width] and an infinite [height]. + const Size.fromWidth(double width) : super(width, double.infinity); + + /// Creates a [Size] with the given [height] and an infinite [width]. + const Size.fromHeight(double height) : super(double.infinity, height); + + /// Creates a square [Size] whose [width] and [height] are twice the given + /// dimension. + /// + /// This is a square that contains a circle with the given radius. + /// + /// See also: + /// + /// * [new Size.square], which creates a square with the given dimension. + const Size.fromRadius(double radius) : super(radius * 2.0, radius * 2.0); + + /// The horizontal extent of this size. + double get width => _dx; + + /// The vertical extent of this size. + double get height => _dy; + + /// The aspect ratio of this size. + /// + /// This returns the [width] divided by the [height]. + /// + /// If the [width] is zero, the result will be zero. If the [height] is zero + /// (and the [width] is not), the result will be [double.infinity] or + /// [double.negativeInfinity] as determined by the sign of [width]. + /// + /// See also: + /// + /// * [AspectRatio], a widget for giving a child widget a specific aspect + /// ratio. + /// * [FittedBox], a widget that (in most modes) attempts to maintain a + /// child widget's aspect ratio while changing its size. + double get aspectRatio { + if (height != 0.0) + return width / height; + if (width > 0.0) + return double.infinity; + if (width < 0.0) + return double.negativeInfinity; + return 0.0; + } + + /// An empty size, one with a zero width and a zero height. + static const Size zero = const Size(0.0, 0.0); + + /// A size whose [width] and [height] are infinite. + /// + /// See also: + /// + /// * [isInfinite], which checks whether either dimension is infinite. + /// * [isFinite], which checks whether both dimensions are finite. + static const Size infinite = const Size(double.infinity, double.infinity); + + /// Whether this size encloses a non-zero area. + /// + /// Negative areas are considered empty. + bool get isEmpty => width <= 0.0 || height <= 0.0; + + /// Binary subtraction operator for [Size]. + /// + /// Subtracting a [Size] from a [Size] returns the [Offset] that describes how + /// much bigger the left-hand-side operand is than the right-hand-side + /// operand. Adding that resulting [Offset] to the [Size] that was the + /// right-hand-side operand would return a [Size] equal to the [Size] that was + /// the left-hand-side operand. (i.e. if `sizeA - sizeB -> offsetA`, then + /// `offsetA + sizeB -> sizeA`) + /// + /// Subtracting an [Offset] from a [Size] returns the [Size] that is smaller than + /// the [Size] operand by the difference given by the [Offset] operand. In other + /// words, the returned [Size] has a [width] consisting of the [width] of the + /// left-hand-side operand minus the [Offset.dx] dimension of the + /// right-hand-side operand, and a [height] consisting of the [height] of the + /// left-hand-side operand minus the [Offset.dy] dimension of the + /// right-hand-side operand. + OffsetBase operator -(OffsetBase other) { + if (other is Size) + return new Offset(width - other.width, height - other.height); + if (other is Offset) + return new Size(width - other.dx, height - other.dy); + throw new ArgumentError(other); + } + + /// Binary addition operator for adding an [Offset] to a [Size]. + /// + /// Returns a [Size] whose [width] is the sum of the [width] of the + /// left-hand-side operand, a [Size], and the [Offset.dx] dimension of the + /// right-hand-side operand, an [Offset], and whose [height] is the sum of the + /// [height] of the left-hand-side operand and the [Offset.dy] dimension of + /// the right-hand-side operand. + Size operator +(Offset other) => new Size(width + other.dx, height + other.dy); + + /// Multiplication operator. + /// + /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side + /// operand (a [Size]) multiplied by the scalar right-hand-side operand (a + /// [double]). + Size operator *(double operand) => new Size(width * operand, height * operand); + + /// Division operator. + /// + /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side + /// operand (a [Size]) divided by the scalar right-hand-side operand (a + /// [double]). + Size operator /(double operand) => new Size(width / operand, height / operand); + + /// Integer (truncating) division operator. + /// + /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side + /// operand (a [Size]) divided by the scalar right-hand-side operand (a + /// [double]), rounded towards zero. + Size operator ~/(double operand) => new Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); + + /// Modulo (remainder) operator. + /// + /// Returns a [Size] whose dimensions are the remainder of dividing the + /// left-hand-side operand (a [Size]) by the scalar right-hand-side operand (a + /// [double]). + Size operator %(double operand) => new Size(width % operand, height % operand); + + /// The lesser of the magnitudes of the [width] and the [height]. + double get shortestSide => math.min(width.abs(), height.abs()); + + /// The greater of the magnitudes of the [width] and the [height]. + double get longestSide => math.max(width.abs(), height.abs()); + + // Convenience methods that do the equivalent of calling the similarly named + // methods on a Rect constructed from the given origin and this size. + + /// The offset to the intersection of the top and left edges of the rectangle + /// described by the given [Offset] (which is interpreted as the top-left corner) + /// and this [Size]. + /// + /// See also [Rect.topLeft]. + Offset topLeft(Offset origin) => origin; + + /// The offset to the center of the top edge of the rectangle described by the + /// given offset (which is interpreted as the top-left corner) and this size. + /// + /// See also [Rect.topCenter]. + Offset topCenter(Offset origin) => new Offset(origin.dx + width / 2.0, origin.dy); + + /// The offset to the intersection of the top and right edges of the rectangle + /// described by the given offset (which is interpreted as the top-left corner) + /// and this size. + /// + /// See also [Rect.topRight]. + Offset topRight(Offset origin) => new Offset(origin.dx + width, origin.dy); + + /// The offset to the center of the left edge of the rectangle described by the + /// given offset (which is interpreted as the top-left corner) and this size. + /// + /// See also [Rect.centerLeft]. + Offset centerLeft(Offset origin) => new Offset(origin.dx, origin.dy + height / 2.0); + + /// The offset to the point halfway between the left and right and the top and + /// bottom edges of the rectangle described by the given offset (which is + /// interpreted as the top-left corner) and this size. + /// + /// See also [Rect.center]. + Offset center(Offset origin) => new Offset(origin.dx + width / 2.0, origin.dy + height / 2.0); + + /// The offset to the center of the right edge of the rectangle described by the + /// given offset (which is interpreted as the top-left corner) and this size. + /// + /// See also [Rect.centerLeft]. + Offset centerRight(Offset origin) => new Offset(origin.dx + width, origin.dy + height / 2.0); + + /// The offset to the intersection of the bottom and left edges of the + /// rectangle described by the given offset (which is interpreted as the + /// top-left corner) and this size. + /// + /// See also [Rect.bottomLeft]. + Offset bottomLeft(Offset origin) => new Offset(origin.dx, origin.dy + height); + + /// The offset to the center of the bottom edge of the rectangle described by + /// the given offset (which is interpreted as the top-left corner) and this + /// size. + /// + /// See also [Rect.bottomLeft]. + Offset bottomCenter(Offset origin) => new Offset(origin.dx + width / 2.0, origin.dy + height); + + /// The offset to the intersection of the bottom and right edges of the + /// rectangle described by the given offset (which is interpreted as the + /// top-left corner) and this size. + /// + /// See also [Rect.bottomRight]. + Offset bottomRight(Offset origin) => new Offset(origin.dx + width, origin.dy + height); + + /// Whether the point specified by the given offset (which is assumed to be + /// relative to the top left of the size) lies between the left and right and + /// the top and bottom edges of a rectangle of this size. + /// + /// Rectangles include their top and left edges but exclude their bottom and + /// right edges. + bool contains(Offset offset) { + return offset.dx >= 0.0 && offset.dx < width && offset.dy >= 0.0 && offset.dy < height; + } + + /// A [Size] with the [width] and [height] swapped. + Size get flipped => new Size(height, width); + + /// Linearly interpolate between two sizes + /// + /// If either size is null, this function interpolates from [Size.zero]. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) + return b * t; + if (b == null) + return a * (1.0 - t); + return new Size(lerpDouble(a.width, b.width, t), lerpDouble(a.height, b.height, t)); + } + + /// Compares two Sizes for equality. + // We don't compare the runtimeType because of _DebugSize in the framework. + @override + bool operator ==(dynamic other) { + if (other is! Size) + return false; + final Size typedOther = other; + return _dx == typedOther._dx && + _dy == typedOther._dy; + } + + @override + int get hashCode => hashValues(_dx, _dy); + + @override + String toString() => 'Size(${width?.toStringAsFixed(1)}, ${height?.toStringAsFixed(1)})'; +} + +/// An immutable, 2D, axis-aligned, floating-point rectangle whose coordinates +/// are relative to a given origin. +/// +/// A Rect can be created with one its constructors or from an [Offset] and a +/// [Size] using the `&` operator: +/// +/// ```dart +/// Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0); +/// ``` +class Rect { + Rect._(); + + /// Construct a rectangle from its left, top, right, and bottom edges. + Rect.fromLTRB(double left, double top, double right, double bottom) { + _value + ..[0] = left + ..[1] = top + ..[2] = right + ..[3] = bottom; + } + + /// Construct a rectangle from its left and top edges, its width, and its + /// height. + /// + /// To construct a [Rect] from an [Offset] and a [Size], you can use the + /// rectangle constructor operator `&`. See [Offset.&]. + Rect.fromLTWH(double left, double top, double width, double height) { + _value + ..[0] = left + ..[1] = top + ..[2] = left + width + ..[3] = top + height; + } + + /// Construct a rectangle that bounds the given circle. + /// + /// The `center` argument is assumed to be an offset from the origin. + Rect.fromCircle({ Offset center, double radius }) { + _value + ..[0] = center.dx - radius + ..[1] = center.dy - radius + ..[2] = center.dx + radius + ..[3] = center.dy + radius; + } + + /// Construct the smallest rectangle that encloses the given offsets, treating + /// them as vectors from the origin. + Rect.fromPoints(Offset a, Offset b) { + _value + ..[0] = math.min(a.dx, b.dx) + ..[1] = math.min(a.dy, b.dy) + ..[2] = math.max(a.dx, b.dx) + ..[3] = math.max(a.dy, b.dy); + } + + static const int _kDataSize = 4; + final Float32List _value = new Float32List(_kDataSize); + + /// The offset of the left edge of this rectangle from the x axis. + double get left => _value[0]; + + /// The offset of the top edge of this rectangle from the y axis. + double get top => _value[1]; + + /// The offset of the right edge of this rectangle from the x axis. + double get right => _value[2]; + + /// The offset of the bottom edge of this rectangle from the y axis. + double get bottom => _value[3]; + + /// The distance between the left and right edges of this rectangle. + double get width => right - left; + + /// The distance between the top and bottom edges of this rectangle. + double get height => bottom - top; + + /// The distance between the upper-left corner and the lower-right corner of + /// this rectangle. + Size get size => new Size(width, height); + + /// A rectangle with left, top, right, and bottom edges all at zero. + static final Rect zero = new Rect._(); + + static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h + + /// A rectangle that covers the entire coordinate space. + /// + /// This covers the space from -1e9,-1e9 to 1e9,1e9. + /// This is the space over which graphics operations are valid. + static final Rect largest = new Rect.fromLTRB(-_giantScalar, -_giantScalar, _giantScalar, _giantScalar); + + /// Whether any of the coordinates of this rectangle are equal to positive infinity. + // included for consistency with Offset and Size + bool get isInfinite { + return left >= double.infinity + || top >= double.infinity + || right >= double.infinity + || bottom >= double.infinity; + } + + /// Whether all coordinates of this rectangle are finite. + bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; + + /// Whether this rectangle encloses a non-zero area. Negative areas are + /// considered empty. + bool get isEmpty => left >= right || top >= bottom; + + /// Returns a new rectangle translated by the given offset. + /// + /// To translate a rectangle by separate x and y components rather than by an + /// [Offset], consider [translate]. + Rect shift(Offset offset) { + return new Rect.fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy); + } + + /// Returns a new rectangle with translateX added to the x components and + /// translateY added to the y components. + /// + /// To translate a rectangle by an [Offset] rather than by separate x and y + /// components, consider [shift]. + Rect translate(double translateX, double translateY) { + return new Rect.fromLTRB(left + translateX, top + translateY, right + translateX, bottom + translateY); + } + + /// Returns a new rectangle with edges moved outwards by the given delta. + Rect inflate(double delta) { + return new Rect.fromLTRB(left - delta, top - delta, right + delta, bottom + delta); + } + + /// Returns a new rectangle with edges moved inwards by the given delta. + Rect deflate(double delta) => inflate(-delta); + + /// Returns a new rectangle that is the intersection of the given + /// rectangle and this rectangle. The two rectangles must overlap + /// for this to be meaningful. If the two rectangles do not overlap, + /// then the resulting Rect will have a negative width or height. + Rect intersect(Rect other) { + return new Rect.fromLTRB( + math.max(left, other.left), + math.max(top, other.top), + math.min(right, other.right), + math.min(bottom, other.bottom) + ); + } + + /// Returns a new rectangle which is the bounding box containing this + /// rectangle and the given rectangle. + Rect expandToInclude(Rect other) { + return new Rect.fromLTRB( + math.min(left, other.left), + math.min(top, other.top), + math.max(right, other.right), + math.max(bottom, other.bottom), + ); + } + + /// Whether `other` has a nonzero area of overlap with this rectangle. + bool overlaps(Rect other) { + if (right <= other.left || other.right <= left) + return false; + if (bottom <= other.top || other.bottom <= top) + return false; + return true; + } + + /// The lesser of the magnitudes of the [width] and the [height] of this + /// rectangle. + double get shortestSide => math.min(width.abs(), height.abs()); + + /// The greater of the magnitudes of the [width] and the [height] of this + /// rectangle. + double get longestSide => math.max(width.abs(), height.abs()); + + /// The offset to the intersection of the top and left edges of this rectangle. + /// + /// See also [Size.topLeft]. + Offset get topLeft => new Offset(left, top); + + /// The offset to the center of the top edge of this rectangle. + /// + /// See also [Size.topCenter]. + Offset get topCenter => new Offset(left + width / 2.0, top); + + /// The offset to the intersection of the top and right edges of this rectangle. + /// + /// See also [Size.topRight]. + Offset get topRight => new Offset(right, top); + + /// The offset to the center of the left edge of this rectangle. + /// + /// See also [Size.centerLeft]. + Offset get centerLeft => new Offset(left, top + height / 2.0); + + /// The offset to the point halfway between the left and right and the top and + /// bottom edges of this rectangle. + /// + /// See also [Size.center]. + Offset get center => new Offset(left + width / 2.0, top + height / 2.0); + + /// The offset to the center of the right edge of this rectangle. + /// + /// See also [Size.centerLeft]. + Offset get centerRight => new Offset(right, top + height / 2.0); + + /// The offset to the intersection of the bottom and left edges of this rectangle. + /// + /// See also [Size.bottomLeft]. + Offset get bottomLeft => new Offset(left, bottom); + + /// The offset to the center of the bottom edge of this rectangle. + /// + /// See also [Size.bottomLeft]. + Offset get bottomCenter => new Offset(left + width / 2.0, bottom); + + /// The offset to the intersection of the bottom and right edges of this rectangle. + /// + /// See also [Size.bottomRight]. + Offset get bottomRight => new Offset(right, bottom); + + /// Whether the point specified by the given offset (which is assumed to be + /// relative to the origin) lies between the left and right and the top and + /// bottom edges of this rectangle. + /// + /// Rectangles include their top and left edges but exclude their bottom and + /// right edges. + bool contains(Offset offset) { + return offset.dx >= left && offset.dx < right && offset.dy >= top && offset.dy < bottom; + } + + /// Linearly interpolate between two rectangles. + /// + /// If either rect is null, [Rect.zero] is used as a substitute. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) + return new Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t); + if (b == null) { + final double k = 1.0 - t; + return new Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k); + } + return new Rect.fromLTRB( + lerpDouble(a.left, b.left, t), + lerpDouble(a.top, b.top, t), + lerpDouble(a.right, b.right, t), + lerpDouble(a.bottom, b.bottom, t), + ); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (runtimeType != other.runtimeType) + return false; + final Rect typedOther = other; + for (int i = 0; i < _kDataSize; i += 1) { + if (_value[i] != typedOther._value[i]) + return false; + } + return true; + } + + @override + int get hashCode => hashList(_value); + + @override + String toString() => 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; +} + +/// A radius for either circular or elliptical shapes. +class Radius { + /// Constructs a circular radius. [x] and [y] will have the same radius value. + const Radius.circular(double radius) : this.elliptical(radius, radius); + + /// Constructs an elliptical radius with the given radii. + const Radius.elliptical(this.x, this.y); + + /// The radius value on the horizontal axis. + final double x; + + /// The radius value on the vertical axis. + final double y; + + /// A radius with [x] and [y] values set to zero. + /// + /// You can use [Radius.zero] with [RRect] to have right-angle corners. + static const Radius zero = const Radius.circular(0.0); + + /// Unary negation operator. + /// + /// Returns a Radius with the distances negated. + /// + /// Radiuses with negative values aren't geometrically meaningful, but could + /// occur as part of expressions. For example, negating a radius of one pixel + /// and then adding the result to another radius is equivalent to subtracting + /// a radius of one pixel from the other. + Radius operator -() => new Radius.elliptical(-x, -y); + + /// Binary subtraction operator. + /// + /// Returns a radius whose [x] value is the left-hand-side operand's [x] + /// minus the right-hand-side operand's [x] and whose [y] value is the + /// left-hand-side operand's [y] minus the right-hand-side operand's [y]. + Radius operator -(Radius other) => new Radius.elliptical(x - other.x, y - other.y); + + /// Binary addition operator. + /// + /// Returns a radius whose [x] value is the sum of the [x] values of the + /// two operands, and whose [y] value is the sum of the [y] values of the + /// two operands. + Radius operator +(Radius other) => new Radius.elliptical(x + other.x, y + other.y); + + /// Multiplication operator. + /// + /// Returns a radius whose coordinates are the coordinates of the + /// left-hand-side operand (a radius) multiplied by the scalar + /// right-hand-side operand (a double). + Radius operator *(double operand) => new Radius.elliptical(x * operand, y * operand); + + /// Division operator. + /// + /// Returns a radius whose coordinates are the coordinates of the + /// left-hand-side operand (a radius) divided by the scalar right-hand-side + /// operand (a double). + Radius operator /(double operand) => new Radius.elliptical(x / operand, y / operand); + + /// Integer (truncating) division operator. + /// + /// Returns a radius whose coordinates are the coordinates of the + /// left-hand-side operand (a radius) divided by the scalar right-hand-side + /// operand (a double), rounded towards zero. + Radius operator ~/(double operand) => new Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); + + /// Modulo (remainder) operator. + /// + /// Returns a radius whose coordinates are the remainder of dividing the + /// coordinates of the left-hand-side operand (a radius) by the scalar + /// right-hand-side operand (a double). + Radius operator %(double operand) => new Radius.elliptical(x % operand, y % operand); + + /// Linearly interpolate between two radii. + /// + /// If either is null, this function substitutes [Radius.zero] instead. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) + return new Radius.elliptical(b.x * t, b.y * t); + if (b == null) { + final double k = 1.0 - t; + return new Radius.elliptical(a.x * k, a.y * k); + } + return new Radius.elliptical( + lerpDouble(a.x, b.x, t), + lerpDouble(a.y, b.y, t), + ); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (runtimeType != other.runtimeType) + return false; + final Radius typedOther = other; + return typedOther.x == x && typedOther.y == y; + } + + @override + int get hashCode => hashValues(x, y); + + @override + String toString() { + return x == y ? 'Radius.circular(${x.toStringAsFixed(1)})' : + 'Radius.elliptical(${x.toStringAsFixed(1)}, ' + '${y.toStringAsFixed(1)})'; + } +} + +/// An immutable rounded rectangle with the custom radii for all four corners. +class RRect { + RRect._(); + + /// Construct a rounded rectangle from its left, top, right, and bottom edges, + /// and the same radii along its horizontal axis and its vertical axis. + RRect.fromLTRBXY(double left, double top, double right, double bottom, + double radiusX, double radiusY) { + _value + ..[0] = left + ..[1] = top + ..[2] = right + ..[3] = bottom + ..[4] = radiusX + ..[5] = radiusY + ..[6] = radiusX + ..[7] = radiusY + ..[8] = radiusX + ..[9] = radiusY + ..[10] = radiusX + ..[11] = radiusY; + } + + /// Construct a rounded rectangle from its left, top, right, and bottom edges, + /// and the same radius in each corner. + RRect.fromLTRBR(double left, double top, double right, double bottom, + Radius radius) { + _value + ..[0] = left + ..[1] = top + ..[2] = right + ..[3] = bottom + ..[4] = radius.x + ..[5] = radius.y + ..[6] = radius.x + ..[7] = radius.y + ..[8] = radius.x + ..[9] = radius.y + ..[10] = radius.x + ..[11] = radius.y; + } + + /// Construct a rounded rectangle from its bounding box and the same radii + /// along its horizontal axis and its vertical axis. + RRect.fromRectXY(Rect rect, double radiusX, double radiusY) { + _value + ..[0] = rect.left + ..[1] = rect.top + ..[2] = rect.right + ..[3] = rect.bottom + ..[4] = radiusX + ..[5] = radiusY + ..[6] = radiusX + ..[7] = radiusY + ..[8] = radiusX + ..[9] = radiusY + ..[10] = radiusX + ..[11] = radiusY; + } + + /// Construct a rounded rectangle from its bounding box and a radius that is + /// the same in each corner. + RRect.fromRectAndRadius(Rect rect, Radius radius) { + _value + ..[0] = rect.left + ..[1] = rect.top + ..[2] = rect.right + ..[3] = rect.bottom + ..[4] = radius.x + ..[5] = radius.y + ..[6] = radius.x + ..[7] = radius.y + ..[8] = radius.x + ..[9] = radius.y + ..[10] = radius.x + ..[11] = radius.y; + } + + /// Construct a rounded rectangle from its left, top, right, and bottom edges, + /// and topLeft, topRight, bottomRight, and bottomLeft radii. + /// + /// The corner radii default to [Radius.zero], i.e. right-angled corners. + RRect.fromLTRBAndCorners( + double left, + double top, + double right, + double bottom, { + Radius topLeft: Radius.zero, + Radius topRight: Radius.zero, + Radius bottomRight: Radius.zero, + Radius bottomLeft: Radius.zero, + }) { + _value + ..[0] = left + ..[1] = top + ..[2] = right + ..[3] = bottom + ..[4] = topLeft.x + ..[5] = topLeft.y + ..[6] = topRight.x + ..[7] = topRight.y + ..[8] = bottomRight.x + ..[9] = bottomRight.y + ..[10] = bottomLeft.x + ..[11] = bottomLeft.y; + } + + /// Construct a rounded rectangle from its bounding box and and topLeft, + /// topRight, bottomRight, and bottomLeft radii. + /// + /// The corner radii default to [Radius.zero], i.e. right-angled corners + RRect.fromRectAndCorners( + Rect rect, + { + Radius topLeft: Radius.zero, + Radius topRight: Radius.zero, + Radius bottomRight: Radius.zero, + Radius bottomLeft: Radius.zero + } + ) { + _value + ..[0] = rect.left + ..[1] = rect.top + ..[2] = rect.right + ..[3] = rect.bottom + ..[4] = topLeft.x + ..[5] = topLeft.y + ..[6] = topRight.x + ..[7] = topRight.y + ..[8] = bottomRight.x + ..[9] = bottomRight.y + ..[10] = bottomLeft.x + ..[11] = bottomLeft.y; + } + + RRect._fromList(List list) { + for (int i = 0; i < _kDataSize; i += 1) + _value[i] = list[i]; + } + + static const int _kDataSize = 12; + final Float32List _value = new Float32List(_kDataSize); + RRect _scaled; // same RRect with scaled radii per side + + /// The offset of the left edge of this rectangle from the x axis. + double get left => _value[0]; + + /// The offset of the top edge of this rectangle from the y axis. + double get top => _value[1]; + + /// The offset of the right edge of this rectangle from the x axis. + double get right => _value[2]; + + /// The offset of the bottom edge of this rectangle from the y axis. + double get bottom => _value[3]; + + /// The top-left horizontal radius. + double get tlRadiusX => _value[4]; + + /// The top-left vertical radius. + double get tlRadiusY => _value[5]; + + /// The top-left [Radius]. + Radius get tlRadius => new Radius.elliptical(_value[4], _value[5]); + + /// The top-right horizontal radius. + double get trRadiusX => _value[6]; + + /// The top-right vertical radius. + double get trRadiusY => _value[7]; + + /// The top-right [Radius]. + Radius get trRadius => new Radius.elliptical(_value[6], _value[7]); + + /// The bottom-right horizontal radius. + double get brRadiusX => _value[8]; + + /// The bottom-right vertical radius. + double get brRadiusY => _value[9]; + + /// The bottom-right [Radius]. + Radius get brRadius => new Radius.elliptical(_value[8], _value[9]); + + /// The bottom-left horizontal radius. + double get blRadiusX => _value[10]; + + /// The bottom-left vertical radius. + double get blRadiusY => _value[11]; + + /// The bottom-left [Radius]. + Radius get blRadius => new Radius.elliptical(_value[10], _value[11]); + + /// A rounded rectangle with all the values set to zero. + static final RRect zero = new RRect._(); + + /// Returns a new [RRect] translated by the given offset. + RRect shift(Offset offset) { + return new RRect.fromLTRBAndCorners( + _value[0] + offset.dx, + _value[1] + offset.dy, + _value[2] + offset.dx, + _value[3] + offset.dy, + topLeft: new Radius.elliptical( + _value[4], + _value[5] + ), + topRight: new Radius.elliptical( + _value[6], + _value[7] + ), + bottomRight: new Radius.elliptical( + _value[8], + _value[9] + ), + bottomLeft: new Radius.elliptical( + _value[10], + _value[11] + ) + ); + } + + /// Returns a new [RRect] with edges and radii moved outwards by the given + /// delta. + RRect inflate(double delta) { + return new RRect.fromLTRBAndCorners( + _value[0] - delta, + _value[1] - delta, + _value[2] + delta, + _value[3] + delta, + topLeft: new Radius.elliptical( + _value[4] + delta, + _value[5] + delta + ), + topRight: new Radius.elliptical( + _value[6] + delta, + _value[7] + delta + ), + bottomRight: new Radius.elliptical( + _value[8] + delta, + _value[9] + delta + ), + bottomLeft: new Radius.elliptical( + _value[10] + delta, + _value[11] + delta + ) + ); + } + + /// Returns a new [RRect] with edges and radii moved inwards by the given delta. + RRect deflate(double delta) => inflate(-delta); + + /// The distance between the left and right edges of this rectangle. + double get width => right - left; + + /// The distance between the top and bottom edges of this rectangle. + double get height => bottom - top; + + /// The bounding box of this rounded rectangle (the rectangle with no rounded corners). + Rect get outerRect => new Rect.fromLTRB(left, top, right, bottom); + + /// The non-rounded rectangle that is constrained by the smaller of the two + /// diagonals, with each diagonal traveling through the middle of the curve + /// corners. The middle of a corner is the intersection of the curve with its + /// respective quadrant bisector. + Rect get safeInnerRect { + const double kInsetFactor = 0.29289321881; // 1-cos(pi/4) + + final double leftRadius = math.max(blRadiusX, tlRadiusX); + final double topRadius = math.max(tlRadiusY, trRadiusY); + final double rightRadius = math.max(trRadiusX, brRadiusX); + final double bottomRadius = math.max(brRadiusY, blRadiusY); + + return new Rect.fromLTRB( + left + leftRadius * kInsetFactor, + top + topRadius * kInsetFactor, + right - rightRadius * kInsetFactor, + bottom - bottomRadius * kInsetFactor + ); + } + + /// The rectangle that would be formed using the axis-aligned intersection of + /// the sides of the rectangle, i.e., the rectangle formed from the + /// inner-most centers of the ellipses that form the corners. This is the + /// intersection of the [wideMiddleRect] and the [tallMiddleRect]. If any of + /// the intersections are void, the resulting [Rect] will have negative width + /// or height. + Rect get middleRect { + final double leftRadius = math.max(blRadiusX, tlRadiusX); + final double topRadius = math.max(tlRadiusY, trRadiusY); + final double rightRadius = math.max(trRadiusX, brRadiusX); + final double bottomRadius = math.max(brRadiusY, blRadiusY); + return new Rect.fromLTRB( + left + leftRadius, + top + topRadius, + right - rightRadius, + bottom - bottomRadius + ); + } + + /// The biggest rectangle that is entirely inside the rounded rectangle and + /// has the full width of the rounded rectangle. If the rounded rectangle does + /// not have an axis-aligned intersection of its left and right side, the + /// resulting [Rect] will have negative width or height. + Rect get wideMiddleRect { + final double topRadius = math.max(tlRadiusY, trRadiusY); + final double bottomRadius = math.max(brRadiusY, blRadiusY); + return new Rect.fromLTRB( + left, + top + topRadius, + right, + bottom - bottomRadius + ); + } + + /// The biggest rectangle that is entirely inside the rounded rectangle and + /// has the full height of the rounded rectangle. If the rounded rectangle + /// does not have an axis-aligned intersection of its top and bottom side, the + /// resulting [Rect] will have negative width or height. + Rect get tallMiddleRect { + final double leftRadius = math.max(blRadiusX, tlRadiusX); + final double rightRadius = math.max(trRadiusX, brRadiusX); + return new Rect.fromLTRB( + left + leftRadius, + top, + right - rightRadius, + bottom + ); + } + + /// Whether this rounded rectangle encloses a non-zero area. + /// Negative areas are considered empty. + bool get isEmpty => left >= right || top >= bottom; + + /// Whether all coordinates of this rounded rectangle are finite. + bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; + + /// Whether this rounded rectangle is a simple rectangle with zero + /// corner radii. + bool get isRect { + return (tlRadiusX == 0.0 || tlRadiusY == 0.0) && + (trRadiusX == 0.0 || trRadiusY == 0.0) && + (blRadiusX == 0.0 || blRadiusY == 0.0) && + (brRadiusX == 0.0 || brRadiusY == 0.0); + } + + /// Whether this rounded rectangle has a side with no straight section. + bool get isStadium { + return tlRadius == trRadius + && trRadius == brRadius + && brRadius == blRadius + && (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); + } + + /// Whether this rounded rectangle has no side with a straight section. + bool get isEllipse { + return tlRadius == trRadius + && trRadius == brRadius + && brRadius == blRadius + && width <= 2.0 * tlRadiusX + && height <= 2.0 * tlRadiusY; + } + + /// Whether this rounded rectangle would draw as a circle. + bool get isCircle => width == height && isEllipse; + + /// The lesser of the magnitudes of the [width] and the [height] of this + /// rounded rectangle. + double get shortestSide => math.min(width.abs(), height.abs()); + + /// The greater of the magnitudes of the [width] and the [height] of this + /// rounded rectangle. + double get longestSide => math.max(width.abs(), height.abs()); + + + /// The offset to the point halfway between the left and right and the top and + /// bottom edges of this rectangle. + Offset get center => new Offset(left + width / 2.0, top + height / 2.0); + + // Returns the minimum between min and scale to which radius1 and radius2 + // should be scaled with in order not to exceed the limit. + double _getMin(double min, double radius1, double radius2, double limit) { + final double sum = radius1 + radius2; + if (sum > limit && sum != 0.0) + return math.min(min, limit / sum); + return min; + } + + // Scales all radii so that on each side their sum will not pass the size of + // the width/height. + // + // Inspired from: + // https://github.com/google/skia/blob/master/src/core/SkRRect.cpp#L164 + void _scaleRadii() { + if (_scaled == null) { + double scale = 1.0; + final List scaled = new List.from(_value); + + scale = _getMin(scale, scaled[11], scaled[5], height); + scale = _getMin(scale, scaled[4], scaled[6], width); + scale = _getMin(scale, scaled[7], scaled[9], height); + scale = _getMin(scale, scaled[8], scaled[10], width); + + if (scale < 1.0) { + for (int i = 4; i < _kDataSize; i += 1) + scaled[i] *= scale; + } + + _scaled = new RRect._fromList(scaled); + } + } + + /// Whether the point specified by the given offset (which is assumed to be + /// relative to the origin) lies inside the rounded rectangle. + /// + /// This method may allocate (and cache) a copy of the object with normalized + /// radii the first time it is called on a particular [RRect] instance. When + /// using this method, prefer to reuse existing [RRect]s rather than + /// recreating the object each time. + bool contains(Offset point) { + if (point.dx < left || point.dx >= right || point.dy < top || point.dy >= bottom) + return false; // outside bounding box + + _scaleRadii(); + + double x; + double y; + double radiusX; + double radiusY; + // check whether point is in one of the rounded corner areas + // x, y -> translate to ellipse center + if (point.dx < left + _scaled.tlRadiusX && + point.dy < top + _scaled.tlRadiusY) { + x = point.dx - left - _scaled.tlRadiusX; + y = point.dy - top - _scaled.tlRadiusY; + radiusX = _scaled.tlRadiusX; + radiusY = _scaled.tlRadiusY; + } else if (point.dx > right - _scaled.trRadiusX && + point.dy < top + _scaled.trRadiusY) { + x = point.dx - right + _scaled.trRadiusX; + y = point.dy - top - _scaled.trRadiusY; + radiusX = _scaled.trRadiusX; + radiusY = _scaled.trRadiusY; + } else if (point.dx > right - _scaled.brRadiusX && + point.dy > bottom - _scaled.brRadiusY) { + x = point.dx - right + _scaled.brRadiusX; + y = point.dy - bottom + _scaled.brRadiusY; + radiusX = _scaled.brRadiusX; + radiusY = _scaled.brRadiusY; + } else if (point.dx < left + _scaled.blRadiusX && + point.dy > bottom - _scaled.blRadiusY) { + x = point.dx - left - _scaled.blRadiusX; + y = point.dy - bottom + _scaled.blRadiusY; + radiusX = _scaled.blRadiusX; + radiusY = _scaled.blRadiusY; + } else { + return true; // inside and not within the rounded corner area + } + + x = x / radiusX; + y = y / radiusY; + // check if the point is outside the unit circle + if (x * x + y * y > 1.0) + return false; + return true; + } + + /// Linearly interpolate between two rounded rectangles. + /// + /// If either is null, this function substitutes [RRect.zero] instead. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) { + return new RRect._fromList([ + b.left * t, + b.top * t, + b.right * t, + b.bottom * t, + b.tlRadiusX * t, + b.tlRadiusY * t, + b.trRadiusX * t, + b.trRadiusY * t, + b.brRadiusX * t, + b.brRadiusY * t, + b.blRadiusX * t, + b.blRadiusY * t, + ]); + } + if (b == null) { + final double k = 1.0 - t; + return new RRect._fromList([ + a.left * k, + a.top * k, + a.right * k, + a.bottom * k, + a.tlRadiusX * k, + a.tlRadiusY * k, + a.trRadiusX * k, + a.trRadiusY * k, + a.brRadiusX * k, + a.brRadiusY * k, + a.blRadiusX * k, + a.blRadiusY * k, + ]); + } + return new RRect._fromList([ + lerpDouble(a.left, b.left, t), + lerpDouble(a.top, b.top, t), + lerpDouble(a.right, b.right, t), + lerpDouble(a.bottom, b.bottom, t), + lerpDouble(a.tlRadiusX, b.tlRadiusX, t), + lerpDouble(a.tlRadiusY, b.tlRadiusY, t), + lerpDouble(a.trRadiusX, b.trRadiusX, t), + lerpDouble(a.trRadiusY, b.trRadiusY, t), + lerpDouble(a.brRadiusX, b.brRadiusX, t), + lerpDouble(a.brRadiusY, b.brRadiusY, t), + lerpDouble(a.blRadiusX, b.blRadiusX, t), + lerpDouble(a.blRadiusY, b.blRadiusY, t), + ]); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (runtimeType != other.runtimeType) + return false; + final RRect typedOther = other; + for (int i = 0; i < _kDataSize; i += 1) { + if (_value[i] != typedOther._value[i]) + return false; + } + return true; + } + + @override + int get hashCode => hashList(_value); + + @override + String toString() { + final String rect = '${left.toStringAsFixed(1)}, ' + '${top.toStringAsFixed(1)}, ' + '${right.toStringAsFixed(1)}, ' + '${bottom.toStringAsFixed(1)}'; + if (tlRadius == trRadius && + trRadius == brRadius && + brRadius == blRadius) { + if (tlRadius.x == tlRadius.y) + return 'RRect.fromLTRBR($rect, ${tlRadius.x.toStringAsFixed(1)})'; + return 'RRect.fromLTRBXY($rect, ${tlRadius.x.toStringAsFixed(1)}, ${tlRadius.y.toStringAsFixed(1)})'; + } + return 'RRect.fromLTRBAndCorners(' + '$rect, ' + 'topLeft: $tlRadius, ' + 'topRight: $trRadius, ' + 'bottomRight: $brRadius, ' + 'bottomLeft: $blRadius' + ')'; + } +} + +/// A transform consisting of a translation, a rotation, and a uniform scale. +/// +/// Used by [Canvas.drawAtlas]. This is a more efficient way to represent these +/// simple transformations than a full matrix. +// Modeled after Skia's SkRSXform. +class RSTransform { + /// Creates an RSTransform. + /// + /// An [RSTransform] expresses the combination of a translation, a rotation + /// around a particular point, and a scale factor. + /// + /// The first argument, `scos`, is the cosine of the rotation, multiplied by + /// the scale factor. + /// + /// The second argument, `ssin`, is the sine of the rotation, multiplied by + /// that same scale factor. + /// + /// The third argument is the x coordinate of the translation, minus the + /// `scos` argument multiplied by the x-coordinate of the rotation point, plus + /// the `ssin` argument multiplied by the y-coordinate of the rotation point. + /// + /// The fourth argument is the y coordinate of the translation, minus the `ssin` + /// argument multiplied by the x-coordinate of the rotation point, minus the + /// `scos` argument multiplied by the y-coordinate of the rotation point. + /// + /// The [new RSTransform.fromComponents] method may be a simpler way to + /// construct these values. However, if there is a way to factor out the + /// computations of the sine and cosine of the rotation so that they can be + /// reused over multiple calls to this constructor, it may be more efficient + /// to directly use this constructor instead. + RSTransform(double scos, double ssin, double tx, double ty) { + _value + ..[0] = scos + ..[1] = ssin + ..[2] = tx + ..[3] = ty; + } + + /// Creates an RSTransform from its individual components. + /// + /// The `rotation` parameter gives the rotation in radians. + /// + /// The `scale` parameter describes the uniform scale factor. + /// + /// The `anchorX` and `anchorY` parameters give the coordinate of the point + /// around which to rotate. + /// + /// The `translateX` and `translateY` parameters give the coordinate of the + /// offset by which to translate. + /// + /// This constructor computes the arguments of the [new RSTransform] + /// constructor and then defers to that constructor to actually create the + /// object. If many [RSTransform] objects are being created and there is a way + /// to factor out the computations of the sine and cosine of the rotation + /// (which are computed each time this constructor is called) and reuse them + /// over multiple [RSTransform] objects, it may be more efficient to directly + /// use the more direct [new RSTransform] constructor instead. + factory RSTransform.fromComponents({ + double rotation, + double scale, + double anchorX, + double anchorY, + double translateX, + double translateY + }) { + final double scos = math.cos(rotation) * scale; + final double ssin = math.sin(rotation) * scale; + final double tx = translateX + -scos * anchorX + ssin * anchorY; + final double ty = translateY + -ssin * anchorX - scos * anchorY; + return new RSTransform(scos, ssin, tx, ty); + } + + final Float32List _value = new Float32List(4); + + /// The cosine of the rotation multiplied by the scale factor. + double get scos => _value[0]; + + /// The sine of the rotation multiplied by that same scale factor. + double get ssin => _value[1]; + + /// The x coordinate of the translation, minus [scos] multiplied by the + /// x-coordinate of the rotation point, plus [ssin] multiplied by the + /// y-coordinate of the rotation point. + double get tx => _value[2]; + + /// The y coordinate of the translation, minus [ssin] multiplied by the + /// x-coordinate of the rotation point, minus [scos] multiplied by the + /// y-coordinate of the rotation point. + double get ty => _value[3]; +} diff --git a/lib/stub_ui/hash_codes.dart b/lib/stub_ui/hash_codes.dart new file mode 100644 index 0000000000000..a3a24d7b7dd54 --- /dev/null +++ b/lib/stub_ui/hash_codes.dart @@ -0,0 +1,121 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +class _HashEnd { const _HashEnd(); } +const _HashEnd _hashEnd = const _HashEnd(); + +/// Jenkins hash function, optimized for small integers. +// +// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart. +class _Jenkins { + static int combine(int hash, Object o) { + assert(o is! Iterable); + hash = 0x1fffffff & (hash + o.hashCode); + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// Combine up to twenty objects' hash codes into one value. +/// +/// If you only need to handle one object's hash code, then just refer to its +/// [Object.hashCode] getter directly. +/// +/// If you need to combine an arbitrary number of objects from a [List] or other +/// [Iterable], use [hashList]. The output of [hashList] can be used as one of +/// the arguments to this function. +/// +/// For example: +/// +/// ```dart +/// int hashCode => hashValues(foo, bar, hashList(quux), baz); +/// ``` +int hashValues( + Object arg01, Object arg02, [ Object arg03 = _hashEnd, + Object arg04 = _hashEnd, Object arg05 = _hashEnd, Object arg06 = _hashEnd, + Object arg07 = _hashEnd, Object arg08 = _hashEnd, Object arg09 = _hashEnd, + Object arg10 = _hashEnd, Object arg11 = _hashEnd, Object arg12 = _hashEnd, + Object arg13 = _hashEnd, Object arg14 = _hashEnd, Object arg15 = _hashEnd, + Object arg16 = _hashEnd, Object arg17 = _hashEnd, Object arg18 = _hashEnd, + Object arg19 = _hashEnd, Object arg20 = _hashEnd ]) { + int result = 0; + result = _Jenkins.combine(result, arg01); + result = _Jenkins.combine(result, arg02); + if (arg03 != _hashEnd) { + result = _Jenkins.combine(result, arg03); + if (arg04 != _hashEnd) { + result = _Jenkins.combine(result, arg04); + if (arg05 != _hashEnd) { + result = _Jenkins.combine(result, arg05); + if (arg06 != _hashEnd) { + result = _Jenkins.combine(result, arg06); + if (arg07 != _hashEnd) { + result = _Jenkins.combine(result, arg07); + if (arg08 != _hashEnd) { + result = _Jenkins.combine(result, arg08); + if (arg09 != _hashEnd) { + result = _Jenkins.combine(result, arg09); + if (arg10 != _hashEnd) { + result = _Jenkins.combine(result, arg10); + if (arg11 != _hashEnd) { + result = _Jenkins.combine(result, arg11); + if (arg12 != _hashEnd) { + result = _Jenkins.combine(result, arg12); + if (arg13 != _hashEnd) { + result = _Jenkins.combine(result, arg13); + if (arg14 != _hashEnd) { + result = _Jenkins.combine(result, arg14); + if (arg15 != _hashEnd) { + result = _Jenkins.combine(result, arg15); + if (arg16 != _hashEnd) { + result = _Jenkins.combine(result, arg16); + if (arg17 != _hashEnd) { + result = _Jenkins.combine(result, arg17); + if (arg18 != _hashEnd) { + result = _Jenkins.combine(result, arg18); + if (arg19 != _hashEnd) { + result = _Jenkins.combine(result, arg19); + if (arg20 != _hashEnd) { + result = _Jenkins.combine(result, arg20); + // I can see my house from here! + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return _Jenkins.finish(result); +} + +/// Combine the [Object.hashCode] values of an arbitrary number of objects from +/// an [Iterable] into one value. This function will return the same value if +/// given null as if given an empty list. +int hashList(Iterable arguments) { + int result = 0; + if (arguments != null) { + for (Object argument in arguments) + result = _Jenkins.combine(result, argument); + } + return _Jenkins.finish(result); +} diff --git a/lib/stub_ui/hooks.dart b/lib/stub_ui/hooks.dart new file mode 100644 index 0000000000000..4ea381bfb8d34 --- /dev/null +++ b/lib/stub_ui/hooks.dart @@ -0,0 +1,259 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + +part of dart.ui; + +// ignore: unused_element +String _decodeUTF8(ByteData message) { + return message != null ? utf8.decoder.convert(message.buffer.asUint8List()) : null; +} + +// ignore: unused_element +dynamic _decodeJSON(String message) { + return message != null ? json.decode(message) : null; +} + +// ignore: unused_element +void _updateWindowMetrics(double devicePixelRatio, + double width, + double height, + double paddingTop, + double paddingRight, + double paddingBottom, + double paddingLeft, + double viewInsetTop, + double viewInsetRight, + double viewInsetBottom, + double viewInsetLeft) { + window + .._devicePixelRatio = devicePixelRatio + .._physicalSize = new Size(width, height) + .._padding = new WindowPadding._( + top: paddingTop, + right: paddingRight, + bottom: paddingBottom, + left: paddingLeft) + .._viewInsets = new WindowPadding._( + top: viewInsetTop, + right: viewInsetRight, + bottom: viewInsetBottom, + left: viewInsetLeft); + _invoke(window.onMetricsChanged, window._onMetricsChangedZone); +} + +typedef _LocaleClosure = String Function(); + +String _localeClosure() { + if (window.locale == null) { + return null; + } + return window.locale.toString(); +} + +// ignore: unused_element +_LocaleClosure _getLocaleClosure() => _localeClosure; + +// ignore: unused_element +void _updateLocales(List locales) { + const int stringsPerLocale = 4; + final int numLocales = locales.length ~/ stringsPerLocale; + window._locales = new List(numLocales); + for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { + final String countryCode = locales[localeIndex * stringsPerLocale + 1]; + final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; + + window._locales[localeIndex] = new Locale.fromSubtags( + languageCode: locales[localeIndex * stringsPerLocale], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + ); + } + _invoke(window.onLocaleChanged, window._onLocaleChangedZone); +} + +// ignore: unused_element +void _updateUserSettingsData(String jsonData) { + final Map data = json.decode(jsonData); + if (data.isEmpty) { + return; + } + _updateTextScaleFactor(data['textScaleFactor'].toDouble()); + _updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat']); +} + +void _updateLifecycleState(String state) { + window._initialLifecycleState ??= state; +} + +void _updateTextScaleFactor(double textScaleFactor) { + window._textScaleFactor = textScaleFactor; + _invoke(window.onTextScaleFactorChanged, window._onTextScaleFactorChangedZone); +} + +void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) { + window._alwaysUse24HourFormat = alwaysUse24HourFormat; +} + +// ignore: unused_element +void _updateSemanticsEnabled(bool enabled) { + window._semanticsEnabled = enabled; + _invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone); +} + +// ignore: unused_element +void _updateAccessibilityFeatures(int values) { + final AccessibilityFeatures newFeatures = new AccessibilityFeatures._(values); + if (newFeatures == window._accessibilityFeatures) + return; + window._accessibilityFeatures = newFeatures; + _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFlagsChangedZone); +} + +// ignore: unused_element +void _dispatchPointerDataPacket(ByteData packet) { + if (window.onPointerDataPacket != null) + _invoke1(window.onPointerDataPacket, window._onPointerDataPacketZone, _unpackPointerDataPacket(packet)); +} + +// ignore: unused_element +void _dispatchSemanticsAction(int id, int action, ByteData args) { + _invoke3( + window.onSemanticsAction, + window._onSemanticsActionZone, + id, + SemanticsAction.values[action], + args, + ); +} + +// ignore: unused_element +void _beginFrame(int microseconds) { + _invoke1(window.onBeginFrame, window._onBeginFrameZone, new Duration(microseconds: microseconds)); +} + +// ignore: unused_element +void _drawFrame() { + _invoke(window.onDrawFrame, window._onDrawFrameZone); +} + +// ignore: unused_element +void _runMainZoned(Function startMainIsolateFunction, Function userMainFunction) { + startMainIsolateFunction((){ + runZoned>(() { + userMainFunction(); + }, onError: (Object error, StackTrace stackTrace) { + _reportUnhandledException(error.toString(), stackTrace.toString()); + }); + }, null); +} + +void _reportUnhandledException(String error, String stackTrace) { + throw UnimplementedError(); +} + +/// 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] and [arg2]. +// ignore: unused_element +void _invoke2(void callback(A1 a1, A2 a2), Zone zone, A1 arg1, A2 arg2) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2); + } else { + zone.runBinaryGuarded(callback, arg1, arg2); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2] and [arg3]. +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); + }); + } +} + +// If this value changes, update the encoding code in the following files: +// +// * pointer_data.cc +// * FlutterView.java +const int _kPointerDataFieldCount = 24; + +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 = new List(length); + for (int i = 0; i < length; ++i) { + int offset = i * _kPointerDataFieldCount; + data[i] = new PointerData( + timeStamp: new 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), + physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), + obscured: 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 new PointerDataPacket(data: data); +} diff --git a/lib/stub_ui/isolate_name_server.dart b/lib/stub_ui/isolate_name_server.dart new file mode 100644 index 0000000000000..b346dc8f32aa9 --- /dev/null +++ b/lib/stub_ui/isolate_name_server.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. + +part of dart.ui; + +/// Static methods to allow for simple sharing of [SendPort]s across [Isolate]s. +/// +/// All isolates share a global mapping of names to ports. An isolate can +/// register a [SendPort] with a given name using [registerPortWithName]; +/// another isolate can then look up that port using [lookupPortByName]. +/// +/// To create a [SendPort], first create a [ReceivePort], then use +/// [ReceivePort.sendPort]. +/// +/// Since multiple isolates can each obtain the same [SendPort] associated with +/// a particular [ReceivePort], the protocol built on top of this mechanism +/// should typically consist of a single message. If more elaborate two-way +/// communication or multiple-message communication is necessary, it is +/// recommended to establish a separate communication channel in that first +/// message (e.g. by passing a dedicated [SendPort]). +class IsolateNameServer { + // This class is only a namespace, and should not be instantiated or + // extended directly. + factory IsolateNameServer._() => null; + + /// Looks up the [SendPort] associated with a given name. + /// + /// Returns null if the name does not exist. To register the name in the first + /// place, consider [registerPortWithName]. + /// + /// The `name` argument must not be null. + static SendPort lookupPortByName(String name) { + assert(name != null, "'name' cannot be null."); + throw UnimplementedError(); + } + + /// Registers a [SendPort] with a given name. + /// + /// Returns true if registration is successful, and false if the name entry + /// already existed (in which case the earlier registration is left + /// unchanged). To remove a registration, consider [removePortNameMapping]. + /// + /// Once a port has been registered with a name, it can be obtained from any + /// [Isolate] using [lookupPortByName]. + /// + /// Multiple isolates should avoid attempting to register ports with the same + /// name, as there is an inherent race condition in doing so. + /// + /// The `port` and `name` arguments must not be null. + static bool registerPortWithName(SendPort port, String name) { + assert(port != null, "'port' cannot be null."); + assert(name != null, "'name' cannot be null."); + throw UnimplementedError(); + } + + /// Removes a name-to-[SendPort] mapping given its name. + /// + /// Returns true if the mapping was successfully removed, false if the mapping + /// did not exist. To add a registration, consider [registerPortWithName]. + /// + /// Generally, removing a port name mapping is an inherently racy operation + /// (another isolate could have obtained the name just prior to the name being + /// removed, and thus would still be able to communicate over the port even + /// after it has been removed). + /// + /// The `name` argument must not be null. + static bool removePortNameMapping(String name) { + assert(name != null, "'name' cannot be null."); + throw UnimplementedError(); + } +} diff --git a/lib/stub_ui/lerp.dart b/lib/stub_ui/lerp.dart new file mode 100644 index 0000000000000..0c69476779b73 --- /dev/null +++ b/lib/stub_ui/lerp.dart @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +/// Linearly interpolate between two numbers. +double lerpDouble(num a, num b, double t) { + if (a == null && b == null) + return null; + a ??= 0.0; + b ??= 0.0; + return a + (b - a) * t; +} diff --git a/lib/stub_ui/natives.dart b/lib/stub_ui/natives.dart new file mode 100644 index 0000000000000..9e46816e5f5b5 --- /dev/null +++ b/lib/stub_ui/natives.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. + +// TODO(dnfield): remove unused_element ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + +part of dart.ui; + +// A service protocol extension to schedule a frame to be rendered into the +// window. +Future _scheduleFrame( + String method, + Map parameters + ) async { + // Schedule the frame. + window.scheduleFrame(); + // Always succeed. + return new developer.ServiceExtensionResponse.result(json.encode({ + 'type': 'Success', + })); +} + +@pragma('vm:entry-point') +void _setupHooks() { // ignore: unused_element + assert(() { + // In debug mode, register the schedule frame extension. + developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); + return true; + }()); +} + +/// Returns runtime Dart compilation trace as a UTF-8 encoded memory buffer. +/// +/// The buffer contains a list of symbols compiled by the Dart JIT at runtime up to the point +/// when this function was called. This list can be saved to a text file and passed to tools +/// such as `flutter build` or Dart `gen_snapshot` in order to pre-compile this code offline. +/// +/// The list has one symbol per line of the following format: `,,\n`. +/// Here are some examples: +/// +/// ``` +/// dart:core,Duration,get:inMilliseconds +/// package:flutter/src/widgets/binding.dart,::,runApp +/// file:///.../my_app.dart,::,main +/// ``` +/// +/// This function is only effective in debug and dynamic modes, and will throw in AOT mode. +List saveCompilationTrace() { + throw UnimplementedError(); +} diff --git a/lib/stub_ui/painting.dart b/lib/stub_ui/painting.dart new file mode 100644 index 0000000000000..ce60d7bb130df --- /dev/null +++ b/lib/stub_ui/painting.dart @@ -0,0 +1,3521 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +// Some methods in this file assert that their arguments are not null. These +// asserts are just to improve the error messages; they should only cover +// arguments that are either dereferenced _in Dart_, before being passed to the +// engine, or that the engine explicitly null-checks itself (after attempting to +// convert the argument to a native type). It should not be possible for a null +// or invalid value to be used by the engine even in release mode, since that +// would cause a crash. It is, however, acceptable for error messages to be much +// less useful or correct in release mode than in debug mode. +// +// Painting APIs will also warn about arguments representing NaN coordinates, +// which can not be rendered by Skia. + +// Update this list when changing the list of supported codecs. +/// {@template flutter.dart:ui.imageFormats} +/// JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, and WBMP +/// {@endtemplate} + +bool _rectIsValid(Rect rect) { + assert(rect != null, 'Rect argument was null.'); + assert(!rect._value.any((double value) => value.isNaN), 'Rect argument contained a NaN value.'); + return true; +} + +bool _rrectIsValid(RRect rrect) { + assert(rrect != null, 'RRect argument was null.'); + assert(!rrect._value.any((double value) => value.isNaN), 'RRect argument contained a NaN value.'); + return true; +} + +bool _offsetIsValid(Offset offset) { + 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.'); + assert(matrix4.length == 16, 'Matrix4 must have 16 entries.'); + return true; +} + +bool _radiusIsValid(Radius radius) { + assert(radius != null, 'Radius argument was null.'); + assert(!radius.x.isNaN && !radius.y.isNaN, 'Radius argument contained a NaN value.'); + return true; +} + +Color _scaleAlpha(Color a, double factor) { + return a.withAlpha((a.alpha * factor).round().clamp(0, 255)); +} + +/// An immutable 32 bit color value in ARGB format. +/// +/// Consider the light teal of the Flutter logo. It is fully opaque, with a red +/// channel value of 0x42 (66), a green channel value of 0xA5 (165), and a blue +/// channel value of 0xF5 (245). In the common "hash syntax" for color values, +/// it would be described as `#42A5F5`. +/// +/// Here are some ways it could be constructed: +/// +/// ```dart +/// Color c = const Color(0xFF42A5F5); +/// Color c = const Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5); +/// Color c = const Color.fromARGB(255, 66, 165, 245); +/// Color c = const Color.fromRGBO(66, 165, 245, 1.0); +/// ``` +/// +/// If you are having a problem with `Color` wherein it seems your color is just +/// not painting, check to make sure you are specifying the full 8 hexadecimal +/// digits. If you only specify six, then the leading two digits are assumed to +/// be zero, which means fully-transparent: +/// +/// ```dart +/// Color c1 = const Color(0xFFFFFF); // fully transparent white (invisible) +/// Color c2 = const Color(0xFFFFFFFF); // fully opaque white (visible) +/// ``` +/// +/// See also: +/// +/// * [Colors](https://docs.flutter.io/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]. + /// + /// The bits are interpreted as follows: + /// + /// * Bits 24-31 are the alpha value. + /// * Bits 16-23 are the red value. + /// * Bits 8-15 are the green value. + /// * Bits 0-7 are the blue value. + /// + /// In other words, if AA is the alpha value in hex, RR the red value in hex, + /// GG the green value in hex, and BB the blue value in hex, a color can be + /// expressed as `const Color(0xAARRGGBB)`. + /// + /// For example, to get a fully opaque orange, you would use `const + /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the + /// green, and `00` for the blue). + @pragma('vm:entry-point') + const Color(int value) : value = value & 0xFFFFFFFF; + + /// Construct a color from the lower 8 bits of four integers. + /// + /// * `a` is the alpha value, with 0 being transparent and 255 being fully + /// opaque. + /// * `r` is [red], from 0 to 255. + /// * `g` is [green], from 0 to 255. + /// * `b` is [blue], from 0 to 255. + /// + /// Out of range values are brought into range using modulo 255. + /// + /// See also [fromRGBO], which takes the alpha value as a floating point + /// value. + const Color.fromARGB(int a, int r, int g, int b) : + value = (((a & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff) << 0)) & 0xFFFFFFFF; + + /// Create a color from red, green, blue, and opacity, similar to `rgba()` in CSS. + /// + /// * `r` is [red], from 0 to 255. + /// * `g` is [green], from 0 to 255. + /// * `b` is [blue], from 0 to 255. + /// * `opacity` is alpha channel of this color as a double, with 0.0 being + /// transparent and 1.0 being fully opaque. + /// + /// Out of range values are brought into range using modulo 255. + /// + /// See also [fromARGB], which takes the opacity as an integer value. + const Color.fromRGBO(int r, int g, int b, double opacity) : + value = ((((opacity * 0xff ~/ 1) & 0xff) << 24) | + ((r & 0xff) << 16) | + ((g & 0xff) << 8) | + ((b & 0xff) << 0)) & 0xFFFFFFFF; + + /// A 32 bit value representing this color. + /// + /// The bits are assigned as follows: + /// + /// * Bits 24-31 are the alpha value. + /// * Bits 16-23 are the red value. + /// * Bits 8-15 are the green value. + /// * Bits 0-7 are the blue value. + final int value; + + /// The alpha channel of this color in an 8 bit value. + /// + /// A value of 0 means this color is fully transparent. A value of 255 means + /// this color is fully opaque. + int get alpha => (0xff000000 & value) >> 24; + + /// The alpha channel of this color as a double. + /// + /// A value of 0.0 means this color is fully transparent. A value of 1.0 means + /// this color is fully opaque. + double get opacity => alpha / 0xFF; + + /// The red channel of this color in an 8 bit value. + int get red => (0x00ff0000 & value) >> 16; + + /// The green channel of this color in an 8 bit value. + int get green => (0x0000ff00 & value) >> 8; + + /// The blue channel of this color in an 8 bit value. + int get blue => (0x000000ff & value) >> 0; + + /// Returns a new color that matches this color with the alpha channel + /// replaced with `a` (which ranges from 0 to 255). + /// + /// Out of range values will have unexpected effects. + Color withAlpha(int a) { + return new Color.fromARGB(a, red, green, blue); + } + + /// Returns a new color that matches this color with the alpha channel + /// replaced with the given `opacity` (which ranges from 0.0 to 1.0). + /// + /// Out of range values will have unexpected effects. + Color withOpacity(double opacity) { + assert(opacity >= 0.0 && opacity <= 1.0); + return withAlpha((255.0 * opacity).round()); + } + + /// Returns a new color that matches this color with the red channel replaced + /// with `r` (which ranges from 0 to 255). + /// + /// Out of range values will have unexpected effects. + Color withRed(int r) { + return new Color.fromARGB(alpha, r, green, blue); + } + + /// Returns a new color that matches this color with the green channel + /// replaced with `g` (which ranges from 0 to 255). + /// + /// Out of range values will have unexpected effects. + Color withGreen(int g) { + return new Color.fromARGB(alpha, red, g, blue); + } + + /// Returns a new color that matches this color with the blue channel replaced + /// with `b` (which ranges from 0 to 255). + /// + /// Out of range values will have unexpected effects. + Color withBlue(int b) { + return new Color.fromARGB(alpha, red, green, b); + } + + // See + static double _linearizeColorComponent(double component) { + if (component <= 0.03928) + return component / 12.92; + return math.pow((component + 0.055) / 1.055, 2.4); + } + + /// Returns a brightness value between 0 for darkest and 1 for lightest. + /// + /// Represents the relative luminance of the color. This value is computationally + /// expensive to calculate. + /// + /// See . + double computeLuminance() { + // See + final double R = _linearizeColorComponent(red / 0xFF); + final double G = _linearizeColorComponent(green / 0xFF); + final double B = _linearizeColorComponent(blue / 0xFF); + return 0.2126 * R + 0.7152 * G + 0.0722 * B; + } + + /// Linearly interpolate between two colors. + /// + /// This is intended to be fast but as a result may be ugly. Consider + /// [HSVColor] or writing custom logic for interpolating colors. + /// + /// If either color is null, this function linearly interpolates from a + /// transparent instance of the other color. This is usually preferable to + /// interpolating from [material.Colors.transparent] (`const + /// Color(0x00000000)`), which is specifically transparent _black_. + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). Each channel + /// will be clamped to the range 0 to 255. + /// + /// 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); + if (a == null && b == null) + return null; + if (a == null) + return _scaleAlpha(b, t); + if (b == null) + return _scaleAlpha(a, 1.0 - t); + return new Color.fromARGB( + lerpDouble(a.alpha, b.alpha, t).toInt().clamp(0, 255), + lerpDouble(a.red, b.red, t).toInt().clamp(0, 255), + lerpDouble(a.green, b.green, t).toInt().clamp(0, 255), + lerpDouble(a.blue, b.blue, t).toInt().clamp(0, 255), + ); + } + + /// Combine the foreground color as a transparent color over top + /// of a background color, and return the resulting combined color. + /// + /// This uses standard alpha blending ("SRC over DST") rules to produce a + /// blended color from two colors. This can be used as a performance + /// enhancement when trying to avoid needless alpha blending compositing + /// operations for two things that are solid colors with the same shape, but + /// overlay each other: instead, just paint one with the combined color. + static Color alphaBlend(Color foreground, Color background) { + final int alpha = foreground.alpha; + if (alpha == 0x00) { // Foreground completely transparent. + return background; + } + final int invAlpha = 0xff - alpha; + int backAlpha = background.alpha; + if (backAlpha == 0xff) { // Opaque background case + return new Color.fromARGB( + 0xff, + (alpha * foreground.red + invAlpha * background.red) ~/ 0xff, + (alpha * foreground.green + invAlpha * background.green) ~/ 0xff, + (alpha * foreground.blue + invAlpha * background.blue) ~/ 0xff, + ); + } else { // General case + backAlpha = (backAlpha * invAlpha) ~/ 0xff; + final int outAlpha = alpha + backAlpha; + assert(outAlpha != 0x00); + return new Color.fromARGB( + outAlpha, + (foreground.red * alpha + background.red * backAlpha) ~/ outAlpha, + (foreground.green * alpha + background.green * backAlpha) ~/ outAlpha, + (foreground.blue * alpha + background.blue * backAlpha) ~/ outAlpha, + ); + } + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + final Color typedOther = other; + return value == typedOther.value; + } + + @override + int get hashCode => value.hashCode; + + @override + String toString() => 'Color(0x${value.toRadixString(16).padLeft(8, '0')})'; +} + +/// Algorithms to use when painting on the canvas. +/// +/// When drawing a shape or image onto a canvas, different algorithms can be +/// used to blend the pixels. The different values of [BlendMode] specify +/// different such algorithms. +/// +/// Each algorithm has two inputs, the _source_, which is the image being drawn, +/// and the _destination_, which is the image into which the source image is +/// being composited. The destination is often thought of as the _background_. +/// The source and destination both have four color channels, the red, green, +/// blue, and alpha channels. These are typically represented as numbers in the +/// range 0.0 to 1.0. The output of the algorithm also has these same four +/// channels, with values computed from the source and destination. +/// +/// The documentation of each value below describes how the algorithm works. In +/// each case, an image shows the output of blending a source image with a +/// destination image. In the images below, the destination is represented by an +/// image with horizontal lines and an opaque landscape photograph, and the +/// source is represented by an image with vertical lines (the same lines but +/// rotated) and a bird clip-art image. The [src] mode shows only the source +/// image, and the [dst] mode shows only the destination image. In the +/// documentation below, the transparency is illustrated by a checkerboard +/// pattern. The [clear] mode drops both the source and destination, resulting +/// in an output that is entirely transparent (illustrated by a solid +/// checkerboard pattern). +/// +/// The horizontal and vertical bars in these images show the red, green, and +/// blue channels with varying opacity levels, then all three color channels +/// together with those same varying opacity levels, then all three color +/// channels set to zero with those varying opacity levels, then two bars showing +/// a red/green/blue repeating gradient, the first with full opacity and the +/// second with partial opacity, and finally a bar with the three color channels +/// set to zero but the opacity varying in a repeating gradient. +/// +/// ## Application to the [Canvas] API +/// +/// When using [Canvas.saveLayer] and [Canvas.restore], the blend mode of the +/// [Paint] given to the [Canvas.saveLayer] will be applied when +/// [Canvas.restore] is called. Each call to [Canvas.saveLayer] introduces a new +/// layer onto which shapes and images are painted; when [Canvas.restore] is +/// called, that layer is then composited onto the parent layer, with the source +/// being the most-recently-drawn shapes and images, and the destination being +/// the parent layer. (For the first [Canvas.saveLayer] call, the parent layer +/// is the canvas itself.) +/// +/// See also: +/// +/// * [Paint.blendMode], which uses [BlendMode] to define the compositing +/// strategy. +enum BlendMode { + // This list comes from Skia's SkXfermode.h and the values (order) should be + // kept in sync. + // See: https://skia.org/user/api/skpaint#SkXfermode + + /// Drop both the source and destination images, leaving nothing. + /// + /// This corresponds to the "clear" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_clear.png) + clear, + + /// Drop the destination image, only paint the source image. + /// + /// Conceptually, the destination is first cleared, then the source image is + /// painted. + /// + /// This corresponds to the "Copy" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_src.png) + src, + + /// Drop the source image, only paint the destination image. + /// + /// Conceptually, the source image is discarded, leaving the destination + /// untouched. + /// + /// This corresponds to the "Destination" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dst.png) + dst, + + /// Composite the source image over the destination image. + /// + /// This is the default value. It represents the most intuitive case, where + /// shapes are painted on top of what is below, with transparent areas showing + /// the destination layer. + /// + /// This corresponds to the "Source over Destination" Porter-Duff operator, + /// also known as the Painter's Algorithm. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOver.png) + srcOver, + + /// Composite the source image under the destination image. + /// + /// This is the opposite of [srcOver]. + /// + /// This corresponds to the "Destination over Source" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOver.png) + /// + /// This is useful when the source image should have been painted before the + /// destination image, but could not be. + dstOver, + + /// Show the source image, but only where the two images overlap. The + /// destination image is not rendered, it is treated merely as a mask. The + /// color channels of the destination are ignored, only the opacity has an + /// effect. + /// + /// To show the destination image instead, consider [dstIn]. + /// + /// To reverse the semantic of the mask (only showing the source where the + /// destination is absent, rather than where it is present), consider + /// [srcOut]. + /// + /// This corresponds to the "Source in Destination" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcIn.png) + srcIn, + + /// Show the destination image, but only where the two images overlap. The + /// source image is not rendered, it is treated merely as a mask. The color + /// channels of the source are ignored, only the opacity has an effect. + /// + /// To show the source image instead, consider [srcIn]. + /// + /// To reverse the semantic of the mask (only showing the source where the + /// destination is present, rather than where it is absent), consider [dstOut]. + /// + /// This corresponds to the "Destination in Source" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstIn.png) + dstIn, + + /// Show the source image, but only where the two images do not overlap. The + /// destination image is not rendered, it is treated merely as a mask. The color + /// channels of the destination are ignored, only the opacity has an effect. + /// + /// To show the destination image instead, consider [dstOut]. + /// + /// To reverse the semantic of the mask (only showing the source where the + /// destination is present, rather than where it is absent), consider [srcIn]. + /// + /// This corresponds to the "Source out Destination" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcOut.png) + srcOut, + + /// Show the destination image, but only where the two images do not overlap. The + /// source image is not rendered, it is treated merely as a mask. The color + /// channels of the source are ignored, only the opacity has an effect. + /// + /// To show the source image instead, consider [srcOut]. + /// + /// To reverse the semantic of the mask (only showing the destination where the + /// source is present, rather than where it is absent), consider [dstIn]. + /// + /// This corresponds to the "Destination out Source" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstOut.png) + dstOut, + + /// Composite the source image over the destination image, but only where it + /// overlaps the destination. + /// + /// This corresponds to the "Source atop Destination" Porter-Duff operator. + /// + /// This is essentially the [srcOver] operator, but with the output's opacity + /// channel being set to that of the destination image instead of being a + /// combination of both image's opacity channels. + /// + /// For a variant with the destination on top instead of the source, see + /// [dstATop]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_srcATop.png) + srcATop, + + /// Composite the destination image over the source image, but only where it + /// overlaps the source. + /// + /// This corresponds to the "Destination atop Source" Porter-Duff operator. + /// + /// This is essentially the [dstOver] operator, but with the output's opacity + /// channel being set to that of the source image instead of being a + /// combination of both image's opacity channels. + /// + /// For a variant with the source on top instead of the destination, see + /// [srcATop]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_dstATop.png) + dstATop, + + /// Apply a bitwise `xor` operator to the source and destination images. This + /// leaves transparency where they would overlap. + /// + /// This corresponds to the "Source xor Destination" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_xor.png) + xor, + + /// Sum the components of the source and destination images. + /// + /// Transparency in a pixel of one of the images reduces the contribution of + /// that image to the corresponding output pixel, as if the color of that + /// pixel in that image was darker. + /// + /// This corresponds to the "Source plus Destination" Porter-Duff operator. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_plus.png) + plus, + + /// Multiply the color components of the source and destination images. + /// + /// This can only result in the same or darker colors (multiplying by white, + /// 1.0, results in no change; multiplying by black, 0.0, results in black). + /// + /// When compositing two opaque images, this has similar effect to overlapping + /// two transparencies on a projector. + /// + /// For a variant that also multiplies the alpha channel, consider [multiply]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_modulate.png) + /// + /// See also: + /// + /// * [screen], which does a similar computation but inverted. + /// * [overlay], which combines [modulate] and [screen] to favor the + /// destination image. + /// * [hardLight], which combines [modulate] and [screen] to favor the + /// source image. + modulate, + + // Following blend modes are defined in the CSS Compositing standard. + + /// Multiply the inverse of the components of the source and destination + /// images, and inverse the result. + /// + /// Inverting the components means that a fully saturated channel (opaque + /// white) is treated as the value 0.0, and values normally treated as 0.0 + /// (black, transparent) are treated as 1.0. + /// + /// This is essentially the same as [modulate] blend mode, but with the values + /// of the colors inverted before the multiplication and the result being + /// inverted back before rendering. + /// + /// This can only result in the same or lighter colors (multiplying by black, + /// 1.0, results in no change; multiplying by white, 0.0, results in white). + /// Similarly, in the alpha channel, it can only result in more opaque colors. + /// + /// This has similar effect to two projectors displaying their images on the + /// same screen simultaneously. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_screen.png) + /// + /// See also: + /// + /// * [modulate], which does a similar computation but without inverting the + /// values. + /// * [overlay], which combines [modulate] and [screen] to favor the + /// destination image. + /// * [hardLight], which combines [modulate] and [screen] to favor the + /// source image. + screen, // The last coeff mode. + + /// Multiply the components of the source and destination images after + /// adjusting them to favor the destination. + /// + /// Specifically, if the destination value is smaller, this multiplies it with + /// the source value, whereas is the source value is smaller, it multiplies + /// the inverse of the source value with the inverse of the destination value, + /// then inverts the result. + /// + /// Inverting the components means that a fully saturated channel (opaque + /// white) is treated as the value 0.0, and values normally treated as 0.0 + /// (black, transparent) are treated as 1.0. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_overlay.png) + /// + /// See also: + /// + /// * [modulate], which always multiplies the values. + /// * [screen], which always multiplies the inverses of the values. + /// * [hardLight], which is similar to [overlay] but favors the source image + /// instead of the destination image. + overlay, + + /// Composite the source and destination image by choosing the lowest value + /// from each color channel. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_darken.png) + darken, + + /// Composite the source and destination image by choosing the highest value + /// from each color channel. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_lighten.png) + lighten, + + /// Divide the destination by the inverse of the source. + /// + /// Inverting the components means that a fully saturated channel (opaque + /// white) is treated as the value 0.0, and values normally treated as 0.0 + /// (black, transparent) are treated as 1.0. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorDodge.png) + colorDodge, + + /// Divide the inverse of the destination by the the source, and inverse the result. + /// + /// Inverting the components means that a fully saturated channel (opaque + /// white) is treated as the value 0.0, and values normally treated as 0.0 + /// (black, transparent) are treated as 1.0. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_colorBurn.png) + colorBurn, + + /// Multiply the components of the source and destination images after + /// adjusting them to favor the source. + /// + /// Specifically, if the source value is smaller, this multiplies it with the + /// destination value, whereas is the destination value is smaller, it + /// multiplies the inverse of the destination value with the inverse of the + /// source value, then inverts the result. + /// + /// Inverting the components means that a fully saturated channel (opaque + /// white) is treated as the value 0.0, and values normally treated as 0.0 + /// (black, transparent) are treated as 1.0. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hardLight.png) + /// + /// See also: + /// + /// * [modulate], which always multiplies the values. + /// * [screen], which always multiplies the inverses of the values. + /// * [overlay], which is similar to [hardLight] but favors the destination + /// image instead of the source image. + hardLight, + + /// Use [colorDodge] for source values below 0.5 and [colorBurn] for source + /// values above 0.5. + /// + /// This results in a similar but softer effect than [overlay]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_softLight.png) + /// + /// See also: + /// + /// * [color], which is a more subtle tinting effect. + softLight, + + /// Subtract the smaller value from the bigger value for each channel. + /// + /// Compositing black has no effect; compositing white inverts the colors of + /// the other image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. + /// + /// The effect is similar to [exclusion] but harsher. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_difference.png) + difference, + + /// Subtract double the product of the two images from the sum of the two + /// images. + /// + /// Compositing black has no effect; compositing white inverts the colors of + /// the other image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. + /// + /// The effect is similar to [difference] but softer. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_exclusion.png) + exclusion, + + /// Multiply the components of the source and destination images, including + /// the alpha channel. + /// + /// This can only result in the same or darker colors (multiplying by white, + /// 1.0, results in no change; multiplying by black, 0.0, results in black). + /// + /// Since the alpha channel is also multiplied, a fully-transparent pixel + /// (opacity 0.0) in one image results in a fully transparent pixel in the + /// output. This is similar to [dstIn], but with the colors combined. + /// + /// For a variant that multiplies the colors but does not multiply the alpha + /// channel, consider [modulate]. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_multiply.png) + multiply, // The last separable mode. + + /// Take the hue of the source image, and the saturation and luminosity of the + /// destination image. + /// + /// The effect is to tint the destination image with the source image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. Regions that are entirely transparent in the source image take + /// their hue from the destination. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hue.png) + /// + /// See also: + /// + /// * [color], which is a similar but stronger effect as it also applies the + /// saturation of the source image. + /// * [HSVColor], which allows colors to be expressed using Hue rather than + /// the red/green/blue channels of [Color]. + hue, + + /// Take the saturation of the source image, and the hue and luminosity of the + /// destination image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. Regions that are entirely transparent in the source image take + /// their saturation from the destination. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_hue.png) + /// + /// See also: + /// + /// * [color], which also applies the hue of the source image. + /// * [luminosity], which applies the luminosity of the source image to the + /// destination. + saturation, + + /// Take the hue and saturation of the source image, and the luminosity of the + /// destination image. + /// + /// The effect is to tint the destination image with the source image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. Regions that are entirely transparent in the source image take + /// their hue and saturation from the destination. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_color.png) + /// + /// See also: + /// + /// * [hue], which is a similar but weaker effect. + /// * [softLight], which is a similar tinting effect but also tints white. + /// * [saturation], which only applies the saturation of the source image. + color, + + /// Take the luminosity of the source image, and the hue and saturation of the + /// destination image. + /// + /// The opacity of the output image is computed in the same way as for + /// [srcOver]. Regions that are entirely transparent in the source image take + /// their luminosity from the destination. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_luminosity.png) + /// + /// See also: + /// + /// * [saturation], which applies the saturation of the source image to the + /// destination. + /// * [ImageFilter.blur], which can be used with [BackdropFilter] for a + /// related effect. + luminosity, +} + +/// Quality levels for image filters. +/// +/// See [Paint.filterQuality]. +enum FilterQuality { + // This list comes from Skia's SkFilterQuality.h and the values (order) should + // be kept in sync. + + /// Fastest possible filtering, albeit also the lowest quality. + /// + /// Typically this implies nearest-neighbor filtering. + none, + + /// Better quality than [none], faster than [medium]. + /// + /// Typically this implies bilinear interpolation. + low, + + /// Better quality than [low], faster than [high]. + /// + /// Typically this implies a combination of bilinear interpolation and + /// pyramidal parametric pre-filtering (mipmaps). + medium, + + /// Best possible quality filtering, albeit also the slowest. + /// + /// Typically this implies bicubic interpolation or better. + high, +} + +/// Styles to use for line endings. +/// +/// See also: +/// +/// * [Paint.strokeCap] for how this value is used. +/// * [StrokeJoin] for the different kinds of line segment joins. +// These enum values must be kept in sync with SkPaint::Cap. +enum StrokeCap { + /// Begin and end contours with a flat edge and no extension. + /// + /// ![A butt cap ends line segments with a square end that stops at the end of + /// the line segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/butt_cap.png) + /// + /// Compare to the [square] cap, which has the same shape, but extends past + /// the end of the line by half a stroke width. + butt, + + /// Begin and end contours with a semi-circle extension. + /// + /// ![A round cap adds a rounded end to the line segment that protrudes + /// by one half of the thickness of the line (which is the radius of the cap) + /// past the end of the segment.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_cap.png) + /// + /// The cap is colored in the diagram above to highlight it: in normal use it + /// is the same color as the line. + round, + + /// Begin and end contours with a half square extension. This is + /// similar to extending each contour by half the stroke width (as + /// given by [Paint.strokeWidth]). + /// + /// ![A square cap has a square end that effectively extends the line length + /// by half of the stroke width.](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/square_cap.png) + /// + /// The cap is colored in the diagram above to highlight it: in normal use it + /// is the same color as the line. + /// + /// Compare to the [butt] cap, which has the same shape, but doesn't extend + /// past the end of the line. + square, +} + +/// Styles to use for line segment joins. +/// +/// This only affects line joins for polygons drawn by [Canvas.drawPath] and +/// rectangles, not points drawn as lines with [Canvas.drawPoints]. +/// +/// See also: +/// +/// * [Paint.strokeJoin] and [Paint.strokeMiterLimit] for how this value is +/// used. +/// * [StrokeCap] for the different kinds of line endings. +// These enum values must be kept in sync with SkPaint::Join. +enum StrokeJoin { + /// Joins between line segments form sharp corners. + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// + /// The center of the line segment is colored in the diagram above to + /// highlight the join, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [Paint.strokeJoin], used to set the line segment join style to this + /// value. + /// * [Paint.strokeMiterLimit], used to define when a miter is drawn instead + /// of a bevel when the join is set to this value. + miter, + + /// Joins between line segments are semi-circular. + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} + /// + /// The center of the line segment is colored in the diagram above to + /// highlight the join, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [Paint.strokeJoin], used to set the line segment join style to this + /// value. + round, + + /// Joins between line segments connect the corners of the butt ends of the + /// line segments to give a beveled appearance. + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} + /// + /// The center of the line segment is colored in the diagram above to + /// highlight the join, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [Paint.strokeJoin], used to set the line segment join style to this + /// value. + bevel, +} + +/// Strategies for painting shapes and paths on a canvas. +/// +/// See [Paint.style]. +// These enum values must be kept in sync with SkPaint::Style. +enum PaintingStyle { + // This list comes from Skia's SkPaint.h and the values (order) should be kept + // in sync. + + /// Apply the [Paint] to the inside of the shape. For example, when + /// applied to the [Canvas.drawCircle] call, this results in a disc + /// of the given size being painted. + fill, + + /// Apply the [Paint] to the edge of the shape. For example, when + /// applied to the [Canvas.drawCircle] call, this results is a hoop + /// of the given size being painted. The line drawn on the edge will + /// be the width given by the [Paint.strokeWidth] property. + stroke, +} + + +/// Different ways to clip a widget's content. +enum Clip { + /// No clip at all. + /// + /// This is the default option for most widgets: if the content does not + /// overflow the widget boundary, don't pay any performance cost for clipping. + /// + /// If the content does overflow, please explicitly specify the following + /// [Clip] options: + /// * [hardEdge], which is the fastest clipping, but with lower fidelity. + /// * [antiAlias], which is a little slower than [hardEdge], but with smoothed edges. + /// * [antiAliasWithSaveLayer], which is much slower than [antiAlias], and should + /// rarely be used. + none, + + /// Clip, but do not apply anti-aliasing. + /// + /// This mode enables clipping, but curves and non-axis-aligned straight lines will be + /// jagged as no effort is made to anti-alias. + /// + /// Faster than other clipping modes, but slower than [none]. + /// + /// This is a reasonable choice when clipping is needed, if the container is an axis- + /// aligned rectangle or an axis-aligned rounded rectangle with very small corner radii. + /// + /// See also: + /// + /// * [antiAlias], which is more reasonable when clipping is needed and the shape is not + /// an axis-aligned rectangle. + hardEdge, + + /// Clip with anti-aliasing. + /// + /// This mode has anti-aliased clipping edges to achieve a smoother look. + /// + /// It' s much faster than [antiAliasWithSaveLayer], but slower than [hardEdge]. + /// + /// This will be the common case when dealing with circles and arcs. + /// + /// Different from [hardEdge] and [antiAliasWithSaveLayer], this clipping may have + /// bleeding edge artifacts. + /// (See https://fiddle.skia.org/c/21cb4c2b2515996b537f36e7819288ae for an example.) + /// + /// See also: + /// + /// * [hardEdge], which is a little faster, but with lower fidelity. + /// * [antiAliasWithSaveLayer], which is much slower, but can avoid the + /// bleeding edges if there's no other way. + /// * [Paint.isAntiAlias], which is the anti-aliasing switch for general draw operations. + antiAlias, + + /// Clip with anti-aliasing and saveLayer immediately following the clip. + /// + /// This mode not only clips with anti-aliasing, but also allocates an offscreen + /// buffer. All subsequent paints are carried out on that buffer before finally + /// being clipped and composited back. + /// + /// This is very slow. It has no bleeding edge artifacts (that [antiAlias] has) + /// but it changes the semantics as an offscreen buffer is now introduced. + /// (See https://github.com/flutter/flutter/issues/18057#issuecomment-394197336 + /// for a difference between paint without saveLayer and paint with saveLayer.) + /// + /// This will be only rarely needed. One case where you might need this is if + /// you have an image overlaid on a very different background color. In these + /// cases, consider whether you can avoid overlaying multiple colors in one + /// spot (e.g. by having the background color only present where the image is + /// absent). If you can, [antiAlias] would be fine and much faster. + /// + /// See also: + /// + /// * [antiAlias], which is much faster, and has similar clipping results. + antiAliasWithSaveLayer, +} + +// If we actually run on big endian machines, we'll need to do something smarter +// here. We don't use [Endian.Host] because it's not a compile-time +// constant and can't propagate into the set/get calls. +const Endian _kFakeHostEndian = Endian.little; + +/// A description of the style to use when drawing on a [Canvas]. +/// +/// Most APIs on [Canvas] take a [Paint] object to describe the style +/// to use for that operation. +class Paint { + // Paint objects are encoded in two buffers: + // + // * _data is binary data in four-byte fields, each of which is either a + // uint32_t or a float. The default value for each field is encoded as + // zero to make initialization trivial. Most values already have a default + // value of zero, but some, such as color, have a non-zero default value. + // To encode or decode these values, XOR the value with the default value. + // + // * _objects is a list of unencodable objects, typically wrappers for native + // objects. The objects are simply stored in the list without any additional + // encoding. + // + // The binary format must match the deserialization code in paint.cc. + + final ByteData _data = new ByteData(_kDataByteCount); + static const int _kIsAntiAliasIndex = 0; + static const int _kColorIndex = 1; + static const int _kBlendModeIndex = 2; + static const int _kStyleIndex = 3; + static const int _kStrokeWidthIndex = 4; + static const int _kStrokeCapIndex = 5; + static const int _kStrokeJoinIndex = 6; + static const int _kStrokeMiterLimitIndex = 7; + static const int _kFilterQualityIndex = 8; + static const int _kColorFilterIndex = 9; + static const int _kColorFilterColorIndex = 10; + static const int _kColorFilterBlendModeIndex = 11; + static const int _kMaskFilterIndex = 12; + static const int _kMaskFilterBlurStyleIndex = 13; + static const int _kMaskFilterSigmaIndex = 14; + static const int _kInvertColorIndex = 15; + + static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2; + static const int _kColorOffset = _kColorIndex << 2; + static const int _kBlendModeOffset = _kBlendModeIndex << 2; + static const int _kStyleOffset = _kStyleIndex << 2; + static const int _kStrokeWidthOffset = _kStrokeWidthIndex << 2; + static const int _kStrokeCapOffset = _kStrokeCapIndex << 2; + static const int _kStrokeJoinOffset = _kStrokeJoinIndex << 2; + static const int _kStrokeMiterLimitOffset = _kStrokeMiterLimitIndex << 2; + static const int _kFilterQualityOffset = _kFilterQualityIndex << 2; + static const int _kColorFilterOffset = _kColorFilterIndex << 2; + static const int _kColorFilterColorOffset = _kColorFilterColorIndex << 2; + static const int _kColorFilterBlendModeOffset = _kColorFilterBlendModeIndex << 2; + static const int _kMaskFilterOffset = _kMaskFilterIndex << 2; + static const int _kMaskFilterBlurStyleOffset = _kMaskFilterBlurStyleIndex << 2; + static const int _kMaskFilterSigmaOffset = _kMaskFilterSigmaIndex << 2; + static const int _kInvertColorOffset = _kInvertColorIndex << 2; + // If you add more fields, remember to update _kDataByteCount. + static const int _kDataByteCount = 75; + + // Binary format must match the deserialization code in paint.cc. + List _objects; + static const int _kShaderIndex = 0; + static const int _kColorFilterMatrixIndex = 1; + static const int _kObjectCount = 2; // Must be one larger than the largest index. + + /// Whether to apply anti-aliasing to lines and images drawn on the + /// canvas. + /// + /// Defaults to true. + bool get isAntiAlias { + return _data.getInt32(_kIsAntiAliasOffset, _kFakeHostEndian) == 0; + } + set isAntiAlias(bool value) { + // We encode true as zero and false as one because the default value, which + // we always encode as zero, is true. + final int encoded = value ? 0 : 1; + _data.setInt32(_kIsAntiAliasOffset, encoded, _kFakeHostEndian); + } + + // Must be kept in sync with the default in paint.cc. + static const int _kColorDefault = 0xFF000000; + + /// The color to use when stroking or filling a shape. + /// + /// Defaults to opaque black. + /// + /// See also: + /// + /// * [style], which controls whether to stroke or fill (or both). + /// * [colorFilter], which overrides [color]. + /// * [shader], which overrides [color] with more elaborate effects. + /// + /// This color is not used when compositing. To colorize a layer, use + /// [colorFilter]. + Color get color { + final int encoded = _data.getInt32(_kColorOffset, _kFakeHostEndian); + return new Color(encoded ^ _kColorDefault); + } + set color(Color value) { + assert(value != null); + final int encoded = value.value ^ _kColorDefault; + _data.setInt32(_kColorOffset, encoded, _kFakeHostEndian); + } + + // Must be kept in sync with the default in paint.cc. + static final int _kBlendModeDefault = BlendMode.srcOver.index; + + /// A blend mode to apply when a shape is drawn or a layer is composited. + /// + /// The source colors are from the shape being drawn (e.g. from + /// [Canvas.drawPath]) or layer being composited (the graphics that were drawn + /// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying + /// the [colorFilter], if any. + /// + /// The destination colors are from the background onto which the shape or + /// layer is being composited. + /// + /// Defaults to [BlendMode.srcOver]. + /// + /// See also: + /// + /// * [Canvas.saveLayer], which uses its [Paint]'s [blendMode] to composite + /// the layer when [restore] is called. + /// * [BlendMode], which discusses the user of [saveLayer] with [blendMode]. + BlendMode get blendMode { + final int encoded = _data.getInt32(_kBlendModeOffset, _kFakeHostEndian); + return BlendMode.values[encoded ^ _kBlendModeDefault]; + } + set blendMode(BlendMode value) { + assert(value != null); + final int encoded = value.index ^ _kBlendModeDefault; + _data.setInt32(_kBlendModeOffset, encoded, _kFakeHostEndian); + } + + /// Whether to paint inside shapes, the edges of shapes, or both. + /// + /// Defaults to [PaintingStyle.fill]. + PaintingStyle get style { + return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)]; + } + set style(PaintingStyle value) { + assert(value != null); + final int encoded = value.index; + _data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian); + } + + /// How wide to make edges drawn when [style] is set to + /// [PaintingStyle.stroke]. The width is given in logical pixels measured in + /// the direction orthogonal to the direction of the path. + /// + /// Defaults to 0.0, which correspond to a hairline width. + double get strokeWidth { + return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian); + } + set strokeWidth(double value) { + assert(value != null); + final double encoded = value; + _data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian); + } + + /// The kind of finish to place on the end of lines drawn when + /// [style] is set to [PaintingStyle.stroke]. + /// + /// Defaults to [StrokeCap.butt], i.e. no caps. + StrokeCap get strokeCap { + return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)]; + } + set strokeCap(StrokeCap value) { + assert(value != null); + final int encoded = value.index; + _data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian); + } + + /// The kind of finish to place on the joins between segments. + /// + /// This applies to paths drawn when [style] is set to [PaintingStyle.stroke], + /// It does not apply to points drawn as lines with [Canvas.drawPoints]. + /// + /// Defaults to [StrokeJoin.miter], i.e. sharp corners. + /// + /// Some examples of joins: + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} + /// + /// The centers of the line segments are colored in the diagrams above to + /// highlight the joins, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [strokeMiterLimit] to control when miters are replaced by bevels when + /// this is set to [StrokeJoin.miter]. + /// * [strokeCap] to control what is drawn at the ends of the stroke. + /// * [StrokeJoin] for the definitive list of stroke joins. + StrokeJoin get strokeJoin { + return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)]; + } + set strokeJoin(StrokeJoin value) { + assert(value != null); + final int encoded = value.index; + _data.setInt32(_kStrokeJoinOffset, encoded, _kFakeHostEndian); + } + + // Must be kept in sync with the default in paint.cc. + static const double _kStrokeMiterLimitDefault = 4.0; + + /// The limit for miters to be drawn on segments when the join is set to + /// [StrokeJoin.miter] and the [style] is set to [PaintingStyle.stroke]. If + /// this limit is exceeded, then a [StrokeJoin.bevel] join will be drawn + /// instead. This may cause some 'popping' of the corners of a path if the + /// angle between line segments is animated, as seen in the diagrams below. + /// + /// This limit is expressed as a limit on the length of the miter. + /// + /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel] + /// join to be used all the time. + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4} + /// + /// The centers of the line segments are colored in the diagrams above to + /// highlight the joins, but in normal usage the join is the same color as the + /// line. + /// + /// See also: + /// + /// * [strokeJoin] to control the kind of finish to place on the joins + /// between segments. + /// * [strokeCap] to control what is drawn at the ends of the stroke. + double get strokeMiterLimit { + return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian); + } + set strokeMiterLimit(double value) { + assert(value != null); + final double encoded = value - _kStrokeMiterLimitDefault; + _data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian); + } + + /// A mask filter (for example, a blur) to apply to a shape after it has been + /// drawn but before it has been composited into the image. + /// + /// See [MaskFilter] for details. + MaskFilter get maskFilter { + switch (_data.getInt32(_kMaskFilterOffset, _kFakeHostEndian)) { + case MaskFilter._TypeNone: + return null; + case MaskFilter._TypeBlur: + return new MaskFilter.blur( + BlurStyle.values[_data.getInt32(_kMaskFilterBlurStyleOffset, _kFakeHostEndian)], + _data.getFloat32(_kMaskFilterSigmaOffset, _kFakeHostEndian), + ); + } + return null; + } + set maskFilter(MaskFilter value) { + if (value == null) { + _data.setInt32(_kMaskFilterOffset, MaskFilter._TypeNone, _kFakeHostEndian); + _data.setInt32(_kMaskFilterBlurStyleOffset, 0, _kFakeHostEndian); + _data.setFloat32(_kMaskFilterSigmaOffset, 0.0, _kFakeHostEndian); + } else { + // For now we only support one kind of MaskFilter, so we don't need to + // check what the type is if it's not null. + _data.setInt32(_kMaskFilterOffset, MaskFilter._TypeBlur, _kFakeHostEndian); + _data.setInt32(_kMaskFilterBlurStyleOffset, value._style.index, _kFakeHostEndian); + _data.setFloat32(_kMaskFilterSigmaOffset, value._sigma, _kFakeHostEndian); + } + } + + /// 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]. + /// + /// Defaults to [FilterQuality.none]. + // TODO(ianh): verify that the image drawing methods actually respect this + FilterQuality get filterQuality { + return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)]; + } + set filterQuality(FilterQuality value) { + assert(value != null); + final int encoded = value.index; + _data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian); + } + + /// The shader to use when stroking or filling a shape. + /// + /// When this is null, the [color] is used instead. + /// + /// See also: + /// + /// * [Gradient], a shader that paints a color gradient. + /// * [ImageShader], a shader that tiles an [Image]. + /// * [colorFilter], which overrides [shader]. + /// * [color], which is used if [shader] and [colorFilter] are null. + Shader get shader { + if (_objects == null) + return null; + return _objects[_kShaderIndex]; + } + set shader(Shader value) { + _objects ??= new List(_kObjectCount); + _objects[_kShaderIndex] = value; + } + + /// A color filter to apply when a shape is drawn or when a layer is + /// composited. + /// + /// See [ColorFilter] for details. + /// + /// When a shape is being drawn, [colorFilter] overrides [color] and [shader]. + ColorFilter get colorFilter { + switch (_data.getInt32(_kColorFilterOffset, _kFakeHostEndian)) { + case ColorFilter._TypeNone: + return null; + case ColorFilter._TypeMode: + return new ColorFilter.mode( + new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), + BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)], + ); + case ColorFilter._TypeMatrix: + return new ColorFilter.matrix(_objects[_kColorFilterMatrixIndex]); + case ColorFilter._TypeLinearToSrgbGamma: + return const ColorFilter.linearToSrgbGamma(); + case ColorFilter._TypeSrgbToLinearGamma: + return const ColorFilter.srgbToLinearGamma(); + } + + return null; + } + + set colorFilter(ColorFilter value) { + if (value == null) { + _data.setInt32(_kColorFilterOffset, ColorFilter._TypeNone, _kFakeHostEndian); + _data.setInt32(_kColorFilterColorOffset, 0, _kFakeHostEndian); + _data.setInt32(_kColorFilterBlendModeOffset, 0, _kFakeHostEndian); + + if (_objects != null) { + _objects[_kColorFilterMatrixIndex] = null; + } + } else { + _data.setInt32(_kColorFilterOffset, value._type, _kFakeHostEndian); + + if (value._type == ColorFilter._TypeMode) { + assert(value._color != null); + assert(value._blendMode != null); + + _data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian); + _data.setInt32(_kColorFilterBlendModeOffset, value._blendMode.index, _kFakeHostEndian); + } else if (value._type == ColorFilter._TypeMatrix) { + assert(value._matrix != null); + + _objects ??= new List(_kObjectCount); + _objects[_kColorFilterMatrixIndex] = Float32List.fromList(value._matrix); + } + } + } + + /// Whether the colors of the image are inverted when drawn. + /// + /// inverting the colors of an image applies a new color filter that will + /// be composed with any user provided color filters. This is primarily + /// used for implementing smart invert on iOS. + bool get invertColors { + return _data.getInt32(_kInvertColorOffset, _kFakeHostEndian) == 1; + } + set invertColors(bool value) { + _data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian); + } + + @override + String toString() { + final StringBuffer result = new StringBuffer(); + String semicolon = ''; + result.write('Paint('); + if (style == PaintingStyle.stroke) { + result.write('$style'); + if (strokeWidth != 0.0) + result.write(' ${strokeWidth.toStringAsFixed(1)}'); + else + result.write(' hairline'); + if (strokeCap != StrokeCap.butt) + result.write(' $strokeCap'); + if (strokeJoin == StrokeJoin.miter) { + if (strokeMiterLimit != _kStrokeMiterLimitDefault) + result.write(' $strokeJoin up to ${strokeMiterLimit.toStringAsFixed(1)}'); + } else { + result.write(' $strokeJoin'); + } + semicolon = '; '; + } + if (isAntiAlias != true) { + result.write('${semicolon}antialias off'); + semicolon = '; '; + } + if (color != const Color(_kColorDefault)) { + if (color != null) + result.write('$semicolon$color'); + else + result.write('${semicolon}no color'); + semicolon = '; '; + } + if (blendMode.index != _kBlendModeDefault) { + result.write('$semicolon$blendMode'); + semicolon = '; '; + } + if (colorFilter != null) { + result.write('${semicolon}colorFilter: $colorFilter'); + semicolon = '; '; + } + if (maskFilter != null) { + result.write('${semicolon}maskFilter: $maskFilter'); + semicolon = '; '; + } + if (filterQuality != FilterQuality.none) { + result.write('${semicolon}filterQuality: $filterQuality'); + semicolon = '; '; + } + if (shader != null) { + result.write('${semicolon}shader: $shader'); + semicolon = '; '; + } + if (invertColors) + result.write('${semicolon}invert: $invertColors'); + result.write(')'); + return result.toString(); + } +} + +/// The format in which image bytes should be returned when using +/// [Image.toByteData]. +enum ImageByteFormat { + /// Raw RGBA format. + /// + /// Unencoded bytes, in RGBA row-primary form, 8 bits per channel. + rawRgba, + + /// Raw unmodified format. + /// + /// Unencoded bytes, in the image's existing format. For example, a grayscale + /// image may use a single 8-bit channel for each pixel. + rawUnmodified, + + /// PNG format. + /// + /// A loss-less compression format for images. This format is well suited for + /// images with hard edges, such as screenshots or sprites, and images with + /// text. Transparency is supported. The PNG format supports images up to + /// 2,147,483,647 pixels in either dimension, though in practice available + /// memory provides a more immediate limitation on maximum image size. + /// + /// PNG images normally use the `.png` file extension and the `image/png` MIME + /// type. + /// + /// See also: + /// + /// * , the Wikipedia page on PNG. + /// * , the PNG standard. + png, +} + +/// The format of pixel data given to [decodeImageFromPixels]. +enum PixelFormat { + /// Each pixel is 32 bits, with the highest 8 bits encoding red, the next 8 + /// bits encoding green, the next 8 bits encoding blue, and the lowest 8 bits + /// encoding alpha. + rgba8888, + + /// Each pixel is 32 bits, with the highest 8 bits encoding blue, the next 8 + /// bits encoding green, the next 8 bits encoding red, and the lowest 8 bits + /// encoding alpha. + bgra8888, +} + +/// Opaque handle to raw decoded image data (pixels). +/// +/// To obtain an [Image] object, use [instantiateImageCodec]. +/// +/// To draw an [Image], use one of the methods on the [Canvas] class, such as +/// [Canvas.drawImage]. +class Image { + // This class is created by the engine, and should not be instantiated + // or extended directly. + // + // To obtain an [Image] object, use [instantiateImageCodec]. + Image._(); + + /// The number of image pixels along the image's horizontal axis. + int get width { + throw UnimplementedError(); + } + + /// The number of image pixels along the image's vertical axis. + int get height { + throw UnimplementedError(); + } + + /// Converts the [Image] object into a byte array. + /// + /// The [format] argument specifies the format in which the bytes will be + /// returned. + /// + /// Returns a future that completes with the binary image data or an error + /// if encoding fails. + Future toByteData({ImageByteFormat format: ImageByteFormat.rawRgba}) { + throw UnimplementedError(); + } + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + void dispose() { + throw UnimplementedError(); + } + + @override + String toString() => '[$width\u00D7$height]'; +} + +/// Callback signature for [decodeImageFromList]. +typedef ImageDecoderCallback = void Function(Image result); + +/// Information for a single frame of an animation. +/// +/// To obtain an instance of the [FrameInfo] interface, see +/// [Codec.getNextFrame]. +class FrameInfo { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To obtain an instance of the [FrameInfo] interface, see + /// [Codec.getNextFrame]. + FrameInfo._(); + + /// The duration this frame should be shown. + Duration get duration { + throw UnimplementedError(); + } + + /// The [Image] object for this frame. + Image get image { + throw UnimplementedError(); + } +} + +/// A handle to an image codec. +/// +/// This class is created by the engine, and should not be instantiated +/// or extended directly. +/// +/// To obtain an instance of the [Codec] interface, see +/// [instantiateImageCodec]. +class Codec { + // + // This class is created by the engine, and should not be instantiated + // or extended directly. + // + // To obtain an instance of the [Codec] interface, see + // [instantiateImageCodec]. + Codec._(); + + /// Number of frames in this image. + int get frameCount { + throw UnimplementedError(); + } + + /// Number of times to repeat the animation. + /// + /// * 0 when the animation should be played once. + /// * -1 for infinity repetitions. + int get repetitionCount { + throw UnimplementedError(); + } + + /// Fetches the next animation frame. + /// + /// Wraps back to the first frame after returning the last frame. + /// + /// The returned future can complete with an error if the decoding has failed. + Future getNextFrame() { + throw UnimplementedError(); + } + + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + void dispose() { + throw UnimplementedError(); + } +} + +/// Instantiates an image codec [Codec] object. +/// +/// [list] 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} +/// +/// The [decodedCacheRatioCap] is the default maximum multiple of the compressed +/// image size to cache when decoding animated image frames. For example, +/// setting this to `2.0` means that a 400KB GIF would be allowed at most to use +/// 800KB of memory caching unessential decoded frames. Caching decoded frames +/// saves CPU but can result in out-of-memory crashes when decoding large (or +/// multiple) animated images. Note that GIFs are highly compressed, and it's +/// unlikely that a factor that low will be sufficient to cache all decoded +/// frames. The default value is `25.0`. +/// +/// The returned future can complete with an error if the image decoding has +/// failed. +Future instantiateImageCodec(Uint8List list, { + double decodedCacheRatioCap = double.infinity, +}) { + throw UnimplementedError(); +} + + +/// Loads a single image frame from a byte array into an [Image] object. +/// +/// This is a convenience wrapper around [instantiateImageCodec]. Prefer using +/// [instantiateImageCodec] which also supports multi frame images. +void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) { + throw UnimplementedError(); +} + +/// Convert an array of pixel values into an [Image] object. +/// +/// [pixels] is the pixel data in the encoding described by [format]. +/// +/// [rowBytes] is the number of bytes consumed by each row of pixels in the +/// data buffer. If unspecified, it defaults to [width] multiplied by the +/// number of bytes per pixel in the provided [format]. +/// +/// The [decodedCacheRatioCap] is the default maximum multiple of the compressed +/// image size to cache when decoding animated image frames. For example, +/// setting this to `2.0` means that a 400KB GIF would be allowed at most to use +/// 800KB of memory caching unessential decoded frames. Caching decoded frames +/// saves CPU but can result in out-of-memory crashes when decoding large (or +/// multiple) animated images. Note that GIFs are highly compressed, and it's +/// unlikely that a factor that low will be sufficient to cache all decoded +/// frames. The default value is `25.0`. +void decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + PixelFormat format, + ImageDecoderCallback callback, + {int rowBytes, double decodedCacheRatioCap = double.infinity} +) { + throw UnimplementedError(); +} + +/// Determines the winding rule that decides how the interior of a [Path] is +/// calculated. +/// +/// This enum is used by the [Path.fillType] property. +enum PathFillType { + /// The interior is defined by a non-zero sum of signed edge crossings. + /// + /// For a given point, the point is considered to be on the inside of the path + /// if a line drawn from the point to infinity crosses lines going clockwise + /// around the point a different number of times than it crosses lines going + /// counter-clockwise around that point. + /// + /// See: + nonZero, + + /// The interior is defined by an odd number of edge crossings. + /// + /// For a given point, the point is considered to be on the inside of the path + /// if a line drawn from the point to infinity crosses an odd number of lines. + /// + /// See: + evenOdd, +} + +/// Strategies for combining paths. +/// +/// See also: +/// +/// * [Path.combine], which uses this enum to decide how to combine two paths. +// Must be kept in sync with SkPathOp +enum PathOperation { + /// Subtract the second path from the first path. + /// + /// For example, if the two paths are overlapping circles of equal diameter + /// but differing centers, the result would be a crescent portion of the + /// first circle that was not overlapped by the second circle. + /// + /// See also: + /// + /// * [reverseDifference], which is the same but subtracting the first path + /// from the second. + difference, + /// Create a new path that is the intersection of the two paths, leaving the + /// overlapping pieces of the path. + /// + /// For example, if the two paths are overlapping circles of equal diameter + /// but differing centers, the result would be only the overlapping portion + /// of the two circles. + /// + /// See also: + /// * [xor], which is the inverse of this operation + intersect, + /// Create a new path that is the union (inclusive-or) of the two paths. + /// + /// For example, if the two paths are overlapping circles of equal diameter + /// but differing centers, the result would be a figure-eight like shape + /// matching the outer boundaries of both circles. + union, + /// Create a new path that is the exclusive-or of the two paths, leaving + /// everything but the overlapping pieces of the path. + /// + /// For example, if the two paths are overlapping circles of equal diameter + /// but differing centers, the figure-eight like shape less the overlapping parts + /// + /// See also: + /// * [intersect], which is the inverse of this operation + xor, + /// Subtract the first path from the second path. + /// + /// For example, if the two paths are overlapping circles of equal diameter + /// but differing centers, the result would be a crescent portion of the + /// second circle that was not overlapped by the first circle. + /// + /// See also: + /// + /// * [difference], which is the same but subtracting the second path + /// from the first. + reverseDifference, +} + +/// A handle for the framework to hold and retain an engine layer across frames. +class EngineLayer { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + EngineLayer._(); +} + +/// A complex, one-dimensional subset of a plane. +/// +/// A path consists of a number of sub-paths, and a _current point_. +/// +/// Sub-paths consist of segments of various types, such as lines, +/// arcs, or beziers. Sub-paths can be open or closed, and can +/// self-intersect. +/// +/// Closed sub-paths enclose a (possibly discontiguous) region of the +/// plane based on the current [fillType]. +/// +/// The _current point_ is initially at the origin. After each +/// operation adding a segment to a sub-path, the current point is +/// updated to the end of that segment. +/// +/// Paths can be drawn on canvases using [Canvas.drawPath], and can +/// used to create clip regions using [Canvas.clipPath]. +class Path { + /// Create a new empty [Path] object. + Path() { throw UnimplementedError(); } + + /// Creates a copy of another [Path]. + /// + /// This copy is fast and does not require additional memory unless either + /// the `source` path or the path returned by this constructor are modified. + Path.from(Path source); + + /// Determines how the interior of this path is calculated. + /// + /// Defaults to the non-zero winding rule, [PathFillType.nonZero]. + PathFillType get fillType { + throw UnimplementedError(); + } + set fillType(PathFillType value) { + throw UnimplementedError(); + } + + + /// Starts a new sub-path at the given coordinate. + void moveTo(double x, double y) { + throw UnimplementedError(); + } + + /// Starts a new sub-path at the given offset from the current point. + void relativeMoveTo(double dx, double dy) { + throw UnimplementedError(); + } + + /// Adds a straight line segment from the current point to the given + /// point. + void lineTo(double x, double y) { + throw UnimplementedError(); + } + + /// Adds a straight line segment from the current point to the point + /// at the given offset from the current point. + void relativeLineTo(double dx, double dy) { + throw UnimplementedError(); + } + + /// Adds a quadratic bezier segment that curves from the current + /// point to the given point (x2,y2), using the control point + /// (x1,y1). + void quadraticBezierTo(double x1, double y1, double x2, double y2) { + throw UnimplementedError(); + } + + /// Adds a quadratic bezier segment that curves from the current + /// point to the point at the offset (x2,y2) from the current point, + /// using the control point at the offset (x1,y1) from the current + /// point. + void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) { + throw UnimplementedError(); + } + + /// Adds a cubic bezier segment that curves from the current point + /// to the given point (x3,y3), using the control points (x1,y1) and + /// (x2,y2). + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { + throw UnimplementedError(); + } + + /// Adds a cubic bezier segment that curves from the current point + /// to the point at the offset (x3,y3) from the current point, using + /// the control points at the offsets (x1,y1) and (x2,y2) from the + /// current point. + void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { + throw UnimplementedError(); + } + + /// Adds a bezier segment that curves from the current point to the + /// given point (x2,y2), using the control points (x1,y1) and the + /// weight w. If the weight is greater than 1, then the curve is a + /// hyperbola; if the weight equals 1, it's a parabola; and if it is + /// less than 1, it is an ellipse. + void conicTo(double x1, double y1, double x2, double y2, double w) { + throw UnimplementedError(); + } + + /// Adds a bezier segment that curves from the current point to the + /// point at the offset (x2,y2) from the current point, using the + /// control point at the offset (x1,y1) from the current point and + /// the weight w. If the weight is greater than 1, then the curve is + /// a hyperbola; if the weight equals 1, it's a parabola; and if it + /// is less than 1, it is an ellipse. + void relativeConicTo(double x1, double y1, double x2, double y2, double w) { + throw UnimplementedError(); + } + + /// If the `forceMoveTo` argument is false, adds a straight line + /// segment and an arc segment. + /// + /// If the `forceMoveTo` argument is true, starts a new sub-path + /// consisting of an arc segment. + /// + /// In either case, the arc segment consists of the arc that follows + /// the edge of the oval bounded by the given rectangle, from + /// startAngle radians around the oval up to startAngle + sweepAngle + /// radians around the oval, with zero radians being the point on + /// the right hand side of the oval that crosses the horizontal line + /// that intersects the center of the rectangle and with positive + /// angles going clockwise around the oval. + /// + /// The line segment added if `forceMoveTo` is false starts at the + /// current point and ends at the start of the arc. + void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + throw UnimplementedError(); + } + + /// Appends up to four conic curves weighted to describe an oval of `radius` + /// and rotated by `rotation`. + /// + /// The first curve begins from the last point in the path and the last ends + /// at `arcEnd`. The curves follow a path in a direction determined by + /// `clockwise` and `largeArc` in such a way that the sweep angle + /// is always less than 360 degrees. + /// + /// A simple line is appended if either either radii are zero or the last + /// point in the path is `arcEnd`. The radii are scaled to fit the last path + /// point if both are greater than zero but too small to describe an arc. + /// + void arcToPoint(Offset arcEnd, { + Radius radius: Radius.zero, + double rotation: 0.0, + bool largeArc: false, + bool clockwise: true, + }) { + throw UnimplementedError(); + } + + + /// Appends up to four conic curves weighted to describe an oval of `radius` + /// and rotated by `rotation`. + /// + /// The last path point is described by (px, py). + /// + /// The first curve begins from the last point in the path and the last ends + /// at `arcEndDelta.dx + px` and `arcEndDelta.dy + py`. The curves follow a + /// path in a direction determined by `clockwise` and `largeArc` + /// in such a way that the sweep angle is always less than 360 degrees. + /// + /// A simple line is appended if either either radii are zero, or, both + /// `arcEndDelta.dx` and `arcEndDelta.dy` are zero. The radii are scaled to + /// fit the last path point if both are greater than zero but too small to + /// describe an arc. + void relativeArcToPoint(Offset arcEndDelta, { + Radius radius: Radius.zero, + double rotation: 0.0, + bool largeArc: false, + bool clockwise: true, + }) { + assert(_offsetIsValid(arcEndDelta)); + assert(_radiusIsValid(radius)); + throw UnimplementedError(); + } + + /// Adds a new sub-path that consists of four lines that outline the + /// given rectangle. + void addRect(Rect rect) { + assert(_rectIsValid(rect)); + throw UnimplementedError(); + } + + /// Adds a new sub-path that consists of a curve that forms the + /// ellipse that fills the given rectangle. + /// + /// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle] + /// can be used to easily describe the circle's center [Offset] and radius. + void addOval(Rect oval) { + assert(_rectIsValid(oval)); + throw UnimplementedError(); + } + + /// Adds a new sub-path with one arc segment that consists of the arc + /// that follows the edge of the oval bounded by the given + /// rectangle, from startAngle radians around the oval up to + /// startAngle + sweepAngle radians around the oval, with zero + /// radians being the point on the right hand side of the oval that + /// crosses the horizontal line that intersects the center of the + /// rectangle and with positive angles going clockwise around the + /// oval. + void addArc(Rect oval, double startAngle, double sweepAngle) { + assert(_rectIsValid(oval)); + throw UnimplementedError(); + } + + + /// Adds a new sub-path with a sequence of line segments that connect the given + /// points. + /// + /// If `close` is true, a final line segment will be added that connects the + /// last point to the first point. + /// + /// The `points` argument is interpreted as offsets from the origin. + void addPolygon(List points, bool close) { + assert(points != null); + throw UnimplementedError(); + } + + /// Adds a new sub-path that consists of the straight lines and + /// curves needed to form the rounded rectangle described by the + /// argument. + void addRRect(RRect rrect) { + assert(_rrectIsValid(rrect)); + throw UnimplementedError(); + } + + /// Adds a new sub-path that consists of the given `path` offset by the given + /// `offset`. + /// + /// 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}) { + assert(path != null); // path is checked on the engine side + assert(_offsetIsValid(offset)); + throw UnimplementedError(); + } + + /// Adds the given path to this path by extending the current segment of this + /// path with the the first segment of the given 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 extendWithPath(Path path, Offset offset, {Float64List matrix4}) { + assert(path != null); // path is checked on the engine side + assert(_offsetIsValid(offset)); + throw UnimplementedError(); + } + + /// Closes the last sub-path, as if a straight line had been drawn + /// from the current point to the first point of the sub-path. + void close() { + throw UnimplementedError(); + } + + /// Clears the [Path] object of all sub-paths, returning it to the + /// same state it had when it was created. The _current point_ is + /// reset to the origin. + void reset() { + throw UnimplementedError(); + } + + /// Tests to see if the given point is within the path. (That is, whether the + /// point would be in the visible portion of the path if the path was used + /// with [Canvas.clipPath].) + /// + /// The `point` argument is interpreted as an offset from the origin. + /// + /// Returns true if the point is in the path, and false otherwise. + bool contains(Offset point) { + assert(_offsetIsValid(point)); + throw UnimplementedError(); + } + + /// Returns a copy of the path with all the segments of every + /// sub-path translated by the given offset. + Path shift(Offset offset) { + assert(_offsetIsValid(offset)); + throw UnimplementedError(); + } + + /// Returns a copy of the path with all the segments of every + /// sub-path transformed by the given matrix. + Path transform(Float64List matrix4) { + assert(_matrix4IsValid(matrix4)); + throw UnimplementedError(); + } + + /// Computes the bounding rectangle for this path. + /// + /// A path containing only axis-aligned points on the same straight line will + /// have no area, and therefore `Rect.isEmpty` will return true for such a + /// path. Consider checking `rect.width + rect.height > 0.0` instead, or + /// using the [computeMetrics] API to check the path length. + /// + /// For many more elaborate paths, the bounds may be inaccurate. For example, + /// when a path contains a circle, the points used to compute the bounds are + /// the circle's implied control points, which form a square around the circle; + /// if the circle has a transformation applied using [transform] then that + /// square is rotated, and the (axis-aligned, non-rotated) bounding box + /// therefore ends up grossly overestimating the actual area covered by the + /// circle. + // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds + Rect getBounds() { + throw UnimplementedError(); + } + + /// Combines the two paths according to the manner specified by the given + /// `operation`. + /// + /// The resulting path will be constructed from non-overlapping contours. The + /// 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); + assert(path2 != null); + throw UnimplementedError(); + } + + /// Creates a [PathMetrics] object for this path. + /// + /// If `forceClosed` is set to true, the contours of the path will be measured + /// as if they had been closed, even if they were not explicitly closed. + PathMetrics computeMetrics({bool forceClosed: false}) { + throw UnimplementedError(); + } +} + +/// The geometric description of a tangent: the angle at a point. +/// +/// See also: +/// * [PathMetric.getTangentForOffset], which returns the tangent of an offset along a path. +class Tangent { + /// Creates a [Tangent] with the given values. + /// + /// The arguments must not be null. + const Tangent(this.position, this.vector) + : assert(position != null), + assert(vector != null); + + /// Creates a [Tangent] based on the angle rather than the vector. + /// + /// The [vector] is computed to be the unit vector at the given angle, interpreted + /// as clockwise radians from the x axis. + factory Tangent.fromAngle(Offset position, double angle) { + return new Tangent(position, new Offset(math.cos(angle), math.sin(angle))); + } + + /// Position of the tangent. + /// + /// When used with [PathMetric.getTangentForOffset], this represents the precise + /// position that the given offset along the path corresponds to. + final Offset position; + + /// The vector of the curve at [position]. + /// + /// When used with [PathMetric.getTangentForOffset], this is the vector of the + /// curve that is at the given offset along the path (i.e. the direction of the + /// curve at [position]). + final Offset vector; + + /// The direction of the curve at [position]. + /// + /// When used with [PathMetric.getTangentForOffset], this is the angle of the + /// curve that is the given offset along the path (i.e. the direction of the + /// curve at [position]). + /// + /// This value is in radians, with 0.0 meaning pointing along the x axis in + /// the positive x-axis direction, positive numbers pointing downward toward + /// the negative y-axis, i.e. in a clockwise direction, and negative numbers + /// pointing upward toward the positive y-axis, i.e. in a counter-clockwise + /// direction. + // flip the sign to be consistent with [Path.arcTo]'s `sweepAngle` + double get angle => -math.atan2(vector.dy, vector.dx); +} + +/// An iterable collection of [PathMetric] objects describing a [Path]. +/// +/// A [PathMetrics] object is created by using the [Path.computeMetrics] method, +/// and represents the path as it stood at the time of the call. Subsequent +/// modifications of the path do not affect the [PathMetrics] object. +/// +/// Each path metric corresponds to a segment, or contour, of a path. +/// +/// For example, a path consisting of a [Path.lineTo], a [Path.moveTo], and +/// another [Path.lineTo] will contain two contours and thus be represented by +/// two [PathMetric] objects. +/// +/// When iterating across a [PathMetrics]' contours, the [PathMetric] objects are only +/// valid until the next one is obtained. +class PathMetrics extends collection.IterableBase { + + @override + Iterator get iterator { + throw UnimplementedError(); + } +} + +/// Tracks iteration from one segment of a path to the next for measurement. +class PathMetricIterator implements Iterator { + + @override + PathMetric get current { + throw UnimplementedError(); + } + + @override + bool moveNext() { + throw UnimplementedError(); + } +} + +/// Utilities for measuring a [Path] and extracting sub-paths. +/// +/// Iterate over the object returned by [Path.computeMetrics] to obtain +/// [PathMetric] objects. +/// +/// Once created, the methods on this class will only be valid while the +/// iterator is at the contour for which they were created. When the next +/// contour's [PathMetric] is obtained, the [length] and [isClosed] properties +/// remain valid, but the [getTangentForOffset] and [extractPath] will throw a +/// [StateError]. +// TODO(dnfield): Fix this if/when https://bugs.chromium.org/p/skia/issues/detail?id=8721 lands. +class PathMetric { + PathMetric._(this._measure) + : assert(_measure != null), + length = _measure.length, + isClosed = _measure.isClosed, + contourIndex = _measure.currentContourIndex; + + /// Return the total length of the current contour. + final double length; + + /// Whether the contour is closed. + /// + /// Returns true if the contour ends with a call to [Path.close] (which may + /// have been implied when using [Path.addRect]) or if `forceClosed` was + /// specified as true in the call to [Path.computeMetrics]. Returns false + /// otherwise. + final bool isClosed; + + /// The zero-based index of the contour. + /// + /// [Path] objects are made up of zero or more contours. The first contour is + /// created once a drawing command (e.g. [Path.lineTo]) is issued. A + /// [Path.moveTo] command after a drawing command may create a new contour, + /// although it may not if optimizations are applied that determine the move + /// command did not actually result in moving the pen. + /// + /// This property is only valid with reference to its original iterator. If + /// [getTangetForOffset] or [extractPath] are called when this property does + /// not match the actual count of the iterator, those methods will throw a + /// [StateError]. + final int contourIndex; + + final _PathMeasure _measure; + + + /// Computes the position of the current contour at the given offset, and the + /// angle of the path at that point. + /// + /// For example, calling this method with a distance of 1.41 for a line from + /// 0.0,0.0 to 2.0,2.0 would give a point 1.0,1.0 and the angle 45 degrees + /// (but in radians). + /// + /// Returns null if the contour has zero [length]. + /// + /// The distance is clamped to the [length] of the current contour. + Tangent getTangentForOffset(double distance) { + if (contourIndex != _measure.currentContourIndex) { + throw StateError('This method cannot be invoked once the underlying iterator has advanced.'); + } + return _measure.getTangentForOffset(distance); + } + + /// Given a start and stop 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. + Path extractPath(double start, double end, {bool startWithMoveTo: true}) { + if (contourIndex != _measure.currentContourIndex) { + throw StateError('This method cannot be invoked once the underlying iterator has advanced.'); + } + return _measure.extractPath(start, end, startWithMoveTo: startWithMoveTo); + } + + @override + String toString() => '$runtimeType{length: $length, isClosed: $isClosed, contourIndex:$contourIndex}'; +} + +class _PathMeasure { + _PathMeasure(Path path, bool forceClosed) { + currentContourIndex = -1; // PathMetricIterator will increment this the first time. + } + + double get length { + throw UnimplementedError(); + } + + Tangent getTangentForOffset(double distance) { + throw UnimplementedError(); + } + + Path extractPath(double start, double end, {bool startWithMoveTo: true}) { + throw UnimplementedError(); + } + + bool get isClosed { + throw UnimplementedError(); + } + + int currentContourIndex; +} + +/// Styles to use for blurs in [MaskFilter] objects. +// These enum values must be kept in sync with SkBlurStyle. +enum BlurStyle { + // These mirror SkBlurStyle and must be kept in sync. + + /// Fuzzy inside and outside. This is useful for painting shadows that are + /// offset from the shape that ostensibly is casting the shadow. + normal, + + /// Solid inside, fuzzy outside. This corresponds to drawing the shape, and + /// additionally drawing the blur. This can make objects appear brighter, + /// maybe even as if they were fluorescent. + solid, + + /// Nothing inside, fuzzy outside. This is useful for painting shadows for + /// partially transparent shapes, when they are painted separately but without + /// an offset, so that the shadow doesn't paint below the shape. + outer, + + /// Fuzzy inside, nothing outside. This can make shapes appear to be lit from + /// within. + inner, +} + +/// A mask filter to apply to shapes as they are painted. A mask filter is a +/// function that takes a bitmap of color pixels, and returns another bitmap of +/// color pixels. +/// +/// Instances of this class are used with [Paint.maskFilter] on [Paint] objects. +class MaskFilter { + /// Creates a mask filter that takes the shape being drawn and blurs it. + /// + /// This is commonly used to approximate shadows. + /// + /// The `style` argument controls the kind of effect to draw; see [BlurStyle]. + /// + /// The `sigma` argument controls the size of the effect. It is the standard + /// deviation of the Gaussian blur to apply. The value must be greater than + /// zero. The sigma corresponds to very roughly half the radius of the effect + /// in pixels. + /// + /// A blur is an expensive operation and should therefore be used sparingly. + /// + /// The arguments must not be null. + /// + /// See also: + /// + /// * [Canvas.drawShadow], which is a more efficient way to draw shadows. + const MaskFilter.blur( + this._style, + this._sigma, + ) : assert(_style != null), + assert(_sigma != null); + + final BlurStyle _style; + final double _sigma; + + // The type of MaskFilter class to create for Skia. + // These constants must be kept in sync with MaskFilterType in paint.cc. + static const int _TypeNone = 0; // null + static const int _TypeBlur = 1; // SkBlurMaskFilter + + @override + bool operator ==(dynamic other) { + if (other is! MaskFilter) + return false; + final MaskFilter typedOther = other; + return _style == typedOther._style && + _sigma == typedOther._sigma; + } + + @override + int get hashCode => hashValues(_style, _sigma); + + @override + String toString() => 'MaskFilter.blur($_style, ${_sigma.toStringAsFixed(1)})'; +} + +/// 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 +/// two colors, and outputs one color. When applied during compositing, it is +/// independently applied to each pixel of the layer being drawn before the +/// entire layer is merged with the destination. +/// +/// Instances of this class are used with [Paint.colorFilter] on [Paint] +/// objects. +class ColorFilter { + /// 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. + /// + /// 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 ColorFilter.mode(Color color, BlendMode blendMode) + : _color = color, + _blendMode = blendMode, + _matrix = null, + _type = _TypeMode; + + /// Construct a color filter that transforms a color by a 4x5 matrix. The + /// matrix is in row-major order and the translation column is specified in + /// unnormalized, 0...255, space. + const ColorFilter.matrix(List matrix) + : _color = null, + _blendMode = null, + _matrix = matrix, + _type = _TypeMatrix; + + /// Construct a color filter that applies the sRGB gamma curve to the RGB + /// channels. + const ColorFilter.linearToSrgbGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeLinearToSrgbGamma; + + /// Creates a color filter that applies the inverse of the sRGB gamma curve + /// to the RGB channels. + const ColorFilter.srgbToLinearGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeSrgbToLinearGamma; + + final Color _color; + final BlendMode _blendMode; + final List _matrix; + final int _type; + + // The type of SkColorFilter class to create for Skia. + // These constants must be kept in sync with ColorFilterType in paint.cc. + static const int _TypeNone = 0; // null + 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 ==(dynamic other) { + if (other is! ColorFilter) { + return false; + } + final ColorFilter typedOther = other; + + if (_type != typedOther._type) { + return false; + } + if (!_listEquals(_matrix, typedOther._matrix)) { + return false; + } + + return _color == typedOther._color && _blendMode == typedOther._blendMode; + } + + @override + int get hashCode => hashValues(_color, _blendMode, 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.'; + } + } +} + +/// A filter operation to apply to a raster image. +/// +/// See also: +/// +/// * [BackdropFilter], a widget that applies [ImageFilter] to its rendering. +/// * [SceneBuilder.pushBackdropFilter], which is the low-level API for using +/// this class. +class ImageFilter { + + /// Creates an image filter that applies a Gaussian blur. + ImageFilter.blur({ double sigmaX: 0.0, double sigmaY: 0.0 }); + + /// Creates an image filter that applies a matrix transformation. + /// + /// For example, applying a positive scale matrix (see [new Matrix4.diagonal3]) + /// when used with [BackdropFilter] would magnify the background image. + ImageFilter.matrix(Float64List matrix4, + { FilterQuality filterQuality: FilterQuality.low }); +} + +/// Base class for objects such as [Gradient] and [ImageShader] which +/// correspond to shaders as used by [Paint.shader]. +class Shader { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + Shader._(); +} + +/// Defines what happens at the edge of the gradient. +/// +/// 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 +/// outside that defined inner area. +/// +/// See also: +/// +/// * [painting.Gradient], the superclass for [LinearGradient] and +/// [RadialGradient], as used by [BoxDecoration] et al, which works in +/// relative coordinates and can create a [Shader] representing the gradient +/// for a particular [Rect] on demand. +/// * [dart:ui.Gradient], the low-level class used when dealing with the +/// [Paint.shader] property directly, with its [new Gradient.linear] and [new +/// Gradient.radial] constructors. +// These enum values must be kept in sync with SkShader::TileMode. +enum TileMode { + /// Edge is clamped to the final color. + /// + /// The gradient will paint the all the regions outside the inner area with + /// the color of the point closest to that region. + /// + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_radial.png) + clamp, + + /// Edge is repeated from first color to last. + /// + /// 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). + /// + /// ![](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) + repeated, + + /// Edge is mirrored from last color to first. + /// + /// 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 + /// negative direction). + /// + /// ![](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) + mirror, +} + +/// A shader (as used by [Paint.shader]) that renders a color gradient. +/// +/// There are several types of gradients, represented by the various constructors +/// on this class. +class Gradient extends Shader { + + /// Creates a linear gradient from `from` to `to`. + /// + /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 + /// that specifies where `color[i]` begins in the gradient. If `colorStops` is + /// not provided, then only two stops, at 0.0 and 1.0, are implied (and + /// `color` must therefore only have two entries). + /// + /// The behavior before `from` and after `to` is described by the `tileMode` + /// 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_mirror_linear.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_linear.png) + /// + /// If `from`, `to`, `colors`, or `tileMode` are null, or if `colors` or + /// `colorStops` contain null values, this constructor will throw a + /// [NoSuchMethodError]. + Gradient.linear( + Offset from, + Offset to, + List colors, [ + List colorStops, + TileMode tileMode = TileMode.clamp, + ]) : assert(_offsetIsValid(from)), + assert(_offsetIsValid(to)), + assert(colors != null), + assert(tileMode != null), + super._(); + + /// Creates a radial gradient centered at `center` that ends at `radius` + /// distance from the center. + /// + /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 + /// that specifies where `color[i]` begins in the gradient. If `colorStops` is + /// not provided, then only two stops, at 0.0 and 1.0, are implied (and + /// `color` must therefore only have two entries). + /// + /// The behavior before and after the radius is described by the `tileMode` + /// 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_mirror_radial.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_radial.png) + /// + /// If `center`, `radius`, `colors`, or `tileMode` are null, or if `colors` or + /// `colorStops` contain null values, this constructor will throw a + /// [NoSuchMethodError]. + /// + /// If `matrix4` is provided, the gradient fill will be transformed by the + /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must + /// be a column-major matrix packed into a list of 16 values. + /// + /// If `focal` is provided and not equal to `center` and `focalRadius` is + /// provided and not equal to 0.0, the generated shader will be a two point + /// conical radial gradient, with `focal` being the center of the focal + /// circle and `focalRadius` being the radius of that circle. If `focal` is + /// provided and not equal to `center`, at least one of the two offsets must + /// not be equal to [Offset.zero]. + Gradient.radial( + Offset center, + double radius, + List colors, [ + List colorStops, + TileMode tileMode = TileMode.clamp, + Float64List matrix4, + Offset focal, + double focalRadius = 0.0 + ]) : assert(_offsetIsValid(center)), + assert(colors != null), + assert(tileMode != null), + assert(matrix4 == null || _matrix4IsValid(matrix4)), + super._(); + + /// Creates a sweep gradient centered at `center` that starts at `startAngle` + /// and ends at `endAngle`. + /// + /// `startAngle` and `endAngle` should be provided in radians, with zero + /// radians being the horizontal line to the right of the `center` and with + /// positive angles going clockwise around the `center`. + /// + /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 + /// that specifies where `color[i]` begins in the gradient. If `colorStops` is + /// not provided, then only two stops, at 0.0 and 1.0, are implied (and + /// `color` must therefore only have two entries). + /// + /// The behavior before `startAngle` and after `endAngle` is described by the + /// `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_mirror_sweep.png) + /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_repeated_sweep.png) + /// + /// If `center`, `colors`, `tileMode`, `startAngle`, or `endAngle` are null, + /// or if `colors` or `colorStops` contain null values, this constructor will + /// throw a [NoSuchMethodError]. + /// + /// If `matrix4` is provided, the gradient fill will be transformed by the + /// specified 4x4 matrix relative to the local coordinate system. `matrix4` must + /// be a column-major matrix packed into a list of 16 values. + Gradient.sweep( + Offset center, + List colors, [ + List colorStops, + TileMode tileMode = TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float64List matrix4, + ]) : assert(_offsetIsValid(center)), + assert(colors != null), + assert(tileMode != null), + assert(startAngle != null), + assert(endAngle != null), + assert(startAngle < endAngle), + assert(matrix4 == null || _matrix4IsValid(matrix4)), + super._(); +} + +/// A shader (as used by [Paint.shader]) that tiles an image. +class ImageShader extends Shader { + /// Creates an image-tiling shader. The first argument specifies the image to + /// 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. + ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) : + assert(image != null), // image is checked on the engine side + assert(tmx != null), + assert(tmy != null), + assert(matrix4 != null), + super._(); +} + +/// Defines how a list of points is interpreted when drawing a set of triangles. +/// +/// Used by [Canvas.drawVertices]. +// These enum values must be kept in sync with SkVertices::VertexMode. +enum VertexMode { + /// Draw each sequence of three points as the vertices of a triangle. + triangles, + + /// Draw each sliding window of three points as the vertices of a triangle. + triangleStrip, + + /// Draw the first point and each sliding window of two points as the vertices of a triangle. + triangleFan, +} + +/// A set of vertex data used by [Canvas.drawVertices]. +class Vertices { + Vertices( + VertexMode mode, + List positions, { + List textureCoordinates, + List colors, + List indices, + }) : assert(mode != null), + assert(positions != null); + + Vertices.raw( + VertexMode mode, + Float32List positions, { + Float32List textureCoordinates, + Int32List colors, + Int32List indices, + }) : assert(mode != null), + assert(positions != null); +} + +/// 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 { + /// Draw each point separately. + /// + /// If the [Paint.strokeCap] is [StrokeCap.round], then each point is drawn + /// as a circle with the diameter of the [Paint.strokeWidth], filled as + /// described by the [Paint] (ignoring [Paint.style]). + /// + /// Otherwise, each point is drawn as an axis-aligned square with sides of + /// length [Paint.strokeWidth], filled as described by the [Paint] (ignoring + /// [Paint.style]). + points, + + /// Draw each sequence of two points as a line segment. + /// + /// If the number of points is odd, then the last point is ignored. + /// + /// The lines are stroked as described by the [Paint] (ignoring + /// [Paint.style]). + lines, + + /// Draw the entire sequence of point as one line. + /// + /// The lines are stroked as described by the [Paint] (ignoring + /// [Paint.style]). + polygon, +} + +/// Defines how a new clip region should be merged with the existing clip +/// region. +/// +/// Used by [Canvas.clipRect]. +enum ClipOp { + /// Subtract the new region from the existing region. + difference, + + /// Intersect the new region from the existing region. + intersect, +} + +/// An interface for recording graphical operations. +/// +/// [Canvas] objects are used in creating [Picture] objects, which can +/// themselves be used with a [SceneBuilder] to build a [Scene]. In +/// normal usage, however, this is all handled by the framework. +/// +/// A canvas has a current transformation matrix which is applied to all +/// operations. Initially, the transformation matrix is the identity transform. +/// It can be modified using the [translate], [scale], [rotate], [skew], +/// and [transform] methods. +/// +/// A canvas also has a current clip region which is applied to all operations. +/// Initially, the clip region is infinite. It can be modified using the +/// [clipRect], [clipRRect], and [clipPath] methods. +/// +/// The current transform and clip can be saved and restored using the stack +/// managed by the [save], [saveLayer], and [restore] methods. +class Canvas { + /// Creates a canvas for recording graphical operations into the + /// given picture recorder. + /// + /// Graphical operations that affect pixels entirely outside the given + /// `cullRect` might be discarded by the implementation. However, the + /// implementation might draw outside these bounds if, for example, a command + /// draws partially inside and outside the `cullRect`. To ensure that pixels + /// outside a given region are discarded, consider using a [clipRect]. The + /// `cullRect` is optional; by default, all operations are kept. + /// + /// To end the recording, call [PictureRecorder.endRecording] on the + /// given recorder. + Canvas(PictureRecorder recorder, [ Rect cullRect ]) : assert(recorder != null); + + /// Saves a copy of the current transform and clip on the save stack. + /// + /// Call [restore] to pop the save stack. + /// + /// See also: + /// + /// * [saveLayer], which does the same thing but additionally also groups the + /// commands done until the matching [restore]. + void save() { + throw UnimplementedError(); + } + + /// Saves a copy of the current transform and clip on the save stack, and then + /// creates a new group which subsequent calls will become a part of. When the + /// save stack is later popped, the group will be flattened into a layer and + /// have the given `paint`'s [Paint.colorFilter] and [Paint.blendMode] + /// applied. + /// + /// This lets you create composite effects, for example making a group of + /// drawing commands semi-transparent. Without using [saveLayer], each part of + /// the group would be painted individually, so where they overlap would be + /// darker than where they do not. By using [saveLayer] to group them + /// together, they can be drawn with an opaque color at first, and then the + /// entire group can be made transparent using the [saveLayer]'s paint. + /// + /// Call [restore] to pop the save stack and apply the paint to the group. + /// + /// ## Using saveLayer with clips + /// + /// When a rectangular clip operation (from [clipRect]) is not axis-aligned + /// with the raster buffer, or when the clip operation is not rectilinear + /// (e.g. because it is a rounded rectangle clip created by [clipRRect] or an + /// arbitrarily complicated path clip created by [clipPath]), the edge of the + /// clip needs to be anti-aliased. + /// + /// If two draw calls overlap at the edge of such a clipped region, without + /// using [saveLayer], the first drawing will be anti-aliased with the + /// background first, and then the second will be anti-aliased with the result + /// of blending the first drawing and the background. On the other hand, if + /// [saveLayer] is used immediately after establishing the clip, the second + /// drawing will cover the first in the layer, and thus the second alone will + /// be anti-aliased with the background when the layer is clipped and + /// composited (when [restore] is called). + /// + /// For example, this [CustomPainter.paint] method paints a clean white + /// rounded rectangle: + /// + /// ```dart + /// void paint(Canvas canvas, Size size) { + /// Rect rect = Offset.zero & size; + /// canvas.save(); + /// canvas.clipRRect(new RRect.fromRectXY(rect, 100.0, 100.0)); + /// canvas.saveLayer(rect, new Paint()); + /// canvas.drawPaint(new Paint()..color = Colors.red); + /// canvas.drawPaint(new Paint()..color = Colors.white); + /// canvas.restore(); + /// canvas.restore(); + /// } + /// ``` + /// + /// On the other hand, this one renders a red outline, the result of the red + /// paint being anti-aliased with the background at the clip edge, then the + /// white paint being similarly anti-aliased with the background _including + /// the clipped red paint_: + /// + /// ```dart + /// void paint(Canvas canvas, Size size) { + /// // (this example renders poorly, prefer the example above) + /// Rect rect = Offset.zero & size; + /// canvas.save(); + /// canvas.clipRRect(new RRect.fromRectXY(rect, 100.0, 100.0)); + /// canvas.drawPaint(new Paint()..color = Colors.red); + /// canvas.drawPaint(new Paint()..color = Colors.white); + /// canvas.restore(); + /// } + /// ``` + /// + /// This point is moot if the clip only clips one draw operation. For example, + /// the following paint method paints a pair of clean white rounded + /// rectangles, even though the clips are not done on a separate layer: + /// + /// ```dart + /// void paint(Canvas canvas, Size size) { + /// canvas.save(); + /// canvas.clipRRect(new RRect.fromRectXY(Offset.zero & (size / 2.0), 50.0, 50.0)); + /// canvas.drawPaint(new Paint()..color = Colors.white); + /// canvas.restore(); + /// canvas.save(); + /// canvas.clipRRect(new RRect.fromRectXY(size.center(Offset.zero) & (size / 2.0), 50.0, 50.0)); + /// canvas.drawPaint(new Paint()..color = Colors.white); + /// canvas.restore(); + /// } + /// ``` + /// + /// (Incidentally, rather than using [clipRRect] and [drawPaint] to draw + /// rounded rectangles like this, prefer the [drawRRect] method. These + /// examples are using [drawPaint] as a proxy for "complicated draw operations + /// that will get clipped", to illustrate the point.) + /// + /// ## Performance considerations + /// + /// Generally speaking, [saveLayer] is relatively expensive. + /// + /// There are a several different hardware architectures for GPUs (graphics + /// processing units, the hardware that handles graphics), but most of them + /// involve batching commands and reordering them for performance. When layers + /// are used, they cause the rendering pipeline to have to switch render + /// target (from one layer to another). Render target switches can flush the + /// GPU's command buffer, which typically means that optimizations that one + /// could get with larger batching are lost. Render target switches also + /// generate a lot of memory churn because the GPU needs to copy out the + /// current frame buffer contents from the part of memory that's optimized for + /// writing, and then needs to copy it back in once the previous render target + /// (layer) is restored. + /// + /// See also: + /// + /// * [save], which saves the current state, but does not create a new layer + /// for subsequent commands. + /// * [BlendMode], which discusses the use of [Paint.blendMode] with + /// [saveLayer]. + void saveLayer(Rect bounds, Paint paint) { + throw UnimplementedError(); + } + + /// Pops the current save stack, if there is anything to pop. + /// Otherwise, does nothing. + /// + /// Use [save] and [saveLayer] to push state onto the stack. + /// + /// If the state was pushed with with [saveLayer], then this call will also + /// cause the new layer to be composited into the previous layer. + void restore() { + throw UnimplementedError(); + } + + /// Returns the number of items on the save stack, including the + /// initial state. This means it returns 1 for a clean canvas, and + /// that each call to [save] and [saveLayer] increments it, and that + /// each matching call to [restore] decrements it. + /// + /// This number cannot go below 1. + int getSaveCount() { + throw UnimplementedError(); + } + + /// Add a translation to the current transform, shifting the coordinate space + /// horizontally by the first argument and vertically by the second argument. + void translate(double dx, double dy) { + throw UnimplementedError(); + } + + /// Add an axis-aligned scale to the current transform, scaling by the first + /// argument in the horizontal direction and the second in the vertical + /// direction. + /// + /// If [sy] is unspecified, [sx] will be used for the scale in both + /// directions. + void scale(double sx, [double sy]) { + throw UnimplementedError(); + } + + /// Add a rotation to the current transform. The argument is in radians clockwise. + void rotate(double radians) { + throw UnimplementedError(); + } + + /// Add an axis-aligned skew to the current transform, with the first argument + /// being the horizontal skew in radians clockwise around the origin, and the + /// second argument being the vertical skew in radians clockwise around the + /// origin. + void skew(double sx, double sy) { + throw UnimplementedError(); + } + + /// 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) { + throw UnimplementedError(); + } + + /// Reduces the clip region to the intersection of the current clip and the + /// given rectangle. + /// + /// If [doAntiAlias] is true, then the clip will be anti-aliased. + /// + /// If multiple draw commands intersect with the clip boundary, this can result + /// in incorrect blending at the clip boundary. See [saveLayer] for a + /// discussion of how to address that. + /// + /// Use [ClipOp.difference] to subtract the provided rectangle from the + /// current clip. + void clipRect(Rect rect, { ClipOp clipOp: ClipOp.intersect, bool doAntiAlias = true }) { + assert(_rectIsValid(rect)); + assert(clipOp != null); + assert(doAntiAlias != null); + throw UnimplementedError(); + } + + /// Reduces the clip region to the intersection of the current clip and the + /// given rounded rectangle. + /// + /// If [doAntiAlias] is true, then the clip will be anti-aliased. + /// + /// If multiple draw commands intersect with the clip boundary, this can result + /// in incorrect blending at the clip boundary. See [saveLayer] for a + /// 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); + throw UnimplementedError(); + } + + /// Reduces the clip region to the intersection of the current clip and the + /// given [Path]. + /// + /// If [doAntiAlias] is true, then the clip will be anti-aliased. + /// + /// If multiple draw commands intersect with the clip boundary, this can result + /// multiple draw commands intersect with the clip boundary, this can result + /// in incorrect blending at the clip boundary. See [saveLayer] for a + /// discussion of how to address that. + void clipPath(Path path, {bool doAntiAlias = true}) { + assert(path != null); // path is checked on the engine side + assert(doAntiAlias != null); + throw UnimplementedError(); + } + + /// Paints the given [Color] onto the canvas, applying the given + /// [BlendMode], with the given color being the source and the background + /// being the destination. + void drawColor(Color color, BlendMode blendMode) { + assert(color != null); + assert(blendMode != null); + throw UnimplementedError(); + } + + /// Draws a line between the given points using the given paint. The line is + /// stroked, the value of the [Paint.style] is ignored for this call. + /// + /// The `p1` and `p2` arguments are interpreted as offsets from the origin. + void drawLine(Offset p1, Offset p2, Paint paint) { + assert(_offsetIsValid(p1)); + assert(_offsetIsValid(p2)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Fills the canvas with the given [Paint]. + /// + /// To fill the canvas with a solid color and blend mode, consider + /// [drawColor] instead. + void drawPaint(Paint paint) { + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws a rectangle with the given [Paint]. Whether the rectangle is filled + /// or stroked (or both) is controlled by [Paint.style]. + void drawRect(Rect rect, Paint paint) { + assert(_rectIsValid(rect)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws a rounded rectangle with the given [Paint]. Whether the rectangle is + /// filled or stroked (or both) is controlled by [Paint.style]. + void drawRRect(RRect rrect, Paint paint) { + assert(_rrectIsValid(rrect)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws a shape consisting of the difference between two rounded rectangles + /// with the given [Paint]. Whether this shape is filled or stroked (or both) + /// is controlled by [Paint.style]. + /// + /// This shape is almost but not quite entirely unlike an annulus. + void drawDRRect(RRect outer, RRect inner, Paint paint) { + assert(_rrectIsValid(outer)); + assert(_rrectIsValid(inner)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws an axis-aligned oval that fills the given axis-aligned rectangle + /// with the given [Paint]. Whether the oval is filled or stroked (or both) is + /// controlled by [Paint.style]. + void drawOval(Rect rect, Paint paint) { + assert(_rectIsValid(rect)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws a circle centered at the point given by the first argument and + /// that has the radius given by the second argument, with the [Paint] given in + /// the third argument. Whether the circle is filled or stroked (or both) is + /// controlled by [Paint.style]. + void drawCircle(Offset c, double radius, Paint paint) { + assert(_offsetIsValid(c)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draw an arc scaled to fit inside the given rectangle. It starts from + /// startAngle radians around the oval up to startAngle + sweepAngle + /// radians around the oval, with zero radians being the point on + /// the right hand side of the oval that crosses the horizontal line + /// that intersects the center of the rectangle and with positive + /// angles going clockwise around the oval. If useCenter is true, the arc is + /// closed back to the center, forming a circle sector. Otherwise, the arc is + /// not closed, forming a circle segment. + /// + /// 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); + throw UnimplementedError(); + } + + /// Draws the given [Path] with the given [Paint]. Whether this shape is + /// filled or stroked (or both) is controlled by [Paint.style]. If the path is + /// filled, then sub-paths within it are implicitly closed (see [Path.close]). + void drawPath(Path path, Paint paint) { + assert(path != null); // path is checked on the engine side + assert(paint != null); + throw UnimplementedError(); + } + + /// 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 p, Paint paint) { + assert(image != null); // image is checked on the engine side + assert(_offsetIsValid(p)); + assert(paint != null); + throw UnimplementedError(); + } + + /// 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. + /// + /// This might sample from outside the `src` rect by up to half the width of + /// an applied filter. + /// + /// Multiple calls to this method with different arguments (from the same + /// image) can be batched into a single call to [drawAtlas] to improve + /// performance. + void drawImageRect(Image image, Rect src, Rect dst, Paint paint) { + assert(image != null); // image is checked on the engine side + assert(_rectIsValid(src)); + assert(_rectIsValid(dst)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws the given [Image] into the canvas using the given [Paint]. + /// + /// The image is drawn in nine portions described by splitting the image by + /// drawing two horizontal lines and two vertical lines, where the `center` + /// argument describes the rectangle formed by the four points where these + /// four lines intersect each other. (This forms a 3-by-3 grid of regions, + /// the center region being described by the `center` argument.) + /// + /// The four regions in the corners are drawn, without scaling, in the four + /// corners of the destination rectangle described by `dst`. The remaining + /// five regions are drawn by stretching them to fit such that they exactly + /// cover the destination rectangle while maintaining their relative + /// positions. + void drawImageNine(Image image, Rect center, Rect dst, Paint paint) { + assert(image != null); // image is checked on the engine side + assert(_rectIsValid(center)); + assert(_rectIsValid(dst)); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draw the given picture onto the canvas. To create a picture, see + /// [PictureRecorder]. + void drawPicture(Picture picture) { + assert(picture != null); // picture is checked on the engine side + throw UnimplementedError(); + } + + /// Draws the text in the given [Paragraph] into this canvas at the given + /// [Offset]. + /// + /// The [Paragraph] object must have had [Paragraph.layout] called on it + /// first. + /// + /// To align the text, set the `textAlign` on the [ParagraphStyle] object + /// passed to the [new ParagraphBuilder] constructor. For more details see + /// [TextAlign] and the discussion at [new ParagraphStyle]. + /// + /// If the text is left aligned or justified, the left margin will be at the + /// position specified by the `offset` argument's [Offset.dx] coordinate. + /// + /// If the text is right aligned or justified, the right margin will be at the + /// position described by adding the [ParagraphConstraints.width] given to + /// [Paragraph.layout], to the `offset` argument's [Offset.dx] coordinate. + /// + /// If the text is centered, the centering axis will be at the position + /// 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); + assert(_offsetIsValid(offset)); + throw UnimplementedError(); + } + + /// Draws a sequence of points according to the given [PointMode]. + /// + /// The `points` argument is interpreted as offsets from the origin. + /// + /// See also: + /// + /// * [drawRawPoints], which takes `points` as a [Float32List] rather than a + /// [List]. + void drawPoints(PointMode pointMode, List points, Paint paint) { + assert(pointMode != null); + assert(points != null); + assert(paint != null); + throw UnimplementedError(); + } + + /// Draws a sequence of points according to the given [PointMode]. + /// + /// The `points` argument is interpreted as a list of pairs of floating point + /// numbers, where each pair represents an x and y offset from the origin. + /// + /// See also: + /// + /// * [drawPoints], which takes `points` as a [List] rather than a + /// [List]. + void drawRawPoints(PointMode pointMode, Float32List points, Paint paint) { + assert(pointMode != null); + assert(points != null); + assert(paint != null); + throw UnimplementedError(); + } + + void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { + assert(vertices != null); // vertices is checked on the engine side + assert(paint != null); + assert(blendMode != null); + throw UnimplementedError(); + } + + // + // See also: + // + // * [drawRawAtlas], which takes its arguments as typed data lists rather + // than objects. + void drawAtlas(Image atlas, + List transforms, + List rects, + List colors, + BlendMode blendMode, + Rect cullRect, + Paint paint) { + assert(atlas != null); // atlas is checked on the engine side + assert(transforms != null); + assert(rects != null); + assert(colors != null); + assert(blendMode != null); + assert(paint != null); + throw UnimplementedError(); + } + + // + // The `rstTransforms` argument is interpreted as a list of four-tuples, with + // each tuple being ([RSTransform.scos], [RSTransform.ssin], + // [RSTransform.tx], [RSTransform.ty]). + // + // The `rects` argument is interpreted as a list of four-tuples, with each + // tuple being ([Rect.left], [Rect.top], [Rect.right], [Rect.bottom]). + // + // The `colors` argument, which can be null, is interpreted as a list of + // 32-bit colors, with the same packing as [Color.value]. + // + // See also: + // + // * [drawAtlas], which takes its arguments as objects rather than typed + // data lists. + void drawRawAtlas(Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List colors, + BlendMode blendMode, + Rect cullRect, + Paint paint) { + throw UnimplementedError(); + } + + /// Draws a shadow for a [Path] representing the given material elevation. + /// + /// The `transparentOccluder` argument should be true if the occluding object + /// is not opaque. + /// + /// The arguments must not be null. + void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { + assert(path != null); // path is checked on the engine side + assert(color != null); + assert(transparentOccluder != null); + throw UnimplementedError(); + } +} + +/// An object representing a sequence of recorded graphical operations. +/// +/// To create a [Picture], use a [PictureRecorder]. +/// +/// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via +/// the [SceneBuilder.addPicture] method. A [Picture] can also be +/// drawn into a [Canvas], using the [Canvas.drawPicture] method. +class Picture { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To create a [Picture], use a [PictureRecorder]. + Picture._(); + + /// Creates an image from this picture. + /// + /// The picture is rasterized using the number of pixels specified by the + /// given width and height. + /// + /// Although the image is returned synchronously, the picture is actually + /// rasterized the first time the image is drawn and then cached. + Future toImage(int width, int height) { + throw UnimplementedError(); + } + /// Release the resources used by this object. The object is no longer usable + /// after this method is called. + void dispose() { + throw UnimplementedError(); + } + + /// Returns the approximate number of bytes allocated for this object. + /// + /// The actual size of this picture may be larger, particularly if it contains + /// references to image or other large objects. + int get approximateBytesUsed { + throw UnimplementedError(); + } +} + +/// Records a [Picture] containing a sequence of graphical operations. +/// +/// To begin recording, construct a [Canvas] to record the commands. +/// To end recording, use the [PictureRecorder.endRecording] method. +class PictureRecorder { + /// Creates a new idle PictureRecorder. To associate it with a + /// [Canvas] and begin recording, pass this [PictureRecorder] to the + /// [Canvas] constructor. + PictureRecorder(); + + /// Whether this object is currently recording commands. + /// + /// Specifically, this returns true if a [Canvas] object has been + /// created to record commands and recording has not yet ended via a + /// call to [endRecording], and false if either this + /// [PictureRecorder] has not yet been associated with a [Canvas], + /// or the [endRecording] method has already been called. + bool get isRecording { + throw UnimplementedError(); + } + + /// Finishes recording graphical operations. + /// + /// Returns a picture containing the graphical operations that have been + /// recorded thus far. After calling this function, both the picture recorder + /// and the canvas objects are invalid and cannot be used further. + /// + /// Returns null if the PictureRecorder is not associated with a canvas. + Picture endRecording() { + throw UnimplementedError(); + } +} + +/// A single shadow. +/// +/// Multiple shadows are stacked together in a [TextStyle]. +class Shadow { + /// Construct a shadow. + /// + /// The default shadow is a black shadow with zero offset and zero blur. + /// Default shadows should be completely covered by the casting element, + /// and not be visible. + /// + /// Transparency should be adjusted through the [color] alpha. + /// + /// Shadow order matters due to compositing multiple translucent objects not + /// being commutative. + const Shadow({ + this.color = const Color(_kColorDefault), + this.offset = Offset.zero, + this.blurRadius = 0.0, + }) : 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; + + /// Color that the shadow will be drawn with. + /// + /// The shadows are shapes composited directly over the base canvas, and do not + /// represent optical occlusion. + final Color color; + + /// The displacement of the shadow from the casting element. + /// + /// Positive x/y offsets will shift the shadow to the right and down, while + /// negative offsets shift the shadow to the left and up. The offsets are + /// relative to the position of the element that is casting it. + final Offset offset; + + /// The standard deviation of the Gaussian to convolve with the shadow's shape. + final double blurRadius; + + /// Converts a blur radius in pixels to sigmas. + /// + /// See the sigma argument to [MaskFilter.blur]. + /// + // See SkBlurMask::ConvertRadiusToSigma(). + // + static double convertRadiusToSigma(double radius) { + return radius * 0.57735 + 0.5; + } + + /// The [blurRadius] in sigmas instead of logical pixels. + /// + /// See the sigma argument to [MaskFilter.blur]. + double get blurSigma => convertRadiusToSigma(blurRadius); + + /// Create the [Paint] object that corresponds to this shadow description. + /// + /// The [offset] is not represented in the [Paint] object. + /// To honor this as well, the shape should be translated by [offset] before + /// being filled using this [Paint]. + /// + /// This class does not provide a way to disable shadows to avoid + /// inconsistencies in shadow blur rendering, primarily as a method of + /// reducing test flakiness. [toPaint] should be overridden in subclasses to + /// provide this functionality. + Paint toPaint() { + throw UnimplementedError(); + } + + /// Returns a new shadow with its [offset] and [blurRadius] scaled by the given + /// factor. + Shadow scale(double factor) { + throw UnimplementedError(); + } + + /// Linearly interpolate between two shadows. + /// + /// If either shadow is null, this function linearly interpolates from a + /// a shadow that matches the other shadow in color but has a zero + /// offset and a zero blurRadius. + /// + /// {@template dart.ui.shadow.lerp} + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). + /// + /// Values for `t` are usually obtained from an [Animation], such as + /// an [AnimationController]. + /// {@endtemplate} + static Shadow lerp(Shadow a, Shadow b, double t) { + throw UnimplementedError(); + } + + /// Linearly interpolate between two lists of shadows. + /// + /// If the lists differ in length, excess items are lerped with null. + /// + /// {@macro dart.ui.shadow.lerp} + static List lerpList(List a, List b, double t) { + throw UnimplementedError(); + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other is! Shadow) + return false; + final Shadow typedOther = other; + return color == typedOther.color && + offset == typedOther.offset && + blurRadius == typedOther.blurRadius; + } + + @override + int get hashCode => hashValues(color, offset, blurRadius); + + @override + String toString() => 'TextShadow($color, $offset, $blurRadius)'; +} + +/// Generic callback signature, used by [_futurize]. +typedef _Callback = void Function(T result); + +/// Signature for a method that receives a [_Callback]. +/// +/// Return value should be null on success, and a string error message on +/// failure. +typedef _Callbacker = String Function(_Callback callback); + +/// Converts a method that receives a value-returning callback to a method that +/// returns a Future. +/// +/// Return a [String] to cause an [Exception] to be synchronously thrown with +/// that string as a message. +/// +/// If the callback is called with null, the future completes with an error. +/// +/// Example usage: +/// +/// ```dart +/// typedef IntCallback = void Function(int result); +/// +/// String _doSomethingAndCallback(IntCallback callback) { +/// new Timer(new Duration(seconds: 1), () { callback(1); }); +/// } +/// +/// Future doSomething() { +/// return _futurize(_doSomethingAndCallback); +/// } +/// ``` +Future _futurize(_Callbacker callbacker) { + throw UnimplementedError(); +} diff --git a/lib/stub_ui/plugins.dart b/lib/stub_ui/plugins.dart new file mode 100644 index 0000000000000..12dbbf2d3c98f --- /dev/null +++ b/lib/stub_ui/plugins.dart @@ -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. + +part of dart.ui; + +/// A wrapper for a raw callback handle. +/// +/// This is the return type for [PluginUtilities.getCallbackHandle]. +class CallbackHandle { + /// Create an instance using a raw callback handle. + /// + /// 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."); + + final int _handle; + + /// Get the raw callback handle to pass over a [MethodChannel] or [SendPort] + /// (to pass to another [Isolate]). + int toRawHandle() => _handle; + + @override + bool operator ==(dynamic other) { + if (runtimeType != other.runtimeType) + return false; + final CallbackHandle typedOther = other; + return _handle == typedOther._handle; + } + + @override + int get hashCode => _handle.hashCode; +} + +/// Functionality for Flutter plugin authors. +/// +/// See also: +/// +/// * [IsolateNameServer], which provides utilities for dealing with +/// [Isolate]s. +class PluginUtilities { + // This class is only a namespace, and should not be instantiated or + // extended directly. + factory PluginUtilities._() => null; + + /// Get a handle to a named top-level or static callback function which can + /// be easily passed between isolates. + /// + /// The `callback` argument must not be null. + /// + /// Returns a [CallbackHandle] that can be provided to + /// [PluginUtilities.getCallbackFromHandle] to retrieve a tear-off of the + /// original callback. If `callback` is not a top-level or static function, + /// null is returned. + static CallbackHandle getCallbackHandle(Function callback) { + throw UnimplementedError(); + } + + /// Get a tear-off of a named top-level or static callback represented by a + /// handle. + /// + /// The `handle` argument must not be null. + /// + /// If `handle` is not a valid handle returned by + /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a + /// tear-off of the callback associated with `handle` is returned. + static Function getCallbackFromHandle(CallbackHandle handle) { + throw UnimplementedError(); + } +} diff --git a/lib/stub_ui/pointer.dart b/lib/stub_ui/pointer.dart new file mode 100644 index 0000000000000..fd5f3a13e25aa --- /dev/null +++ b/lib/stub_ui/pointer.dart @@ -0,0 +1,276 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +/// How the pointer has changed since the last report. +enum PointerChange { + /// The input from the pointer is no longer directed towards this receiver. + cancel, + + /// The device has started tracking the pointer. + /// + /// For example, the pointer might be hovering above the device, having not yet + /// made contact with the surface of the device. + add, + + /// The device is no longer tracking the pointer. + /// + /// For example, the pointer might have drifted out of the device's hover + /// detection range or might have been disconnected from the system entirely. + remove, + + /// The pointer has moved with respect to the device while not in contact with + /// the device. + hover, + + /// The pointer has made contact with the device. + down, + + /// The pointer has moved with respect to the device while in contact with the + /// device. + move, + + /// The pointer has stopped making contact with the device. + up, +} + +/// The kind of pointer device. +enum PointerDeviceKind { + /// A touch-based pointer device. + touch, + + /// A mouse-based pointer device. + mouse, + + /// A pointer device with a stylus. + stylus, + + /// A pointer device with a stylus that has been inverted. + invertedStylus, + + /// An unknown pointer device. + unknown +} + +/// The kind of [PointerDeviceKind.signal]. +enum PointerSignalKind { + /// The event is not associated with a pointer signal. + none, + + /// A pointer-generated scroll (e.g., mouse wheel or trackpad scroll). + scroll, + + /// An unknown pointer signal kind. + unknown +} + +/// Information about the state of a pointer. +class PointerData { + /// Creates an object that represents the state of a pointer. + const PointerData({ + this.timeStamp: Duration.zero, + this.change: PointerChange.cancel, + this.kind: PointerDeviceKind.touch, + this.signalKind, + this.device: 0, + this.physicalX: 0.0, + this.physicalY: 0.0, + this.buttons: 0, + this.obscured: false, + this.pressure: 0.0, + this.pressureMin: 0.0, + this.pressureMax: 0.0, + this.distance: 0.0, + this.distanceMax: 0.0, + this.size: 0.0, + this.radiusMajor: 0.0, + this.radiusMinor: 0.0, + this.radiusMin: 0.0, + this.radiusMax: 0.0, + this.orientation: 0.0, + this.tilt: 0.0, + this.platformData: 0, + this.scrollDeltaX: 0.0, + this.scrollDeltaY: 0.0, + }); + + /// Time of event dispatch, relative to an arbitrary timeline. + final Duration timeStamp; + + /// How the pointer has changed since the last report. + final PointerChange change; + + /// The kind of input device for which the event was generated. + final PointerDeviceKind kind; + + /// The kind of signal for a pointer signal event. + final PointerSignalKind signalKind; + + /// Unique identifier for the pointing device, reused across interactions. + final int device; + + /// X coordinate of the position of the pointer, in physical pixels in the + /// global coordinate space. + final double physicalX; + + /// Y coordinate of the position of the pointer, in physical pixels in the + /// global coordinate space. + final double physicalY; + + /// Bit field using the *Button constants (primaryMouseButton, + /// secondaryStylusButton, etc). For example, if this has the value 6 and the + /// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an + /// upside-down stylus with both its primary and secondary buttons pressed. + final int buttons; + + /// Set if an application from a different security domain is in any way + /// obscuring this application's window. (Aspirational; not currently + /// implemented.) + final bool obscured; + + /// The pressure of the touch as a number ranging from 0.0, indicating a touch + /// with no discernible pressure, to 1.0, indicating a touch with "normal" + /// pressure, and possibly beyond, indicating a stronger touch. For devices + /// that do not detect pressure (e.g. mice), returns 1.0. + final double pressure; + + /// The minimum value that [pressure] can return for this pointer. For devices + /// that do not detect pressure (e.g. mice), returns 1.0. This will always be + /// a number less than or equal to 1.0. + final double pressureMin; + + /// The maximum value that [pressure] can return for this pointer. For devices + /// that do not detect pressure (e.g. mice), returns 1.0. This will always be + /// a greater than or equal to 1.0. + final double pressureMax; + + /// The distance of the detected object from the input surface (e.g. the + /// distance of a stylus or finger from a touch screen), in arbitrary units on + /// an arbitrary (not necessarily linear) scale. If the pointer is down, this + /// is 0.0 by definition. + final double distance; + + /// The maximum value that a distance can return for this pointer. If this + /// input device cannot detect "hover touch" input events, then this will be + /// 0.0. + final double distanceMax; + + /// The area of the screen being pressed, scaled to a value between 0 and 1. + /// The value of size can be used to determine fat touch events. This value + /// is only set on Android, and is a device specific approximation within + /// the range of detectable values. So, for example, the value of 0.1 could + /// mean a touch with the tip of the finger, 0.2 a touch with full finger, + /// and 0.3 the full palm. + final double size; + + /// The radius of the contact ellipse along the major axis, in logical pixels. + final double radiusMajor; + + /// The radius of the contact ellipse along the minor axis, in logical pixels. + final double radiusMinor; + + /// The minimum value that could be reported for radiusMajor and radiusMinor + /// for this pointer, in logical pixels. + final double radiusMin; + + /// The minimum value that could be reported for radiusMajor and radiusMinor + /// for this pointer, in logical pixels. + final double radiusMax; + + /// For PointerDeviceKind.touch events: + /// + /// The angle of the contact ellipse, in radius in the range: + /// + /// -pi/2 < orientation <= pi/2 + /// + /// ...giving the angle of the major axis of the ellipse with the y-axis + /// (negative angles indicating an orientation along the top-left / + /// bottom-right diagonal, positive angles indicating an orientation along the + /// top-right / bottom-left diagonal, and zero indicating an orientation + /// parallel with the y-axis). + /// + /// For PointerDeviceKind.stylus and PointerDeviceKind.invertedStylus events: + /// + /// The angle of the stylus, in radians in the range: + /// + /// -pi < orientation <= pi + /// + /// ...giving the angle of the axis of the stylus projected onto the input + /// surface, relative to the positive y-axis of that surface (thus 0.0 + /// indicates the stylus, if projected onto that surface, would go from the + /// contact point vertically up in the positive y-axis direction, pi would + /// indicate that the stylus would go down in the negative y-axis direction; + /// pi/4 would indicate that the stylus goes up and to the right, -pi/2 would + /// indicate that the stylus goes to the left, etc). + final double orientation; + + /// For PointerDeviceKind.stylus and PointerDeviceKind.invertedStylus events: + /// + /// The angle of the stylus, in radians in the range: + /// + /// 0 <= tilt <= pi/2 + /// + /// ...giving the angle of the axis of the stylus, relative to the axis + /// perpendicular to the input surface (thus 0.0 indicates the stylus is + /// orthogonal to the plane of the input surface, while pi/2 indicates that + /// the stylus is flat on that surface). + final double tilt; + + /// Opaque platform-specific data associated with the event. + final int platformData; + + /// For events with signalKind of PointerSignalKind.scroll: + /// + /// The amount to scroll in the x direction, in physical pixels. + final double scrollDeltaX; + + /// For events with signalKind of PointerSignalKind.scroll: + /// + /// The amount to scroll in the y direction, in physical pixels. + final double scrollDeltaY; + + @override + String toString() => '$runtimeType(x: $physicalX, y: $physicalY)'; + + /// Returns a complete textual description of the information in this object. + String toStringFull() { + return '$runtimeType(' + 'timeStamp: $timeStamp, ' + 'change: $change, ' + 'kind: $kind, ' + 'signalKind: $signalKind, ' + 'device: $device, ' + 'physicalX: $physicalX, ' + 'physicalY: $physicalY, ' + 'buttons: $buttons, ' + 'pressure: $pressure, ' + 'pressureMin: $pressureMin, ' + 'pressureMax: $pressureMax, ' + 'distance: $distance, ' + 'distanceMax: $distanceMax, ' + 'size: $size, ' + 'radiusMajor: $radiusMajor, ' + 'radiusMinor: $radiusMinor, ' + 'radiusMin: $radiusMin, ' + 'radiusMax: $radiusMax, ' + 'orientation: $orientation, ' + 'tilt: $tilt, ' + 'platformData: $platformData, ' + 'scrollDeltaX: $scrollDeltaX, ' + 'scrollDeltaY: $scrollDeltaY' + ')'; + } +} + +/// A sequence of reports about the state of pointers. +class PointerDataPacket { + /// Creates a packet of pointer data reports. + const PointerDataPacket({ this.data: const [] }); + + /// Data about the individual pointers in this packet. + /// + /// This list might contain multiple pieces of data about the same pointer. + final List data; +} diff --git a/lib/stub_ui/semantics.dart b/lib/stub_ui/semantics.dart new file mode 100644 index 0000000000000..cd2715eddf3db --- /dev/null +++ b/lib/stub_ui/semantics.dart @@ -0,0 +1,700 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +/// The possible actions that can be conveyed from the operating system +/// accessibility APIs to a semantics node. +class SemanticsAction { + const SemanticsAction._(this.index); + + static const int _kTapIndex = 1 << 0; + static const int _kLongPressIndex = 1 << 1; + static const int _kScrollLeftIndex = 1 << 2; + static const int _kScrollRightIndex = 1 << 3; + static const int _kScrollUpIndex = 1 << 4; + static const int _kScrollDownIndex = 1 << 5; + static const int _kIncreaseIndex = 1 << 6; + static const int _kDecreaseIndex = 1 << 7; + static const int _kShowOnScreenIndex = 1 << 8; + static const int _kMoveCursorForwardByCharacterIndex = 1 << 9; + static const int _kMoveCursorBackwardByCharacterIndex = 1 << 10; + static const int _kSetSelectionIndex = 1 << 11; + static const int _kCopyIndex = 1 << 12; + static const int _kCutIndex = 1 << 13; + static const int _kPasteIndex = 1 << 14; + static const int _kDidGainAccessibilityFocusIndex = 1 << 15; + static const int _kDidLoseAccessibilityFocusIndex = 1 << 16; + static const int _kCustomAction = 1 << 17; + static const int _kDismissIndex = 1 << 18; + static const int _kMoveCursorForwardByWordIndex = 1 << 19; + static const int _kMoveCursorBackwardByWordIndex = 1 << 20; + + /// The numerical value for this action. + /// + /// Each action has one bit set in this bit field. + final int index; + + /// The equivalent of a user briefly tapping the screen with the finger + /// without moving it. + static const SemanticsAction tap = const SemanticsAction._(_kTapIndex); + + /// The equivalent of a user pressing and holding the screen with the finger + /// for a few seconds without moving it. + static const SemanticsAction longPress = const SemanticsAction._(_kLongPressIndex); + + /// The equivalent of a user moving their finger across the screen from right + /// to left. + /// + /// This action should be recognized by controls that are horizontally + /// scrollable. + static const SemanticsAction scrollLeft = const SemanticsAction._(_kScrollLeftIndex); + + /// The equivalent of a user moving their finger across the screen from left + /// to right. + /// + /// This action should be recognized by controls that are horizontally + /// scrollable. + static const SemanticsAction scrollRight = const SemanticsAction._(_kScrollRightIndex); + + /// The equivalent of a user moving their finger across the screen from + /// bottom to top. + /// + /// This action should be recognized by controls that are vertically + /// scrollable. + static const SemanticsAction scrollUp = const SemanticsAction._(_kScrollUpIndex); + + /// The equivalent of a user moving their finger across the screen from top + /// to bottom. + /// + /// This action should be recognized by controls that are vertically + /// scrollable. + static const SemanticsAction scrollDown = const SemanticsAction._(_kScrollDownIndex); + + /// A request to increase the value represented by the semantics node. + /// + /// For example, this action might be recognized by a slider control. + static const SemanticsAction increase = const SemanticsAction._(_kIncreaseIndex); + + /// A request to decrease the value represented by the semantics node. + /// + /// For example, this action might be recognized by a slider control. + static const SemanticsAction decrease = const SemanticsAction._(_kDecreaseIndex); + + /// A request to fully show the semantics node on screen. + /// + /// For example, this action might be send to a node in a scrollable list that + /// is partially off screen to bring it on screen. + static const SemanticsAction showOnScreen = const SemanticsAction._(_kShowOnScreenIndex); + + /// Move the cursor forward by one character. + /// + /// This is for example used by the cursor control in text fields. + /// + /// The action includes a boolean argument, which indicates whether the cursor + /// movement should extend (or start) a selection. + static const SemanticsAction moveCursorForwardByCharacter = const SemanticsAction._(_kMoveCursorForwardByCharacterIndex); + + /// Move the cursor backward by one character. + /// + /// This is for example used by the cursor control in text fields. + /// + /// The action includes a boolean argument, which indicates whether the cursor + /// movement should extend (or start) a selection. + static const SemanticsAction moveCursorBackwardByCharacter = const SemanticsAction._(_kMoveCursorBackwardByCharacterIndex); + + /// Set the text selection to the given range. + /// + /// The provided argument is a Map which includes the keys `base` + /// and `extent` indicating where the selection within the `value` of the + /// semantics node should start and where it should end. Values for both + /// keys can range from 0 to length of `value` (inclusive). + /// + /// Setting `base` and `extent` to the same value will move the cursor to + /// that position (without selecting anything). + static const SemanticsAction setSelection = const SemanticsAction._(_kSetSelectionIndex); + + /// Copy the current selection to the clipboard. + static const SemanticsAction copy = const SemanticsAction._(_kCopyIndex); + + /// Cut the current selection and place it in the clipboard. + static const SemanticsAction cut = const SemanticsAction._(_kCutIndex); + + /// Paste the current content of the clipboard. + static const SemanticsAction paste = const SemanticsAction._(_kPasteIndex); + + /// Indicates that the node has gained accessibility focus. + /// + /// This handler is invoked when the node annotated with this handler gains + /// the accessibility focus. The accessibility focus is the + /// green (on Android with TalkBack) or black (on iOS with VoiceOver) + /// rectangle shown on screen to indicate what element an accessibility + /// user is currently interacting with. + /// + /// The accessibility focus is different from the input focus. The input focus + /// is usually held by the element that currently responds to keyboard inputs. + /// Accessibility focus and input focus can be held by two different nodes! + static const SemanticsAction didGainAccessibilityFocus = const SemanticsAction._(_kDidGainAccessibilityFocusIndex); + + /// Indicates that the node has lost accessibility focus. + /// + /// This handler is invoked when the node annotated with this handler + /// loses the accessibility focus. The accessibility focus is + /// the green (on Android with TalkBack) or black (on iOS with VoiceOver) + /// rectangle shown on screen to indicate what element an accessibility + /// user is currently interacting with. + /// + /// The accessibility focus is different from the input focus. The input focus + /// is usually held by the element that currently responds to keyboard inputs. + /// Accessibility focus and input focus can be held by two different nodes! + static const SemanticsAction didLoseAccessibilityFocus = const SemanticsAction._(_kDidLoseAccessibilityFocusIndex); + + /// Indicates that the user has invoked a custom accessibility action. + /// + /// This handler is added automatically whenever a custom accessibility + /// action is added to a semantics node. + static const SemanticsAction customAction = const SemanticsAction._(_kCustomAction); + + /// A request that the node should be dismissed. + /// + /// A [Snackbar], for example, may have a dismiss action to indicate to the + /// user that it can be removed after it is no longer relevant. On Android, + /// (with TalkBack) special hint text is spoken when focusing the node and + /// a custom action is available in the local context menu. On iOS, + /// (with VoiceOver) users can perform a standard gesture to dismiss it. + static const SemanticsAction dismiss = const SemanticsAction._(_kDismissIndex); + + /// Move the cursor forward by one word. + /// + /// This is for example used by the cursor control in text fields. + /// + /// The action includes a boolean argument, which indicates whether the cursor + /// movement should extend (or start) a selection. + static const SemanticsAction moveCursorForwardByWord = const SemanticsAction._(_kMoveCursorForwardByWordIndex); + + /// Move the cursor backward by one word. + /// + /// This is for example used by the cursor control in text fields. + /// + /// The action includes a boolean argument, which indicates whether the cursor + /// movement should extend (or start) a selection. + static const SemanticsAction moveCursorBackwardByWord = const SemanticsAction._(_kMoveCursorBackwardByWordIndex); + + /// The possible semantics actions. + /// + /// The map's key is the [index] of the action and the value is the action + /// itself. + static const Map values = const { + _kTapIndex: tap, + _kLongPressIndex: longPress, + _kScrollLeftIndex: scrollLeft, + _kScrollRightIndex: scrollRight, + _kScrollUpIndex: scrollUp, + _kScrollDownIndex: scrollDown, + _kIncreaseIndex: increase, + _kDecreaseIndex: decrease, + _kShowOnScreenIndex: showOnScreen, + _kMoveCursorForwardByCharacterIndex: moveCursorForwardByCharacter, + _kMoveCursorBackwardByCharacterIndex: moveCursorBackwardByCharacter, + _kSetSelectionIndex: setSelection, + _kCopyIndex: copy, + _kCutIndex: cut, + _kPasteIndex: paste, + _kDidGainAccessibilityFocusIndex: didGainAccessibilityFocus, + _kDidLoseAccessibilityFocusIndex: didLoseAccessibilityFocus, + _kCustomAction: customAction, + _kDismissIndex: dismiss, + _kMoveCursorForwardByWordIndex: moveCursorForwardByWord, + _kMoveCursorBackwardByWordIndex: moveCursorBackwardByWord, + }; + + @override + String toString() { + switch (index) { + case _kTapIndex: + return 'SemanticsAction.tap'; + case _kLongPressIndex: + return 'SemanticsAction.longPress'; + case _kScrollLeftIndex: + return 'SemanticsAction.scrollLeft'; + case _kScrollRightIndex: + return 'SemanticsAction.scrollRight'; + case _kScrollUpIndex: + return 'SemanticsAction.scrollUp'; + case _kScrollDownIndex: + return 'SemanticsAction.scrollDown'; + case _kIncreaseIndex: + return 'SemanticsAction.increase'; + case _kDecreaseIndex: + return 'SemanticsAction.decrease'; + case _kShowOnScreenIndex: + return 'SemanticsAction.showOnScreen'; + case _kMoveCursorForwardByCharacterIndex: + return 'SemanticsAction.moveCursorForwardByCharacter'; + case _kMoveCursorBackwardByCharacterIndex: + return 'SemanticsAction.moveCursorBackwardByCharacter'; + case _kSetSelectionIndex: + return 'SemanticsAction.setSelection'; + case _kCopyIndex: + return 'SemanticsAction.copy'; + case _kCutIndex: + return 'SemanticsAction.cut'; + case _kPasteIndex: + return 'SemanticsAction.paste'; + case _kDidGainAccessibilityFocusIndex: + return 'SemanticsAction.didGainAccessibilityFocus'; + case _kDidLoseAccessibilityFocusIndex: + return 'SemanticsAction.didLoseAccessibilityFocus'; + case _kCustomAction: + return 'SemanticsAction.customAction'; + case _kDismissIndex: + return 'SemanticsAction.dismiss'; + case _kMoveCursorForwardByWordIndex: + return 'SemanticsAction.moveCursorForwardByWord'; + case _kMoveCursorBackwardByWordIndex: + return 'SemanticsAction.moveCursorBackwardByWord'; + } + return null; + } +} + +/// A Boolean value that can be associated with a semantics node. +class SemanticsFlag { + static const int _kHasCheckedStateIndex = 1 << 0; + static const int _kIsCheckedIndex = 1 << 1; + static const int _kIsSelectedIndex = 1 << 2; + static const int _kIsButtonIndex = 1 << 3; + static const int _kIsTextFieldIndex = 1 << 4; + static const int _kIsFocusedIndex = 1 << 5; + static const int _kHasEnabledStateIndex = 1 << 6; + static const int _kIsEnabledIndex = 1 << 7; + static const int _kIsInMutuallyExclusiveGroupIndex = 1 << 8; + static const int _kIsHeaderIndex = 1 << 9; + static const int _kIsObscuredIndex = 1 << 10; + static const int _kScopesRouteIndex= 1 << 11; + static const int _kNamesRouteIndex = 1 << 12; + static const int _kIsHiddenIndex = 1 << 13; + static const int _kIsImageIndex = 1 << 14; + static const int _kIsLiveRegionIndex = 1 << 15; + static const int _kHasToggledStateIndex = 1 << 16; + static const int _kIsToggledIndex = 1 << 17; + static const int _kHasImplicitScrollingIndex = 1 << 18; + + const SemanticsFlag._(this.index); + + /// The numerical value for this flag. + /// + /// Each flag has one bit set in this bit field. + final int index; + + /// The semantics node has the quality of either being "checked" or "unchecked". + /// + /// This flag is mutually exclusive with [hasToggledState]. + /// + /// For example, a checkbox or a radio button widget has checked state. + /// + /// See also: + /// + /// * [SemanticsFlag.isChecked], which controls whether the node is "checked" or "unchecked". + static const SemanticsFlag hasCheckedState = const SemanticsFlag._(_kHasCheckedStateIndex); + + /// Whether a semantics node that [hasCheckedState] is checked. + /// + /// If true, the semantics node is "checked". If false, the semantics node is + /// "unchecked". + /// + /// For example, if a checkbox has a visible checkmark, [isChecked] is true. + /// + /// See also: + /// + /// * [SemanticsFlag.hasCheckedState], which enables a checked state. + static const SemanticsFlag isChecked = const SemanticsFlag._(_kIsCheckedIndex); + + + /// Whether a semantics node is selected. + /// + /// If true, the semantics node is "selected". If false, the semantics node is + /// "unselected". + /// + /// For example, the active tab in a tab bar has [isSelected] set to true. + static const SemanticsFlag isSelected = const SemanticsFlag._(_kIsSelectedIndex); + + /// Whether the semantic node represents a button. + /// + /// Platforms has special handling for buttons, for example Android's TalkBack + /// and iOS's VoiceOver provides an additional hint when the focused object is + /// a button. + static const SemanticsFlag isButton = const SemanticsFlag._(_kIsButtonIndex); + + /// Whether the semantic node represents a text field. + /// + /// Text fields are announced as such and allow text input via accessibility + /// affordances. + static const SemanticsFlag isTextField = const SemanticsFlag._(_kIsTextFieldIndex); + + /// Whether the semantic node currently holds the user's focus. + /// + /// The focused element is usually the current receiver of keyboard inputs. + static const SemanticsFlag isFocused = const SemanticsFlag._(_kIsFocusedIndex); + + /// The semantics node has the quality of either being "enabled" or + /// "disabled". + /// + /// For example, a button can be enabled or disabled and therefore has an + /// "enabled" state. Static text is usually neither enabled nor disabled and + /// therefore does not have an "enabled" state. + static const SemanticsFlag hasEnabledState = const SemanticsFlag._(_kHasEnabledStateIndex); + + /// Whether a semantic node that [hasEnabledState] is currently enabled. + /// + /// A disabled element does not respond to user interaction. For example, a + /// button that currently does not respond to user interaction should be + /// marked as disabled. + static const SemanticsFlag isEnabled = const SemanticsFlag._(_kIsEnabledIndex); + + /// Whether a semantic node is in a mutually exclusive group. + /// + /// For example, a radio button is in a mutually exclusive group because + /// only one radio button in that group can be marked as [isChecked]. + static const SemanticsFlag isInMutuallyExclusiveGroup = const SemanticsFlag._(_kIsInMutuallyExclusiveGroupIndex); + + /// Whether a semantic node is a header that divides content into sections. + /// + /// For example, headers can be used to divide a list of alphabetically + /// sorted words into the sections A, B, C, etc. as can be found in many + /// address book applications. + static const SemanticsFlag isHeader = const SemanticsFlag._(_kIsHeaderIndex); + + /// Whether the value of the semantics node is obscured. + /// + /// This is usually used for text fields to indicate that its content + /// is a password or contains other sensitive information. + static const SemanticsFlag isObscured = const SemanticsFlag._(_kIsObscuredIndex); + + /// Whether the semantics node is the root of a subtree for which a route name + /// should be announced. + /// + /// When a node with this flag is removed from the semantics tree, the + /// framework will select the last in depth-first, paint order node with this + /// flag. When a node with this flag is added to the semantics tree, it is + /// selected automatically, unless there were multiple nodes with this flag + /// added. In this case, the last added node in depth-first, paint order + /// will be selected. + /// + /// From this selected node, the framework will search in depth-first, paint + /// order for the first node with a [namesRoute] flag and a non-null, + /// non-empty label. The [namesRoute] and [scopesRoute] flags may be on the + /// same node. The label of the found node will be announced as an edge + /// transition. If no non-empty, non-null label is found then: + /// + /// * VoiceOver will make a chime announcement. + /// * TalkBack will make no announcement + /// + /// Semantic nodes annotated with this flag are generally not a11y focusable. + /// + /// This is used in widgets such as Routes, Drawers, and Dialogs to + /// communicate significant changes in the visible screen. + static const SemanticsFlag scopesRoute = const SemanticsFlag._(_kScopesRouteIndex); + + /// Whether the semantics node label is the name of a visually distinct + /// route. + /// + /// This is used by certain widgets like Drawers and Dialogs, to indicate + /// that the node's semantic label can be used to announce an edge triggered + /// semantics update. + /// + /// Semantic nodes annotated with this flag will still receive a11y focus. + /// + /// Updating this label within the same active route subtree will not cause + /// additional announcements. + static const SemanticsFlag namesRoute = const SemanticsFlag._(_kNamesRouteIndex); + + /// Whether the semantics node is considered hidden. + /// + /// Hidden elements are currently not visible on screen. They may be covered + /// by other elements or positioned outside of the visible area of a viewport. + /// + /// Hidden elements cannot gain accessibility focus though regular touch. The + /// only way they can be focused is by moving the focus to them via linear + /// navigation. + /// + /// Platforms are free to completely ignore hidden elements and new platforms + /// are encouraged to do so. + /// + /// Instead of marking an element as hidden it should usually be excluded from + /// 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. + static const SemanticsFlag isHidden = const SemanticsFlag._(_kIsHiddenIndex); + + /// Whether the semantics node represents an image. + /// + /// Both TalkBack and VoiceOver will inform the user the the semantics node + /// represents an image. + static const SemanticsFlag isImage = const SemanticsFlag._(_kIsImageIndex); + + /// Whether the semantics node is a live region. + /// + /// A live region indicates that updates to semantics node are important. + /// Platforms may use this information to make polite announcements to the + /// user to inform them of updates to this node. + /// + /// An example of a live region is a [SnackBar] widget. On Android, A live + /// region causes a polite announcement to be generated automatically, even + /// if the user does not have focus of the widget. + static const SemanticsFlag isLiveRegion = const SemanticsFlag._(_kIsLiveRegionIndex); + + /// The semantics node has the quality of either being "on" or "off". + /// + /// This flag is mutually exclusive with [hasCheckedState]. + /// + /// For example, a switch has toggled state. + /// + /// See also: + /// + /// * [SemanticsFlag.isToggled], which controls whether the node is "on" or "off". + static const SemanticsFlag hasToggledState = const SemanticsFlag._(_kHasToggledStateIndex); + + /// If true, the semantics node is "on". If false, the semantics node is + /// "off". + /// + /// For example, if a switch is in the on position, [isToggled] is true. + /// + /// See also: + /// + /// * [SemanticsFlag.hasToggledState], which enables a toggled state. + static const SemanticsFlag isToggled = const SemanticsFlag._(_kIsToggledIndex); + + /// Whether the platform can scroll the semantics node when the user attempts + /// to move focus to an offscreen child. + /// + /// For example, a [ListView] widget has implicit scrolling so that users can + /// easily move to the next visible set of children. A [TabBar] widget does + /// not have implicit scrolling, so that users can navigate into the tab + /// body when reaching the end of the tab bar. + static const SemanticsFlag hasImplicitScrolling = const SemanticsFlag._(_kHasImplicitScrollingIndex); + + /// The possible semantics flags. + /// + /// The map's key is the [index] of the flag and the value is the flag itself. + static const Map values = const { + _kHasCheckedStateIndex: hasCheckedState, + _kIsCheckedIndex: isChecked, + _kIsSelectedIndex: isSelected, + _kIsButtonIndex: isButton, + _kIsTextFieldIndex: isTextField, + _kIsFocusedIndex: isFocused, + _kHasEnabledStateIndex: hasEnabledState, + _kIsEnabledIndex: isEnabled, + _kIsInMutuallyExclusiveGroupIndex: isInMutuallyExclusiveGroup, + _kIsHeaderIndex: isHeader, + _kIsObscuredIndex: isObscured, + _kScopesRouteIndex: scopesRoute, + _kNamesRouteIndex: namesRoute, + _kIsHiddenIndex: isHidden, + _kIsImageIndex: isImage, + _kIsLiveRegionIndex: isLiveRegion, + _kHasToggledStateIndex: hasToggledState, + _kIsToggledIndex: isToggled, + _kHasImplicitScrollingIndex: hasImplicitScrolling, + }; + + @override + String toString() { + switch (index) { + case _kHasCheckedStateIndex: + return 'SemanticsFlag.hasCheckedState'; + case _kIsCheckedIndex: + return 'SemanticsFlag.isChecked'; + case _kIsSelectedIndex: + return 'SemanticsFlag.isSelected'; + case _kIsButtonIndex: + return 'SemanticsFlag.isButton'; + case _kIsTextFieldIndex: + return 'SemanticsFlag.isTextField'; + case _kIsFocusedIndex: + return 'SemanticsFlag.isFocused'; + case _kHasEnabledStateIndex: + return 'SemanticsFlag.hasEnabledState'; + case _kIsEnabledIndex: + return 'SemanticsFlag.isEnabled'; + case _kIsInMutuallyExclusiveGroupIndex: + return 'SemanticsFlag.isInMutuallyExclusiveGroup'; + case _kIsHeaderIndex: + return 'SemanticsFlag.isHeader'; + case _kIsObscuredIndex: + return 'SemanticsFlag.isObscured'; + case _kScopesRouteIndex: + return 'SemanticsFlag.scopesRoute'; + case _kNamesRouteIndex: + return 'SemanticsFlag.namesRoute'; + case _kIsHiddenIndex: + return 'SemanticsFlag.isHidden'; + case _kIsImageIndex: + return 'SemanticsFlag.isImage'; + case _kIsLiveRegionIndex: + return 'SemanticsFlag.isLiveRegion'; + case _kHasToggledStateIndex: + return 'SemanticsFlag.hasToggledState'; + case _kIsToggledIndex: + return 'SemanticsFlag.isToggled'; + case _kHasImplicitScrollingIndex: + return 'SemanticsFlag.hasImplicitScrolling'; + } + return null; + } +} + +/// 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. +class SemanticsUpdateBuilder { + /// Creates an empty [SemanticsUpdateBuilder] object. + SemanticsUpdateBuilder(); + + /// Update the information associated with the node with the given `id`. + /// + /// The semantics nodes form a tree, with the root of the tree always having + /// an id of zero. The `childrenInTraversalOrder` and `childrenInHitTestOrder` + /// are the ids of the nodes that are immediate children of this node. The + /// former enumerates children in traversal order, and the latter enumerates + /// the same children in the hit test order. The two lists must have the same + /// length and contain the same ids. They may only differ in the order the + /// ids are listed in. For more information about different child orders, see + /// [DebugSemanticsDumpOrder]. + /// + /// The system retains the nodes that are currently reachable from the root. + /// A given update need not contain information for nodes that do not change + /// in the update. If a node is not reachable from the root after an update, + /// the node will be discarded from the tree. + /// + /// The `flags` are a bit field of [SemanticsFlag]s that apply to this node. + /// + /// 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. + /// + /// The `label` is a string that describes this node. The `value` property + /// describes the current value of the node as a string. The `increasedValue` + /// string will become the `value` string after a [SemanticsAction.increase] + /// action is performed. The `decreasedValue` string will become the `value` + /// string after a [SemanticsAction.decrease] action is performed. The `hint` + /// string describes what result an action performed on this node has. The + /// reading direction of all these strings is given by `textDirection`. + /// + /// The fields 'textSelectionBase' and 'textSelectionExtent' describe the + /// currently selected text within `value`. + /// + /// The field `platformViewId` references the platform view, whose semantics + /// nodes will be added as children to this node. If a platform view is + /// specified, `childrenInHitTestOrder` and `childrenInTraversalOrder` must be + /// empty. + /// + /// For scrollable nodes `scrollPosition` describes the current scroll + /// position in logical pixel. `scrollExtentMax` and `scrollExtentMin` + /// describe the maximum and minimum in-rage values that `scrollPosition` can + /// be. Both or either may be infinity to indicate unbound scrolling. The + /// value for `scrollPosition` can (temporarily) be outside this range, for + /// example during an overscroll. `scrollChildren` is the count of the + /// total number of child nodes that contribute semantics and `scrollIndex` + /// is the index of the first visible child node that contributes semantics. + /// + /// The `rect` is the region occupied by this node in its own coordinate + /// system. + /// + /// The `transform` is a matrix that maps this node's coordinate system into + /// its parent's coordinate system. + /// + /// The `elevation` describes the distance in z-direction between this node + /// and the `elevation` of the parent. + /// + /// The `thickness` describes how much space this node occupies in the + /// z-direction starting at `elevation`. Basically, in the z-direction the + /// node starts at `elevation` above the parent and ends at `elevation` + + /// `thickness` above the parent. + void updateNode({ + int id, + int flags, + int actions, + int textSelectionBase, + int textSelectionExtent, + int platformViewId, + int scrollChildren, + int scrollIndex, + double scrollPosition, + double scrollExtentMax, + double scrollExtentMin, + double elevation, + double thickness, + Rect rect, + String label, + String hint, + String value, + String increasedValue, + String decreasedValue, + TextDirection textDirection, + Float64List transform, + Int32List childrenInTraversalOrder, + Int32List childrenInHitTestOrder, + Int32List additionalActions, + }) { + throw UnimplementedError(); + } + + /// Update the custom semantics action associated with the given `id`. + /// + /// The name of the action exposed to the user is the `label`. For overridden + /// standard actions this value is ignored. + /// + /// The `hint` should describe what happens when an action occurs, not the + /// manner in which a tap is accomplished. For example, use "delete" instead + /// of "double tap to delete". + /// + /// The text direction of the `hint` and `label` is the same as the global + /// window. + /// + /// For overridden standard actions, `overrideId` corresponds with a + /// [SemanticsAction.index] value. For custom actions this argument should not + /// be provided. + void updateCustomAction({int id, String label, String hint, int overrideId = -1}) { + assert(id != null); + assert(overrideId != null); + throw UnimplementedError(); + } + + /// 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. + SemanticsUpdate build() { + throw UnimplementedError(); + } +} + +/// An opaque object representing a batch of semantics updates. +/// +/// 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. +class SemanticsUpdate { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To create a SemanticsUpdate object, use a [SemanticsUpdateBuilder]. + SemanticsUpdate._(); + + /// Releases the resources used by this semantics update. + /// + /// After calling this function, the semantics update is cannot be used + /// further. + void dispose() { + throw UnimplementedError(); + } +} diff --git a/lib/stub_ui/text.dart b/lib/stub_ui/text.dart new file mode 100644 index 0000000000000..dd07b232980ca --- /dev/null +++ b/lib/stub_ui/text.dart @@ -0,0 +1,1441 @@ +// Copyright 2013 The Flutter 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 dart.ui; + +/// Whether to slant the glyphs in the font +enum FontStyle { + /// Use the upright glyphs + normal, + + /// Use glyphs designed for slanting + italic, +} + +/// The thickness of the glyphs used to draw the text +class FontWeight { + const FontWeight._(this.index); + + /// The encoded integer value of this font weight. + final int index; + + /// Thin, the least thick + static const FontWeight w100 = const FontWeight._(0); + + /// Extra-light + static const FontWeight w200 = const FontWeight._(1); + + /// Light + static const FontWeight w300 = const FontWeight._(2); + + /// Normal / regular / plain + static const FontWeight w400 = const FontWeight._(3); + + /// Medium + static const FontWeight w500 = const FontWeight._(4); + + /// Semi-bold + static const FontWeight w600 = const FontWeight._(5); + + /// Bold + static const FontWeight w700 = const FontWeight._(6); + + /// Extra-bold + static const FontWeight w800 = const FontWeight._(7); + + /// Black, the most thick + static const FontWeight w900 = const FontWeight._(8); + + /// The default font weight. + static const FontWeight normal = w400; + + /// A commonly used font weight that is heavier than normal. + static const FontWeight bold = w700; + + /// A list of all the font weights. + static const List values = const [ + w100, w200, w300, w400, w500, w600, w700, w800, w900 + ]; + + /// Linearly interpolates between two font weights. + /// + /// Rather than using fractional weights, the interpolation rounds to the + /// nearest weight. + /// + /// Any null values for `a` or `b` are interpreted as equivalent to [normal] + /// (also known as [w400]). + /// + /// The `t` argument represents position on the timeline, with 0.0 meaning + /// that the interpolation has not started, returning `a` (or something + /// equivalent to `a`), 1.0 meaning that the interpolation has finished, + /// returning `b` (or something equivalent to `b`), and values in between + /// meaning that the interpolation is at the relevant point on the timeline + /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and + /// 1.0, so negative values and values greater than 1.0 are valid (and can + /// easily be generated by curves such as [Curves.elasticInOut]). The result + /// is clamped to the range [w100]–[w900]. + /// + /// 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); + return values[lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t).round().clamp(0, 8)]; + } + + @override + String toString() { + return const { + 0: 'FontWeight.w100', + 1: 'FontWeight.w200', + 2: 'FontWeight.w300', + 3: 'FontWeight.w400', + 4: 'FontWeight.w500', + 5: 'FontWeight.w600', + 6: 'FontWeight.w700', + 7: 'FontWeight.w800', + 8: 'FontWeight.w900', + }[index]; + } +} + +/// Whether and how to align text horizontally. +// The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign. +enum TextAlign { + /// Align the text on the left edge of the container. + left, + + /// Align the text on the right edge of the container. + right, + + /// Align the text in the center of the container. + center, + + /// Stretch lines of text that end with a soft line break to fill the width of + /// the container. + /// + /// Lines that end with hard line breaks are aligned towards the [start] edge. + justify, + + /// Align the text on the leading edge of the container. + /// + /// For left-to-right text ([TextDirection.ltr]), this is the left edge. + /// + /// For right-to-left text ([TextDirection.rtl]), this is the right edge. + start, + + /// Align the text on the trailing edge of the container. + /// + /// For left-to-right text ([TextDirection.ltr]), this is the right edge. + /// + /// For right-to-left text ([TextDirection.rtl]), this is the left edge. + end, +} + +/// A horizontal line used for aligning text. +enum TextBaseline { + /// The horizontal line used to align the bottom of glyphs for alphabetic characters. + alphabetic, + + /// The horizontal line used to align ideographic characters. + ideographic, +} + +/// A linear decoration to draw near the text. +class TextDecoration { + const TextDecoration._(this._mask); + + /// 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) + mask |= decoration._mask; + return new TextDecoration._(mask); + } + + final int _mask; + + /// Whether this decoration will paint at least as much decoration as the given decoration. + bool contains(TextDecoration other) { + return (_mask | other._mask) == _mask; + } + + /// Do not draw a decoration + static const TextDecoration none = const TextDecoration._(0x0); + + /// Draw a line underneath each line of text + static const TextDecoration underline = const TextDecoration._(0x1); + + /// Draw a line above each line of text + static const TextDecoration overline = const TextDecoration._(0x2); + + /// Draw a line through each line of text + static const TextDecoration lineThrough = const TextDecoration._(0x4); + + @override + bool operator ==(dynamic other) { + if (other is! TextDecoration) + return false; + final TextDecoration typedOther = other; + return _mask == typedOther._mask; + } + + @override + int get hashCode => _mask.hashCode; + + @override + String toString() { + if (_mask == 0) + return 'TextDecoration.none'; + final List values = []; + if (_mask & underline._mask != 0) + values.add('underline'); + if (_mask & overline._mask != 0) + values.add('overline'); + if (_mask & lineThrough._mask != 0) + values.add('lineThrough'); + if (values.length == 1) + return 'TextDecoration.${values[0]}'; + return 'TextDecoration.combine([${values.join(", ")}])'; + } +} + +/// The style in which to draw a text decoration +enum TextDecorationStyle { + /// Draw a solid line + solid, + + /// Draw two lines + double, + + /// Draw a dotted line + dotted, + + /// Draw a dashed line + dashed, + + /// Draw a sinusoidal line + wavy +} + +/// 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; +} + +// This encoding must match the C++ version of ParagraphBuilder::pushStyle. +// +// The encoded array buffer has 8 elements. +// +// - Element 0: A bit field where the ith bit indicates wheter 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. +// +// - Element 1: The |color| in ARGB with 8 bits per channel. +// +// - Element 2: A bit field indicating which text decorations are present in +// the |textDecoration| list. The ith bit is set if there's a TextDecoration +// with enum index i in the list. +// +// - Element 3: The |decorationColor| in ARGB with 8 bits per channel. +// +// - Element 4: The bit field of the |decorationStyle|. +// +// - Element 5: The index of the |fontWeight|. +// +// - Element 6: The enum index of the |fontStyle|. +// +// - Element 7: The enum index of the |textBaseline|. +// +Int32List _encodeTextStyle( + Color color, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, + double decorationThickness, + FontWeight fontWeight, + FontStyle fontStyle, + TextBaseline textBaseline, + String fontFamily, + List fontFamilyFallback, + double fontSize, + double letterSpacing, + double wordSpacing, + double height, + Locale locale, + Paint background, + Paint foreground, + List shadows +) { + final Int32List result = new Int32List(8); + if (color != null) { + result[0] |= 1 << 1; + result[1] = color.value; + } + if (decoration != null) { + result[0] |= 1 << 2; + result[2] = decoration._mask; + } + if (decorationColor != null) { + result[0] |= 1 << 3; + result[3] = decorationColor.value; + } + if (decorationStyle != null) { + result[0] |= 1 << 4; + result[4] = decorationStyle.index; + } + if (decorationThickness != null) { + result[0] |= 1 << 5; + } + if (fontWeight != null) { + result[0] |= 1 << 6; + result[5] = fontWeight.index; + } + if (fontStyle != null) { + result[0] |= 1 << 7; + result[6] = fontStyle.index; + } + if (textBaseline != null) { + result[0] |= 1 << 8; + result[7] = textBaseline.index; + } + if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { + result[0] |= 1 << 9; + // Passed separately to native. + } + if (fontSize != null) { + result[0] |= 1 << 9; + // Passed separately to native. + } + if (letterSpacing != null) { + result[0] |= 1 << 10; + // Passed separately to native. + } + if (wordSpacing != null) { + result[0] |= 1 << 11; + // Passed separately to native. + } + if (height != null) { + result[0] |= 1 << 12; + // Passed separately to native. + } + if (locale != null) { + result[0] |= 1 << 13; + // Passed separately to native. + } + if (background != null) { + result[0] |= 1 << 14; + // Passed separately to native. + } + if (foreground != null) { + result[0] |= 1 << 15; + // Passed separately to native. + } + if (shadows != null) { + result[0] |= 1 << 16; + // Passed separately to native. + } + return result; +} + +/// An opaque object that determines the size, position, and rendering of text. +class TextStyle { + /// Creates a new TextStyle object. + /// + /// * `color`: The color to use when painting the text. If this is specified, `foreground` must be null. + /// * `decoration`: The decorations to paint near the text (e.g., an underline). + /// * `decorationColor`: The color in which to paint the text decorations. + /// * `decorationStyle`: The style in which to paint the text decorations (e.g., dashed). + /// * `decorationThickness`: The thickness of the decoration as a muliplier on the thickness specified by the font. + /// * `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). + /// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). If a `fontFamilyFallback` is + /// provided and `fontFamily` is not, then the first font family in `fontFamilyFallback` will take the position of + /// the preferred font family. When a higher priority font cannot be found or does not contain a glyph, a lower + /// priority font will be used. + /// * `fontFamilyFallback`: An ordered list of the names of the fonts to fallback on when a glyph cannot + /// be found in a higher priority font. When the `fontFamily` is null, the first font family in this list + /// is used as the preferred font. Internally, the 'fontFamily` is concatenated to the front of this list. + /// When no font family is provided through 'fontFamilyFallback' (null or empty) or `fontFamily`, then the + /// platform default font will be used. + /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting the text. + /// * `letterSpacing`: The amount of space (in logical pixels) to add between each letter. + /// * `wordSpacing`: The amount of space (in logical pixels) to add at each sequence of white-space (i.e. between each word). + /// * `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. + /// * `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. + TextStyle({ + Color color, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, + double decorationThickness, + FontWeight fontWeight, + FontStyle fontStyle, + TextBaseline textBaseline, + String fontFamily, + List fontFamilyFallback, + double fontSize, + double letterSpacing, + double wordSpacing, + double height, + Locale locale, + Paint background, + Paint foreground, + List shadows, + }) : 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".' + ), + _encoded = _encodeTextStyle( + color, + decoration, + decorationColor, + decorationStyle, + decorationThickness, + fontWeight, + fontStyle, + textBaseline, + fontFamily, + fontFamilyFallback, + fontSize, + letterSpacing, + wordSpacing, + height, + locale, + background, + foreground, + shadows, + ), + _fontFamily = fontFamily ?? '', + _fontFamilyFallback = fontFamilyFallback, + _fontSize = fontSize, + _letterSpacing = letterSpacing, + _wordSpacing = wordSpacing, + _height = height, + _decorationThickness = decorationThickness, + _locale = locale, + _background = background, + _foreground = foreground, + _shadows = shadows; + + final Int32List _encoded; + final String _fontFamily; + final List _fontFamilyFallback; + final double _fontSize; + final double _letterSpacing; + final double _wordSpacing; + final double _height; + final double _decorationThickness; + final Locale _locale; + final Paint _background; + final Paint _foreground; + final List _shadows; + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other is! TextStyle) + return false; + final TextStyle typedOther = other; + if (_fontFamily != typedOther._fontFamily || + _fontSize != typedOther._fontSize || + _letterSpacing != typedOther._letterSpacing || + _wordSpacing != typedOther._wordSpacing || + _height != typedOther._height || + _decorationThickness != typedOther._decorationThickness || + _locale != typedOther._locale || + _background != typedOther._background || + _foreground != typedOther._foreground) + return false; + for (int index = 0; index < _encoded.length; index += 1) { + if (_encoded[index] != typedOther._encoded[index]) + return false; + } + if (!_listEquals(_shadows, typedOther._shadows)) + return false; + if (!_listEquals(_fontFamilyFallback, typedOther._fontFamilyFallback)) + return false; + return true; + } + + @override + int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, _shadows, _decorationThickness); + + @override + String toString() { + return 'TextStyle(' + 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? new Color(_encoded[1]) : "unspecified"}, ' + 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? new TextDecoration._(_encoded[2]) : "unspecified"}, ' + 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? new Color(_encoded[3]) : "unspecified"}, ' + 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' + 'decorationThickness: ${_encoded[0] & 0x00020 == 0x00020 ? _decorationThickness : "unspecified"}, ' + 'fontWeight: ${ _encoded[0] & 0x00040 == 0x00040 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' + 'fontStyle: ${ _encoded[0] & 0x00080 == 0x00080 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' + 'textBaseline: ${ _encoded[0] & 0x00100 == 0x00100 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' + 'fontFamily: ${ _encoded[0] & 0x00200 == 0x00200 + && _fontFamily != null ? _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"}' + ')'; + } +} + +// This encoding must match the C++ version ParagraphBuilder::build. +// +// The encoded array buffer has 6 elements. +// +// - Element 0: A bit mask indicating which fields are non-null. +// Bit 0 is unused. Bits 1-n are set if the corresponding index in the +// encoded array is non-null. The remaining bits represent fields that +// are passed separately from the array. +// +// - Element 1: The enum index of the |textAlign|. +// +// - Element 2: The enum index of the |textDirection|. +// +// - Element 3: The index of the |fontWeight|. +// +// - Element 4: The enum index of the |fontStyle|. +// +// - Element 5: The value of |maxLines|. +// +Int32List _encodeParagraphStyle( + TextAlign textAlign, + TextDirection textDirection, + int maxLines, + String fontFamily, + double fontSize, + double height, + FontWeight fontWeight, + FontStyle fontStyle, + StrutStyle strutStyle, + String ellipsis, + Locale locale, +) { + final Int32List result = new Int32List(6); // also update paragraph_builder.cc + if (textAlign != null) { + result[0] |= 1 << 1; + result[1] = textAlign.index; + } + if (textDirection != null) { + result[0] |= 1 << 2; + result[2] = textDirection.index; + } + if (fontWeight != null) { + result[0] |= 1 << 3; + result[3] = fontWeight.index; + } + if (fontStyle != null) { + result[0] |= 1 << 4; + result[4] = fontStyle.index; + } + if (maxLines != null) { + result[0] |= 1 << 5; + result[5] = maxLines; + } + if (fontFamily != null) { + result[0] |= 1 << 6; + // Passed separately to native. + } + if (fontSize != null) { + result[0] |= 1 << 7; + // Passed separately to native. + } + if (height != null) { + result[0] |= 1 << 8; + // Passed separately to native. + } + if (strutStyle != null) { + result[0] |= 1 << 9; + // Passed separately to native. + } + if (ellipsis != null) { + result[0] |= 1 << 10; + // Passed separately to native. + } + if (locale != null) { + result[0] |= 1 << 11; + // Passed separately to native. + } + return result; +} + +/// An opaque object that determines the configuration used by +/// [ParagraphBuilder] to position lines within a [Paragraph] of text. +class ParagraphStyle { + /// Creates a new ParagraphStyle object. + /// + /// * `textAlign`: The alignment of the text within the lines of the + /// paragraph. If the last line is ellipsized (see `ellipsis` below), the + /// alignment is applied to that line after it has been truncated but before + /// the ellipsis has been added. + // See: https://github.com/flutter/flutter/issues/9819 + /// + /// * `textDirection`: The directionality of the text, left-to-right (e.g. + /// Norwegian) or right-to-left (e.g. Hebrew). This controls the overall + /// directionality of the paragraph, as well as the meaning of + /// [TextAlign.start] and [TextAlign.end] in the `textAlign` field. + /// + /// * `maxLines`: The maximum number of lines painted. Lines beyond this + /// number are silently dropped. For example, if `maxLines` is 1, then only + /// one line is rendered. If `maxLines` is null, but `ellipsis` is not null, + /// then lines after the first one that overflows the width constraints are + /// dropped. The width constraints are those set in the + /// [ParagraphConstraints] object passed to the [Paragraph.layout] method. + /// + /// * `fontFamily`: The fallback name of the font to use when painting the text + /// (e.g., Roboto). This is used when there is no [TextStyle]. + /// + /// * `fontSize`: The fallback size of glyphs (in logical pixels) to + /// use when painting the text. This is used when there is no [TextStyle]. + /// + /// * `height`: The height of the spans as a multiplier of the font size. The + /// fallback height to use when no height is provided in through + /// [TextStyle.height]. + /// + /// * `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). + /// + /// * `strutStyle`: The properties of the strut. Strut defines a set of minimum + /// vertical line height related metrics and can be used to obtain more + /// advanced line spacing behavior. + /// + /// * `ellipsis`: String used to ellipsize overflowing text. If `maxLines` is + /// not null, then the `ellipsis`, if any, is applied to the last rendered + /// line, if that line overflows the width constraints. If `maxLines` is + /// null, then the `ellipsis` is applied to the first line that overflows + /// the width constraints, and subsequent lines are dropped. The width + /// constraints are those set in the [ParagraphConstraints] object passed to + /// the [Paragraph.layout] method. The empty string and the null value are + /// considered equivalent and turn off this behavior. + /// + /// * `locale`: The locale used to select region-specific glyphs. + ParagraphStyle({ + TextAlign textAlign, + TextDirection textDirection, + int maxLines, + String fontFamily, + double fontSize, + double height, + FontWeight fontWeight, + FontStyle fontStyle, + StrutStyle strutStyle, + String ellipsis, + Locale locale, + }) : _encoded = _encodeParagraphStyle( + textAlign, + textDirection, + maxLines, + fontFamily, + fontSize, + height, + fontWeight, + fontStyle, + strutStyle, + ellipsis, + locale, + ), + _fontFamily = fontFamily, + _fontSize = fontSize, + _height = height, + _strutStyle = strutStyle, + _ellipsis = ellipsis, + _locale = locale; + + final Int32List _encoded; + final String _fontFamily; + final double _fontSize; + final double _height; + final StrutStyle _strutStyle; + final String _ellipsis; + final Locale _locale; + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + final ParagraphStyle typedOther = other; + if (_fontFamily != typedOther._fontFamily || + _fontSize != typedOther._fontSize || + _height != typedOther._height || + _strutStyle != typedOther._strutStyle || + _ellipsis != typedOther._ellipsis || + _locale != typedOther._locale) + return false; + for (int index = 0; index < _encoded.length; index += 1) { + if (_encoded[index] != typedOther._encoded[index]) + return false; + } + return true; + } + + @override + int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale); + + @override + String toString() { + return '$runtimeType(' + 'textAlign: ${ _encoded[0] & 0x002 == 0x002 ? TextAlign.values[_encoded[1]] : "unspecified"}, ' + 'textDirection: ${ _encoded[0] & 0x004 == 0x004 ? TextDirection.values[_encoded[2]] : "unspecified"}, ' + 'fontWeight: ${ _encoded[0] & 0x008 == 0x008 ? FontWeight.values[_encoded[3]] : "unspecified"}, ' + 'fontStyle: ${ _encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]] : "unspecified"}, ' + 'maxLines: ${ _encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' + 'fontFamily: ${ _encoded[0] & 0x040 == 0x040 ? _fontFamily : "unspecified"}, ' + 'fontSize: ${ _encoded[0] & 0x080 == 0x080 ? _fontSize : "unspecified"}, ' + 'height: ${ _encoded[0] & 0x100 == 0x100 ? "${_height}x" : "unspecified"}, ' + 'ellipsis: ${ _encoded[0] & 0x200 == 0x200 ? "\"$_ellipsis\"" : "unspecified"}, ' + 'locale: ${ _encoded[0] & 0x400 == 0x400 ? _locale : "unspecified"}' + ')'; + } +} + +// Serialize strut properties into ByteData. This encoding errs towards +// 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. +// +// We serialize this more thoroughly than ParagraphStyle because it is +// much more likely that the strut is empty/null and we wish to add +// minimal overhead for non-strut cases. +ByteData _encodeStrut( + String fontFamily, + List fontFamilyFallback, + double fontSize, + double height, + double leading, + FontWeight fontWeight, + FontStyle fontStyle, + bool forceStrutHeight) { + if (fontFamily == null && + fontSize == null && + height == null && + leading == null && + fontWeight == null && + fontStyle == null && + forceStrutHeight == null) + return ByteData(0); + + final ByteData data = ByteData(15); // Max size is 15 bytes + int bitmask = 0; // 8 bit mask + int byteCount = 1; + if (fontWeight != null) { + bitmask |= 1 << 0; + data.setInt8(byteCount, fontWeight.index); + byteCount += 1; + } + if (fontStyle != null) { + bitmask |= 1 << 1; + data.setInt8(byteCount, fontStyle.index); + byteCount += 1; + } + if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)){ + bitmask |= 1 << 2; + // passed separately to native + } + if (fontSize != null) { + bitmask |= 1 << 3; + data.setFloat32(byteCount, fontSize, _kFakeHostEndian); + byteCount += 4; + } + if (height != null) { + bitmask |= 1 << 4; + data.setFloat32(byteCount, height, _kFakeHostEndian); + byteCount += 4; + } + if (leading != null) { + bitmask |= 1 << 5; + 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; + } + + data.setInt8(0, bitmask); + + return ByteData.view(data.buffer, 0, byteCount); +} + +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. + /// + /// * `height`: The minimum height of the line boxes, as a multiplier of the + /// font size. The lines of the paragraph will be at least `(height + 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 + /// `(height + 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 `(height + leading) * fontSize`. Otherwise, it + /// will be determined by the Ascent + half-leading of the first text. + StrutStyle({ + String fontFamily, + List fontFamilyFallback, + double fontSize, + double height, + double leading, + FontWeight fontWeight, + FontStyle fontStyle, + bool forceStrutHeight, + }) : _encoded = _encodeStrut( + fontFamily, + fontFamilyFallback, + fontSize, + height, + leading, + fontWeight, + fontStyle, + forceStrutHeight, + ), + _fontFamily = fontFamily, + _fontFamilyFallback = fontFamilyFallback; + + final ByteData _encoded; // Most of the data for strut is encoded. + final String _fontFamily; + final List _fontFamilyFallback; + + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + final StrutStyle typedOther = other; + if (_fontFamily != typedOther._fontFamily) + return false; + final Int8List encodedList = _encoded.buffer.asInt8List(); + final Int8List otherEncodedList = typedOther._encoded.buffer.asInt8List(); + for (int index = 0; index < _encoded.lengthInBytes; index += 1) { + if (encodedList[index] != otherEncodedList[index]) + return false; + } + if (!_listEquals(_fontFamilyFallback, typedOther._fontFamilyFallback)) + return false; + return true; + } + + @override + int get hashCode => hashValues(hashList(_encoded.buffer.asInt8List()), _fontFamily); + +} + +/// A direction in which text flows. +/// +/// Some languages are written from the left to the right (for example, English, +/// Tamil, or Chinese), while others are written from the right to the left (for +/// example Aramaic, Hebrew, or Urdu). Some are also written in a mixture, for +/// example Arabic is mostly written right-to-left, with numerals written +/// left-to-right. +/// +/// The text direction must be provided to APIs that render text or lay out +/// boxes horizontally, so that they can determine which direction to start in: +/// either right-to-left, [TextDirection.rtl]; or left-to-right, +/// [TextDirection.ltr]. +/// +/// ## Design discussion +/// +/// Flutter is designed to address the needs of applications written in any of +/// the world's currently-used languages, whether they use a right-to-left or +/// left-to-right writing direction. Flutter does not support other writing +/// modes, such as vertical text or boustrophedon text, as these are rarely used +/// in computer programs. +/// +/// It is common when developing user interface frameworks to pick a default +/// text direction — typically left-to-right, the direction most familiar to the +/// engineers working on the framework — because this simplifies the development +/// of applications on the platform. Unfortunately, this frequently results in +/// the platform having unexpected left-to-right biases or assumptions, as +/// engineers will typically miss places where they need to support +/// right-to-left text. This then results in bugs that only manifest in +/// right-to-left environments. +/// +/// In an effort to minimize the extent to which Flutter experiences this +/// category of issues, the lowest levels of the Flutter framework do not have a +/// default text reading direction. Any time a reading direction is necessary, +/// for example when text is to be displayed, or when a +/// writing-direction-dependent value is to be interpreted, the reading +/// direction must be explicitly specified. Where possible, such as in `switch` +/// statements, the right-to-left case is listed first, to avoid the impression +/// that it is an afterthought. +/// +/// At the higher levels (specifically starting at the widgets library), an +/// ambient [Directionality] is introduced, which provides a default. Thus, for +/// instance, a [Text] widget in the scope of a [MaterialApp] widget does not +/// need to be given an explicit writing direction. The [Directionality.of] +/// static method can be used to obtain the ambient text direction for a +/// particular [BuildContext]. +/// +/// ### Known left-to-right biases in Flutter +/// +/// Despite the design intent described above, certain left-to-right biases have +/// nonetheless crept into Flutter's design. These include: +/// +/// * The [Canvas] origin is at the top left, and the x-axis increases in a +/// left-to-right direction. +/// +/// * The default localization in the widgets and material libraries is +/// American English, which is left-to-right. +/// +/// ### Visual properties vs directional properties +/// +/// Many classes in the Flutter framework are offered in two versions, a +/// visually-oriented variant, and a text-direction-dependent variant. For +/// example, [EdgeInsets] is described in terms of top, left, right, and bottom, +/// while [EdgeInsetsDirectional] is described in terms of top, start, end, and +/// bottom, where start and end correspond to right and left in right-to-left +/// text and left and right in left-to-right text. +/// +/// There are distinct use cases for each of these variants. +/// +/// Text-direction-dependent variants are useful when developing user interfaces +/// that should "flip" with the text direction. For example, a paragraph of text +/// in English will typically be left-aligned and a quote will be indented from +/// the left, while in Arabic it will be right-aligned and indented from the +/// right. Both of these cases are described by the direction-dependent +/// [TextAlign.start] and [EdgeInsetsDirectional.start]. +/// +/// In contrast, the visual variants are useful when the text direction is known +/// and not affected by the reading direction. For example, an application +/// giving driving directions might show a "turn left" arrow on the left and a +/// "turn right" arrow on the right — and would do so whether the application +/// was localized to French (left-to-right) or Hebrew (right-to-left). +/// +/// In practice, it is also expected that many developers will only be +/// targeting one language, and in that case it may be simpler to think in +/// visual terms. +// The order of this enum must match the order of the values in TextDirection.h's TextDirection. +enum TextDirection { + /// The text flows from right to left (e.g. Arabic, Hebrew). + rtl, + + /// The text flows from left to right (e.g., English, French). + ltr, +} + +/// A rectangle enclosing a run of text. +/// +/// This is similar to [Rect] but includes an inherent [TextDirection]. +class TextBox { + /// Creates an object that describes a box containing text. + const TextBox.fromLTRBD( + this.left, + this.top, + this.right, + this.bottom, + this.direction, + ); + + TextBox._( + this.left, + this.top, + this.right, + this.bottom, + int directionIndex, + ) : direction = TextDirection.values[directionIndex]; + + /// The left edge of the text box, irrespective of direction. + /// + /// To get the leading edge (which may depend on the [direction]), consider [start]. + final double left; + + /// The top edge of the text box. + final double top; + + /// The right edge of the text box, irrespective of direction. + /// + /// To get the trailing edge (which may depend on the [direction]), consider [end]. + final double right; + + /// The bottom edge of the text box. + final double bottom; + + /// The direction in which text inside this box flows. + final TextDirection direction; + + /// Returns a rect of the same size as this box. + Rect toRect() => new Rect.fromLTRB(left, top, right, bottom); + + /// The [left] edge of the box for left-to-right text; the [right] edge of the box for right-to-left text. + /// + /// See also: + /// + /// * [direction], which specifies the text direction. + double get start { + return (direction == TextDirection.ltr) ? left : right; + } + + /// The [right] edge of the box for left-to-right text; the [left] edge of the box for right-to-left text. + /// + /// See also: + /// + /// * [direction], which specifies the text direction. + double get end { + return (direction == TextDirection.ltr) ? right : left; + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + final TextBox typedOther = other; + return typedOther.left == left + && typedOther.top == top + && typedOther.right == right + && typedOther.bottom == bottom + && typedOther.direction == direction; + } + + @override + int get hashCode => hashValues(left, top, right, bottom, direction); + + @override + String toString() => 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)'; +} + +/// A way to disambiguate a [TextPosition] when its offset could match two +/// different locations in the rendered string. +/// +/// For example, at an offset where the rendered text wraps, there are two +/// visual positions that the offset could represent: one prior to the line +/// break (at the end of the first line) and one after the line break (at the +/// start of the second line). A text affinity disambiguates between these two +/// cases. +/// +/// This affects only line breaks caused by wrapping, not explicit newline +/// characters. For newline characters, the position is fully specified by the +/// offset alone, and there is no ambiguity. +/// +/// [TextAffinity] also affects bidirectional text at the interface between LTR +/// and RTL text. Consider the following string, where the lowercase letters +/// will be displayed as LTR and the uppercase letters RTL: "helloHELLO". When +/// rendered, the string would appear visually as "helloOLLEH". An offset of 5 +/// would be ambiguous without a corresponding [TextAffinity]. Looking at the +/// string in code, the offset represents the position just after the "o" and +/// just before the "H". When rendered, this offset could be either in the +/// middle of the string to the right of the "o" or at the end of the string to +/// the right of the "H". +enum TextAffinity { + /// The position has affinity for the upstream side of the text position, i.e. + /// in the direction of the beginning of the string. + /// + /// In the example of an offset at the place where text is wrapping, upstream + /// indicates the end of the first line. + /// + /// In the bidirectional text example "helloHELLO", an offset of 5 with + /// [TextAffinity] upstream would appear in the middle of the rendered text, + /// just to the right of the "o". See the definition of [TextAffinity] for the + /// full example. + upstream, + + /// The position has affinity for the downstream side of the text position, + /// i.e. in the direction of the end of the string. + /// + /// In the example of an offset at the place where text is wrapping, + /// downstream indicates the beginning of the second line. + /// + /// In the bidirectional text example "helloHELLO", an offset of 5 with + /// [TextAffinity] downstream would appear at the end of the rendered text, + /// just to the right of the "H". See the definition of [TextAffinity] for the + /// full example. + downstream, +} + +/// A position in a string of text. +/// +/// A TextPosition can be used to locate a position in a string in code (using +/// the [offset] property), and it can also be used to locate the same position +/// visually in a rendered string of text (using [offset] and, when needed to +/// resolve ambiguity, [affinity]). +/// +/// The location of an offset in a rendered string is ambiguous in two cases. +/// One happens when rendered text is forced to wrap. In this case, the offset +/// where the wrap occurs could visually appear either at the end of the first +/// line or the beginning of the second line. The second way is with +/// bidirectional text. An offset at the interface between two different text +/// directions could have one of two locations in the rendered text. +/// +/// See the documentation for [TextAffinity] for more information on how +/// TextAffinity disambiguates situations like these. +class TextPosition { + /// Creates an object representing a particular position in a string. + /// + /// The arguments must not be null (so the [offset] argument is required). + const TextPosition({ + this.offset, + this.affinity: TextAffinity.downstream, + }) : assert(offset != null), + assert(affinity != null); + + /// The index of the character that immediately follows the position in the + /// string representation of the text. + /// + /// For example, given the string `'Hello'`, offset 0 represents the cursor + /// being before the `H`, while offset 5 represents the cursor being just + /// after the `o`. + final int offset; + + /// Disambiguates cases where the position in the string given by [offset] + /// could represent two different visual positions in the rendered text. For + /// example, this can happen when text is forced to wrap, or when one string + /// of text is rendered with multiple text directions. + /// + /// See the documentation for [TextAffinity] for more information on how + /// TextAffinity disambiguates situations like these. + final TextAffinity affinity; + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) + return false; + final TextPosition typedOther = other; + return typedOther.offset == offset + && typedOther.affinity == affinity; + } + + @override + int get hashCode => hashValues(offset, affinity); + + @override + String toString() { + return '$runtimeType(offset: $offset, affinity: $affinity)'; + } +} + +/// Layout constraints for [Paragraph] objects. +/// +/// Instances of this class are typically used with [Paragraph.layout]. +/// +/// The only constraint that can be specified is the [width]. See the discussion +/// at [width] for more details. +class ParagraphConstraints { + /// Creates constraints for laying out a paragraph. + /// + /// The [width] argument must not be null. + const ParagraphConstraints({ + this.width, + }) : assert(width != null); + + /// The width the paragraph should use whey computing the positions of glyphs. + /// + /// If possible, the paragraph will select a soft line break prior to reaching + /// this width. If no soft line break is available, the paragraph will select + /// a hard line break prior to reaching this width. If that would force a line + /// break without any characters having been placed (i.e. if the next + /// character to be laid out does not fit within the given width constraint) + /// then the next character is allowed to overflow the width constraint and a + /// forced line break is placed after it (even if an explicit line break + /// follows). + /// + /// The width influences how ellipses are applied. See the discussion at [new + /// ParagraphStyle] for more details. + /// + /// This width is also used to position glyphs according to the [TextAlign] + /// alignment described in the [ParagraphStyle] used when building the + /// [Paragraph] with a [ParagraphBuilder]. + final double width; + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) + return false; + final ParagraphConstraints typedOther = other; + return typedOther.width == width; + } + + @override + int get hashCode => width.hashCode; + + @override + String toString() => '$runtimeType(width: $width)'; +} + +/// Defines various ways to vertically bound the boxes returned by +/// [Paragraph.getBoxesForRange]. +enum BoxHeightStyle { + /// Provide tight bounding boxes that fit heights per run. This style may result + /// in uneven bounding boxes that do not nicely connect with adjacent boxes. + tight, + + /// The height of the boxes will be the maximum height of all runs in the + /// line. All boxes in the same line will be the same height. This does not + /// guarantee that the boxes will cover the entire vertical height of the line + /// when there is additional line spacing. + /// + /// See [RectHeightStyle.includeLineSpacingTop], [RectHeightStyle.includeLineSpacingMiddle], + /// and [RectHeightStyle.includeLineSpacingBottom] for styles that will cover + /// the entire line. + max, + + /// Extends the top and bottom edge of the bounds to fully cover any line + /// spacing. + /// + /// 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} + /// 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 + /// and below the last line due to no additional space present there. + /// {@endtemplate} + includeLineSpacingMiddle, + + /// Extends the top edge of the bounds to fully cover any line spacing. + /// + /// The line spacing will be added to the top of the box. + /// + /// {@macro flutter.dart:ui.rectHeightStyle.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} + includeLineSpacingBottom, +} + +/// Defines various ways to horizontally bound the boxes returned by +/// [Paragraph.getBoxesForRange]. +enum BoxWidthStyle { + // Provide tight bounding boxes that fit widths to the runs of each line + // independently. + tight, + + /// Adds up to two additional boxes as needed at the beginning and/or end + /// of each line so that the widths of the boxes in line are the same width + /// as the widest line in the paragraph. The additional boxes on each line + /// are only added when the relevant box at the relevant edge of that line + /// does not span the maximum width of the paragraph. + max, +} + +/// A paragraph of text. +/// +/// A paragraph retains the size and position of each glyph in the text and can +/// be efficiently resized and painted. +/// +/// To create a [Paragraph] object, use a [ParagraphBuilder]. +/// +/// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph] +/// method. +class Paragraph { + /// This class is created by the engine, and should not be instantiated + /// or extended directly. + /// + /// To create a [Paragraph] object, use a [ParagraphBuilder]. + Paragraph._(); + + /// The amount of horizontal space this paragraph occupies. + /// + /// Valid only after [layout] has been called. + double get width { + throw UnimplementedError(); + } + + /// The amount of vertical space this paragraph occupies. + /// + /// Valid only after [layout] has been called. + double get height { + throw UnimplementedError(); + } + + /// The minimum width that this paragraph could be without failing to paint + /// its contents within itself. + /// + /// Valid only after [layout] has been called. + double get minIntrinsicWidth { + throw UnimplementedError(); + } + + /// Returns the smallest width beyond which increasing the width never + /// decreases the height. + /// + /// Valid only after [layout] has been called. + double get maxIntrinsicWidth { + throw UnimplementedError(); + } + + /// The distance from the top of the paragraph to the alphabetic + /// baseline of the first line, in logical pixels. + double get alphabeticBaseline { + throw UnimplementedError(); + } + + /// The distance from the top of the paragraph to the ideographic + /// baseline of the first line, in logical pixels. + double get ideographicBaseline { + throw UnimplementedError(); + } + + /// True if there is more vertical content, but the text was truncated, either + /// because we reached `maxLines` lines of text or because the `maxLines` was + /// null, `ellipsis` was not null, and one of the lines exceeded the width + /// constraint. + /// + /// See the discussion of the `maxLines` and `ellipsis` arguments at [new + /// ParagraphStyle]. + bool get didExceedMaxLines { + throw UnimplementedError(); + } + + /// Computes the size and position of each glyph in the paragraph. + /// + /// The [ParagraphConstraints] control how wide the text is allowed to be. + void layout(ParagraphConstraints constraints) { + throw UnimplementedError(); + } + + /// Returns a list of text boxes that enclose the given text range. + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization + /// of how the boxes are bound vertically and horizontally. Both style + /// parameters default to the tight option, which will provide close-fitting + /// boxes and will not account for any line spacing. + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. + /// + /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. + List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}) { + throw UnimplementedError(); + } + + /// Returns the text position closest to the given offset. + TextPosition getPositionForOffset(Offset offset) { + throw UnimplementedError(); + } + + /// Returns the [start, end] of the word at the given offset. Characters not + /// part of a word, such as spaces, symbols, and punctuation, have word breaks + /// on both sides. In such cases, this method will return [offset, offset+1]. + /// Word boundaries are defined more precisely in Unicode Standard Annex #29 + /// http://www.unicode.org/reports/tr29/#Word_Boundaries + List getWordBoundary(int offset) { + throw UnimplementedError(); + } +} + +/// Builds a [Paragraph] containing text with the given styling information. +/// +/// To set the paragraph's alignment, truncation, and ellipsizing behavior, pass +/// an appropriately-configured [ParagraphStyle] object to the [new +/// ParagraphBuilder] constructor. +/// +/// Then, call combinations of [pushStyle], [addText], and [pop] to add styled +/// text to the object. +/// +/// Finally, call [build] to obtain the constructed [Paragraph] object. After +/// this point, the builder is no longer usable. +/// +/// After constructing a [Paragraph], call [Paragraph.layout] on it and then +/// paint it with [Canvas.drawParagraph]. +class ParagraphBuilder { + + /// Creates a [ParagraphBuilder] object, which is used to create a + /// [Paragraph]. + ParagraphBuilder(ParagraphStyle style); + + /// Applies the given style to the added text until [pop] is called. + /// + /// See [pop] for details. + void pushStyle(TextStyle style) { + throw UnimplementedError(); + } + + /// 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. + void pop() { + throw UnimplementedError(); + } + + /// Adds the given text to the paragraph. + /// + /// The text will be styled according to the current stack of text styles. + void addText(String text) { + throw UnimplementedError(); + } + + /// 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. + Paragraph build() { + throw UnimplementedError(); + } +} + +/// Loads a font from a buffer and makes it available for rendering text. +/// +/// * `list`: A list of bytes containing the font file. +/// * `fontFamily`: The family name used to identify the font in text styles. +/// If this is not provided, then the family name will be extracted from the font file. +Future loadFontFromList(Uint8List list, {String fontFamily}) { + throw UnimplementedError(); +} diff --git a/lib/stub_ui/ui.dart b/lib/stub_ui/ui.dart new file mode 100644 index 0000000000000..f87f1ec6f2d58 --- /dev/null +++ b/lib/stub_ui/ui.dart @@ -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. + +/// Built-in types and core primitives for a Flutter application. +/// +/// To use, import `dart:ui`. +/// +/// 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. +library dart.ui; + +import 'dart:_internal' hide Symbol; // ignore: import_internal_library, unused_import +import 'dart:async'; +import 'dart:collection' as collection; +import 'dart:convert'; +import 'dart:developer' as developer; +import 'dart:io'; // ignore: unused_import +import 'dart:isolate' show SendPort; +import 'dart:math' as math; +import 'dart:typed_data'; + +part 'compositing.dart'; +part 'geometry.dart'; +part 'hash_codes.dart'; +part 'hooks.dart'; +part 'isolate_name_server.dart'; +part 'lerp.dart'; +part 'natives.dart'; +part 'painting.dart'; +part 'plugins.dart'; +part 'pointer.dart'; +part 'semantics.dart'; +part 'text.dart'; +part 'versions.dart'; +part 'window.dart'; diff --git a/lib/stub_ui/versions.dart b/lib/stub_ui/versions.dart new file mode 100644 index 0000000000000..3f555262b8f9a --- /dev/null +++ b/lib/stub_ui/versions.dart @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of dart.ui; + +/// Wraps version information for Dart, Skia and Flutter. +class Versions { + /// Private constructor to capture the versions. + Versions._( + this.dartVersion, + this.skiaVersion, + this.flutterEngineVersion + ) : assert(dartVersion != null), + assert(skiaVersion != null), + assert(flutterEngineVersion != null); + + final String dartVersion; + final String skiaVersion; + final String flutterEngineVersion; +} + +/// [Versions] singleton. This object exposes Dart, Skia and +/// Flutter engine versions. +final Versions versions = Versions._('', '', ''); diff --git a/lib/stub_ui/window.dart b/lib/stub_ui/window.dart new file mode 100644 index 0000000000000..f3dedc642887e --- /dev/null +++ b/lib/stub_ui/window.dart @@ -0,0 +1,961 @@ +// Copyright 2013 The Flutter 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 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.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); + +/// 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. + /// + /// Android apps in this state should assume that they may enter the + /// [suspending] state at any time. + paused, + + /// The application will be suspended momentarily. + /// + /// When the application is in this state, the engine will not call the + /// [Window.onBeginFrame] and [Window.onDrawFrame] callbacks. + /// + /// On iOS, this state is currently unused. + suspending, +} + +/// 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._({ this.left, this.top, this.right, 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 = const WindowPadding._(left: 0.0, top: 0.0, right: 0.0, bottom: 0.0); + + @override + String toString() { + return '$runtimeType(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: +/// +/// * [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 = const Locale('fr', 'CH'); + /// const Locale canadianFrench = const 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: + /// + /// * [new 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](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), + 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: + /// + /// * [new Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String get languageCode => _replaceDeprecatedLanguageSubtag(_languageCode); + final String _languageCode; + + static String _replaceDeprecatedLanguageSubtag(String languageCode) { + // This switch statement is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2018-08-08. + switch (languageCode) { + case 'in': return 'id'; // Indonesian; deprecated 1989-01-01 + case 'iw': return 'he'; // Hebrew; deprecated 1989-01-01 + case 'ji': return 'yi'; // Yiddish; deprecated 1989-01-01 + case 'jw': return 'jv'; // Javanese; deprecated 2001-08-13 + case 'mo': return 'ro'; // Moldavian, Moldovan; deprecated 2008-11-22 + case 'aam': return 'aas'; // Aramanik; deprecated 2015-02-12 + case 'adp': return 'dz'; // Adap; deprecated 2015-02-12 + case 'aue': return 'ktz'; // =/Kx'au//'ein; deprecated 2015-02-12 + case 'ayx': return 'nun'; // Ayi (China); deprecated 2011-08-16 + case 'bgm': return 'bcg'; // Baga Mboteni; deprecated 2016-05-30 + case 'bjd': return 'drl'; // Bandjigali; deprecated 2012-08-12 + case 'ccq': return 'rki'; // Chaungtha; deprecated 2012-08-12 + case 'cjr': return 'mom'; // Chorotega; deprecated 2010-03-11 + case 'cka': return 'cmr'; // Khumi Awa Chin; deprecated 2012-08-12 + case 'cmk': return 'xch'; // Chimakum; deprecated 2010-03-11 + case 'coy': return 'pij'; // Coyaima; deprecated 2016-05-30 + case 'cqu': return 'quh'; // Chilean Quechua; deprecated 2016-05-30 + case 'drh': return 'khk'; // Darkhat; deprecated 2010-03-11 + case 'drw': return 'prs'; // Darwazi; deprecated 2010-03-11 + case 'gav': return 'dev'; // Gabutamon; deprecated 2010-03-11 + case 'gfx': return 'vaj'; // Mangetti Dune !Xung; deprecated 2015-02-12 + case 'ggn': return 'gvr'; // Eastern Gurung; deprecated 2016-05-30 + case 'gti': return 'nyc'; // Gbati-ri; deprecated 2015-02-12 + case 'guv': return 'duz'; // Gey; deprecated 2016-05-30 + case 'hrr': return 'jal'; // Horuru; deprecated 2012-08-12 + case 'ibi': return 'opa'; // Ibilo; deprecated 2012-08-12 + case 'ilw': return 'gal'; // Talur; deprecated 2013-09-10 + case 'jeg': return 'oyb'; // Jeng; deprecated 2017-02-23 + case 'kgc': return 'tdf'; // Kasseng; deprecated 2016-05-30 + case 'kgh': return 'kml'; // Upper Tanudan Kalinga; deprecated 2012-08-12 + case 'koj': return 'kwv'; // Sara Dunjo; deprecated 2015-02-12 + case 'krm': return 'bmf'; // Krim; deprecated 2017-02-23 + case 'ktr': return 'dtp'; // Kota Marudu Tinagas; deprecated 2016-05-30 + case 'kvs': return 'gdj'; // Kunggara; deprecated 2016-05-30 + case 'kwq': return 'yam'; // Kwak; deprecated 2015-02-12 + case 'kxe': return 'tvd'; // Kakihum; deprecated 2015-02-12 + case 'kzj': return 'dtp'; // Coastal Kadazan; deprecated 2016-05-30 + case 'kzt': return 'dtp'; // Tambunan Dusun; deprecated 2016-05-30 + case 'lii': return 'raq'; // Lingkhim; deprecated 2015-02-12 + case 'lmm': return 'rmx'; // Lamam; deprecated 2014-02-28 + case 'meg': return 'cir'; // Mea; deprecated 2013-09-10 + case 'mst': return 'mry'; // Cataelano Mandaya; deprecated 2010-03-11 + case 'mwj': return 'vaj'; // Maligo; deprecated 2015-02-12 + case 'myt': return 'mry'; // Sangab Mandaya; deprecated 2010-03-11 + case 'nad': return 'xny'; // Nijadali; deprecated 2016-05-30 + case 'ncp': return 'kdz'; // Ndaktup; deprecated 2018-03-08 + case 'nnx': return 'ngv'; // Ngong; deprecated 2015-02-12 + case 'nts': return 'pij'; // Natagaimas; deprecated 2016-05-30 + case 'oun': return 'vaj'; // !O!ung; deprecated 2015-02-12 + case 'pcr': return 'adx'; // Panang; deprecated 2013-09-10 + case 'pmc': return 'huw'; // Palumata; deprecated 2016-05-30 + case 'pmu': return 'phr'; // Mirpur Panjabi; deprecated 2015-02-12 + case 'ppa': return 'bfy'; // Pao; deprecated 2016-05-30 + case 'ppr': return 'lcq'; // Piru; deprecated 2013-09-10 + case 'pry': return 'prt'; // Pray 3; deprecated 2016-05-30 + case 'puz': return 'pub'; // Purum Naga; deprecated 2014-02-28 + case 'sca': return 'hle'; // Sansu; deprecated 2012-08-12 + case 'skk': return 'oyb'; // Sok; deprecated 2017-02-23 + case 'tdu': return 'dtp'; // Tempasuk Dusun; deprecated 2016-05-30 + case 'thc': return 'tpo'; // Tai Hang Tong; deprecated 2016-05-30 + case 'thx': return 'oyb'; // The; deprecated 2015-02-12 + case 'tie': return 'ras'; // Tingal; deprecated 2011-08-16 + case 'tkk': return 'twm'; // Takpa; deprecated 2011-08-16 + case 'tlw': return 'weo'; // South Wemale; deprecated 2012-08-12 + case 'tmp': return 'tyj'; // Tai Mène; deprecated 2016-05-30 + case 'tne': return 'kak'; // Tinoc Kallahan; deprecated 2016-05-30 + case 'tnf': return 'prs'; // Tangshewi; deprecated 2010-03-11 + case 'tsf': return 'taj'; // Southwestern Tamang; deprecated 2015-02-12 + case 'uok': return 'ema'; // Uokha; deprecated 2015-02-12 + case 'xba': return 'cax'; // Kamba (Brazil); deprecated 2016-05-30 + case 'xia': return 'acn'; // Xiandao; deprecated 2013-09-10 + case 'xkh': return 'waw'; // Karahawyana; deprecated 2016-05-30 + case 'xsj': return 'suj'; // Subi; deprecated 2015-02-12 + case 'ybd': return 'rki'; // Yangbye; deprecated 2012-08-12 + case 'yma': return 'lrr'; // Yamphe; deprecated 2012-08-12 + case 'ymt': return 'mtm'; // Mator-Taygi-Karagas; deprecated 2015-02-12 + case 'yos': return 'zom'; // Yos; deprecated 2013-09-10 + case 'yuu': return 'yug'; // Yugh; deprecated 2014-02-28 + default: return languageCode; + } + } + + /// 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: + /// + /// * [new 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: + /// + /// * [new Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String get countryCode => _replaceDeprecatedRegionSubtag(_countryCode); + final String _countryCode; + + static String _replaceDeprecatedRegionSubtag(String regionCode) { + // This switch statement is generated by //flutter/tools/gen_locale.dart + // Mappings generated for language subtag registry as of 2018-08-08. + switch (regionCode) { + case 'BU': return 'MM'; // Burma; deprecated 1989-12-05 + case 'DD': return 'DE'; // German Democratic Republic; deprecated 1990-10-30 + case 'FX': return 'FR'; // Metropolitan France; deprecated 1997-07-14 + case 'TP': return 'TL'; // East Timor; deprecated 2002-05-20 + case 'YD': return 'YE'; // Democratic Yemen; deprecated 1990-08-14 + case 'ZR': return 'CD'; // Zaire; deprecated 1997-07-14 + default: return regionCode; + } + } + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other is! Locale) + return false; + final Locale typedOther = other; + return languageCode == typedOther.languageCode + && scriptCode == typedOther.scriptCode + && countryCode == typedOther.countryCode; + } + + @override + int get hashCode => hashValues(languageCode, scriptCode, countryCode); + + @override + String toString() { + final StringBuffer out = StringBuffer(languageCode); + if (scriptCode != null) + out.write('_$scriptCode'); + if (_countryCode != null) + out.write('_$countryCode'); + return out.toString(); + } +} + +/// The most basic interface to the host operating system's user interface. +/// +/// There is a single Window instance in the system, which you can +/// obtain from the [window] property. +class Window { + Window._(); + + /// 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. + /// + /// Device pixels are also referred to as physical pixels. Logical pixels are + /// also referred to as device-independent or resolution-independent pixels. + /// + /// By definition, there are roughly 38 logical pixels per centimeter, or + /// about 96 logical pixels per inch, of the physical display. The value + /// returned by [devicePixelRatio] is ultimately obtained either from the + /// hardware itself, the device drivers, or a hard-coded value stored in the + /// operating system or firmware, and may be inaccurate, sometimes by a + /// significant margin. + /// + /// The Flutter framework operates in logical pixels, so it is rarely + /// necessary to directly deal with this property. + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + double get devicePixelRatio => _devicePixelRatio; + double _devicePixelRatio = 1.0; + + /// The dimensions of the rectangle into which the application will be drawn, + /// in physical pixels. + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// At startup, the size of the application window 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 a view into + /// how much of each side of the application may be obscured by system UI. + /// + /// See also: + /// + /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to + /// observe when this value changes. + Size get physicalSize => _physicalSize; + Size _physicalSize = Size.zero; + + /// 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. + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// 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 view insets in material + /// design applications. + WindowPadding get viewInsets => _viewInsets; + WindowPadding _viewInsets = WindowPadding.zero; + + /// 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). + /// + /// When this changes, [onMetricsChanged] is called. + /// + /// 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; + + /// A callback that is invoked whenever the [devicePixelRatio], + /// [physicalSize], [padding], or [viewInsets] 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. + VoidCallback get onMetricsChanged => _onMetricsChanged; + VoidCallback _onMetricsChanged; + Zone _onMetricsChangedZone; + set onMetricsChanged(VoidCallback callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// 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` 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; + } + + /// 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 => _locales; + List _locales; + + /// 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; + set onLocaleChanged(VoidCallback callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// 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 => _initialLifecycleState; + String _initialLifecycleState; + + /// 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; + + /// 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; + set onPlatformBrightnessChanged(VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// 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 => _textScaleFactor; + double _textScaleFactor = 1.0; + + /// The setting indicating whether time should always be shown in the 24-hour + /// format. + /// + /// This option is used by [showTimePicker]. + bool get alwaysUse24HourFormat => _alwaysUse24HourFormat; + bool _alwaysUse24HourFormat = false; + + /// 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; + set onTextScaleFactorChanged(VoidCallback callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// 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. + /// + /// The [onDrawFrame] callback is invoked immediately after [onBeginFrame], + /// after draining any microtasks (e.g. completions of any [Future]s) queued + /// by the [onBeginFrame] handler. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + FrameCallback get onBeginFrame => _onBeginFrame; + FrameCallback _onBeginFrame; + Zone _onBeginFrameZone; + set onBeginFrame(FrameCallback callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// 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. + /// + /// The framework invokes this callback in the same zone in which the + /// callback was set. + /// + /// See also: + /// + /// * [SchedulerBinding], the Flutter framework class which manages the + /// scheduling of frames. + /// * [RendererBinding], the Flutter framework class which manages layout and + /// painting. + VoidCallback get onDrawFrame => _onDrawFrame; + VoidCallback _onDrawFrame; + Zone _onDrawFrameZone; + set onDrawFrame(VoidCallback callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// 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; + set onPointerDataPacket(PointerDataPacketCallback callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// 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 { + throw UnimplementedError(); + } + + /// 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() { + throw UnimplementedError(); + } + + /// 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. + void render(Scene scene) { + throw UnimplementedError(); + } + + /// 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. + bool get semanticsEnabled => _semanticsEnabled; + bool _semanticsEnabled = false; + + /// 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; + set onSemanticsEnabledChanged(VoidCallback callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// 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; + set onSemanticsAction(SemanticsActionCallback callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// Additional accessibility features that may be enabled by the platform. + AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; + AccessibilityFeatures _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 _onAccessibilityFlagsChangedZone; + set onAccessibilityFeaturesChanged(VoidCallback callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFlagsChangedZone = Zone.current; + } + + /// 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. + void updateSemantics(SemanticsUpdate update) { + throw UnimplementedError(); + } + + /// 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) { + throw UnimplementedError(); + } + + /// 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) { + throw UnimplementedError(); + } + + /// Called whenever this window 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. + PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; + PlatformMessageCallback _onPlatformMessage; + Zone _onPlatformMessageZone; + set onPlatformMessage(PlatformMessageCallback callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } +} + +/// Additional accessibility features that may be enabled by the platform. +/// +/// It is not possible to enable these settings from Flutter, instead they are +/// used by the platform to indicate that additional accessibility features are +/// enabled. +class AccessibilityFeatures { + const AccessibilityFeatures._(this._index); + + static const int _kAccessibleNavigation = 1 << 0; + static const int _kInvertColorsIndex = 1 << 1; + static const int _kDisableAnimationsIndex = 1 << 2; + static const int _kBoldTextIndex = 1 << 3; + static const int _kReduceMotionIndex = 1 << 4; + + // A bitfield which represents each enabled feature. + final int _index; + + /// Whether there is a running accessibility service which is changing the + /// interaction model of the device. + /// + /// For example, TalkBack on Android and VoiceOver on iOS enable this flag. + bool get accessibleNavigation => _kAccessibleNavigation & _index != 0; + + /// The platform is inverting the colors of the application. + bool get invertColors => _kInvertColorsIndex & _index != 0; + + /// The platform is requesting that animations be disabled or simplified. + bool get disableAnimations => _kDisableAnimationsIndex & _index != 0; + + /// The platform is requesting that text be rendered at a bold font weight. + /// + /// Only supported on iOS. + bool get boldText => _kBoldTextIndex & _index != 0; + + /// The platform is requesting that certain animations be simplified and + /// parallax effects removed. + /// + /// Only supported on iOS. + bool get reduceMotion => _kReduceMotionIndex & _index != 0; + + @override + String toString() { + final List features = []; + if (accessibleNavigation) + features.add('accessibleNavigation'); + if (invertColors) + features.add('invertColors'); + if (disableAnimations) + features.add('disableAnimations'); + if (boldText) + features.add('boldText'); + if (reduceMotion) + features.add('reduceMotion'); + return 'AccessibilityFeatures$features'; + } + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) + return false; + final AccessibilityFeatures typedOther = other; + return _index == typedOther._index; + } + + @override + int get hashCode => _index.hashCode; +} + +/// Describes the contrast of a theme or color palette. +enum Brightness { + /// The color is dark and will require a light text color to achieve readable + /// contrast. + /// + /// For example, the color might be dark grey, requiring white text. + dark, + + /// The color is light and will require a dark text color to achieve readable + /// contrast. + /// + /// For example, the color might be bright white, requiring black text. + light, +} + +/// The [Window] singleton. This object exposes the size of the display, the +/// core scheduler API, the input event callback, the graphics drawing API, and +/// other such core services. +final Window window = new Window._(); diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 28791a35af619..cba7632ce0b60 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -15,6 +15,7 @@ source_set("ui") { "dart_ui.cc", "dart_ui.h", "dart_wrapper.h", + "io_manager.h", "isolate_name_server/isolate_name_server.cc", "isolate_name_server/isolate_name_server.h", "isolate_name_server/isolate_name_server_natives.cc", @@ -23,8 +24,8 @@ source_set("ui") { "painting/canvas.h", "painting/codec.cc", "painting/codec.h", - "painting/engine_layer.h", "painting/engine_layer.cc", + "painting/engine_layer.h", "painting/frame_info.cc", "painting/frame_info.h", "painting/gradient.cc", @@ -65,6 +66,7 @@ source_set("ui") { "semantics/semantics_update.h", "semantics/semantics_update_builder.cc", "semantics/semantics_update_builder.h", + "snapshot_delegate.h", "text/asset_manager_font_provider.cc", "text/asset_manager_font_provider.h", "text/font_collection.cc", @@ -81,6 +83,8 @@ source_set("ui") { "text/text_box.h", "ui_dart_state.cc", "ui_dart_state.h", + "versions.cc", + "versions.h", "window/platform_message.cc", "window/platform_message.h", "window/platform_message_response.cc", @@ -91,6 +95,7 @@ source_set("ui") { "window/pointer_data.h", "window/pointer_data_packet.cc", "window/pointer_data_packet.h", + "window/viewport_metrics.cc", "window/viewport_metrics.h", "window/window.cc", "window/window.h", @@ -101,14 +106,13 @@ source_set("ui") { deps = [ "$flutter_root/assets", "$flutter_root/common", + "$flutter_root/common/version", "$flutter_root/flow", "$flutter_root/fml", "$flutter_root/runtime:test_font", "//third_party/dart/runtime/bin:dart_io_api", "//third_party/rapidjson", "//third_party/skia", - "//third_party/skia:effects", - "//third_party/skia:gpu", "//third_party/tonic", ] if (is_fuchsia) { diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index 8c3d9261d69e5..9eb7c317485b0 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ part of dart.ui; /// /// Scene objects can be displayed on the screen using the /// [Window.render] method. +@pragma('vm:entry-point') class Scene extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @@ -55,14 +56,14 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// The objects are transformed by the given matrix before rasterization. /// /// See [pop] for details about the operation stack. - void pushTransform(Float64List matrix4) { + EngineLayer pushTransform(Float64List matrix4) { if (matrix4 == null) throw new ArgumentError('"matrix4" argument cannot be null'); if (matrix4.length != 16) throw new ArgumentError('"matrix4" must have 16 entries.'); - _pushTransform(matrix4); + return _pushTransform(matrix4); } - void _pushTransform(Float64List matrix4) native 'SceneBuilder_pushTransform'; + EngineLayer _pushTransform(Float64List matrix4) native 'SceneBuilder_pushTransform'; /// Pushes an offset operation onto the operation stack. /// @@ -77,16 +78,16 @@ 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]). - void pushClipRect(Rect rect, {Clip clipBehavior = Clip.antiAlias}) { + EngineLayer pushClipRect(Rect rect, {Clip clipBehavior = Clip.antiAlias}) { assert(clipBehavior != null); assert(clipBehavior != Clip.none); - _pushClipRect(rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index); + return _pushClipRect(rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index); } - void _pushClipRect(double left, - double right, - double top, - double bottom, - int clipBehavior) native 'SceneBuilder_pushClipRect'; + EngineLayer _pushClipRect(double left, + double right, + double top, + double bottom, + int clipBehavior) native 'SceneBuilder_pushClipRect'; /// Pushes a rounded-rectangular clip operation onto the operation stack. /// @@ -94,12 +95,12 @@ 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]). - void pushClipRRect(RRect rrect, {Clip clipBehavior = Clip.antiAlias}) { + EngineLayer pushClipRRect(RRect rrect, {Clip clipBehavior = Clip.antiAlias}) { assert(clipBehavior != null); assert(clipBehavior != Clip.none); - _pushClipRRect(rrect._value, clipBehavior.index); + return _pushClipRRect(rrect._value, clipBehavior.index); } - void _pushClipRRect(Float32List rrect, int clipBehavior) native 'SceneBuilder_pushClipRRect'; + EngineLayer _pushClipRRect(Float32List rrect, int clipBehavior) native 'SceneBuilder_pushClipRRect'; /// Pushes a path clip operation onto the operation stack. /// @@ -107,12 +108,12 @@ 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]). - void pushClipPath(Path path, {Clip clipBehavior = Clip.antiAlias}) { + EngineLayer pushClipPath(Path path, {Clip clipBehavior = Clip.antiAlias}) { assert(clipBehavior != null); assert(clipBehavior != Clip.none); - _pushClipPath(path, clipBehavior.index); + return _pushClipPath(path, clipBehavior.index); } - void _pushClipPath(Path path, int clipBehavior) native 'SceneBuilder_pushClipPath'; + EngineLayer _pushClipPath(Path path, int clipBehavior) native 'SceneBuilder_pushClipPath'; /// Pushes an opacity operation onto the operation stack. /// @@ -122,7 +123,10 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// opacity). /// /// See [pop] for details about the operation stack. - void pushOpacity(int alpha) native 'SceneBuilder_pushOpacity'; + EngineLayer pushOpacity(int alpha, {Offset offset = Offset.zero}) { + return _pushOpacity(alpha, offset.dx, offset.dy); + } + EngineLayer _pushOpacity(int alpha, double dx, double dy) native 'SceneBuilder_pushOpacity'; /// Pushes a color filter operation onto the operation stack. /// @@ -130,10 +134,10 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// blend mode. /// /// See [pop] for details about the operation stack. - void pushColorFilter(Color color, BlendMode blendMode) { - _pushColorFilter(color.value, blendMode.index); + EngineLayer pushColorFilter(Color color, BlendMode blendMode) { + return _pushColorFilter(color.value, blendMode.index); } - void _pushColorFilter(int color, int blendMode) native 'SceneBuilder_pushColorFilter'; + EngineLayer _pushColorFilter(int color, int blendMode) native 'SceneBuilder_pushColorFilter'; /// Pushes a backdrop filter operation onto the operation stack. /// @@ -141,7 +145,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// rasterizing the given objects. /// /// See [pop] for details about the operation stack. - void pushBackdropFilter(ImageFilter filter) native 'SceneBuilder_pushBackdropFilter'; + EngineLayer pushBackdropFilter(ImageFilter filter) native 'SceneBuilder_pushBackdropFilter'; /// Pushes a shader mask operation onto the operation stack. /// @@ -149,20 +153,20 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// rectangle using the given blend mode. /// /// See [pop] for details about the operation stack. - void pushShaderMask(Shader shader, Rect maskRect, BlendMode blendMode) { - _pushShaderMask(shader, - maskRect.left, - maskRect.right, - maskRect.top, - maskRect.bottom, - blendMode.index); + EngineLayer pushShaderMask(Shader shader, Rect maskRect, BlendMode blendMode) { + return _pushShaderMask(shader, + maskRect.left, + maskRect.right, + maskRect.top, + maskRect.bottom, + blendMode.index); } - void _pushShaderMask(Shader shader, - double maskRectLeft, - double maskRectRight, - double maskRectTop, - double maskRectBottom, - int blendMode) native 'SceneBuilder_pushShaderMask'; + EngineLayer _pushShaderMask(Shader shader, + double maskRectLeft, + double maskRectRight, + double maskRectTop, + double maskRectBottom, + int blendMode) native 'SceneBuilder_pushShaderMask'; /// Pushes a physical layer operation for an arbitrary shape onto the /// operation stack. @@ -177,7 +181,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// /// See [pop] for details about the operation stack, and [Clip] for different clip modes. // ignore: deprecated_member_use - EngineLayer pushPhysicalShape({ Path path, double elevation, Color color, Color shadowColor, Clip clipBehavior = defaultClipBehavior}) { + EngineLayer pushPhysicalShape({ Path path, double elevation, Color color, Color shadowColor, Clip clipBehavior = Clip.none}) { return _pushPhysicalShape(path, elevation, color.value, shadowColor?.value ?? 0xFF000000, clipBehavior.index); } EngineLayer _pushPhysicalShape(Path path, double elevation, int color, int shadowColor, int clipBehavior) native @@ -196,7 +200,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// All the engine layers that are in the subtree of the retained layer will /// be automatically appended to the current engine layer tree. /// - /// Therefore, when implementing a subclas of the [Layer] concept defined in + /// Therefore, when implementing a subclass of the [Layer] concept defined in /// the rendering layer of Flutter's framework, once this is called, there's /// no need to call [addToScene] for its children layers. EngineLayer addRetained(EngineLayer retainedLayer) native 'SceneBuilder_addRetained'; @@ -260,7 +264,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// Android view: When resizing an Android view there is a short period during /// which the framework cannot tell if the newest texture frame has the /// previous or new size, to workaround this the framework "freezes" the - /// texture just before resizing the Android view and unfreezes it when it is + /// texture just before resizing the Android view and un-freezes it when it is /// certain that a frame with the new size is ready. void addTexture(int textureId, { Offset offset: Offset.zero, double width: 0.0, double height: 0.0 , bool freeze: false}) { assert(offset != null, 'Offset argument was null'); @@ -270,7 +274,20 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// Adds a platform view (e.g an iOS UIView) to the scene. /// - /// This is work in progress and is not currently supported on any platform. + /// Only supported on iOS, this is currently a no-op on other platforms. + /// + /// On iOS this layer splits the current output surface into two surfaces, one for the scene nodes + /// preceding the platform view, and one for the scene nodes following the platform view. + /// + /// ## Performance impact + /// + /// Adding an additional surface doubles the amount of graphics memory directly used by Flutter + /// for output buffers. Quartz might allocated extra buffers for compositing the Flutter surfaces + /// and the platform view. + /// + /// With a platform view in the scene, Quartz has to composite the two Flutter surfaces and the + /// embedded UIView. In addition to that, on iOS versions greater than 9, the Flutter frames are + /// synchronized with the UIView frames adding additional performance overhead. void addPlatformView(int viewId, { Offset offset: Offset.zero, double width: 0.0, double height: 0.0}) { assert(offset != null, 'Offset argument was null'); _addPlatformView(offset.dx, offset.dy, width, height, viewId); @@ -353,7 +370,7 @@ class SceneHost extends NativeFieldWrapperClass2 { /// /// The export token is a dart:zircon Handle, but that type isn't /// available here. This is called by ChildViewConnection in - /// //topaz/public/lib/ui/flutter/. + /// //topaz/public/dart/fuchsia_scenic_flutter/. /// /// The scene host takes ownership of the provided export token handle. SceneHost(dynamic exportTokenHandle) { diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 735ce44197921..3ad02d3867cbf 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -1,20 +1,18 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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.h" -#include "flutter/fml/make_copyable.h" #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/painting/image.h" +#include "flutter/lib/ui/painting/picture.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/dart_library_natives.h" -#include "third_party/tonic/dart_persistent_value.h" -#include "third_party/tonic/logging/dart_invoke.h" namespace blink { @@ -53,126 +51,21 @@ void Scene::dispose() { ClearDartWrapper(); } -static sk_sp CreateSceneSnapshot(GrContext* context, - sk_sp picture, - const SkSize& size) { - TRACE_EVENT0("flutter", "CreateSceneSnapshot"); - auto image_info = - SkImageInfo::MakeN32Premul(SkISize::Make(size.width(), size.height())); - - sk_sp surface; - - if (context) { - surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info); - } - - if (!surface) { - surface = SkSurface::MakeRaster(image_info); - } - - if (!surface) { - return nullptr; - } - - auto canvas = surface->getCanvas(); - - if (!canvas) { - return nullptr; - } - - if (picture) { - canvas->drawPicture(picture.get()); - } - - auto snapshot = surface->makeImageSnapshot(); - - if (!snapshot) { - return nullptr; - } - - return snapshot->makeRasterImage(); -} - Dart_Handle Scene::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { - return tonic::ToDart("Image callback was invalid"); - } if (!m_layerTree) { return tonic::ToDart("Scene did not contain a layer tree."); } - if (width == 0 || height == 0) { - return tonic::ToDart("Image dimensions for scene were invalid."); - } - - auto dart_state = UIDartState::Current(); - - auto image_callback = std::make_unique( - dart_state, raw_image_callback); - - // We can't create an image on this task runner because we don't have a - // graphics context. Even if we did, it would be slow anyway. Also, this - // thread owns the sole reference to the layer tree. So we flatten the layer - // tree into a picture and use that as the thread transport mechanism. - - auto bounds_size = SkSize::Make(width, height); - auto picture = m_layerTree->Flatten(SkRect::MakeSize(bounds_size)); + auto picture = m_layerTree->Flatten(SkRect::MakeWH(width, height)); if (!picture) { - // Already in Dart scope. return tonic::ToDart("Could not flatten scene into a layer tree."); } - auto resource_context = dart_state->GetResourceContext(); - auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); - auto unref_queue = dart_state->GetSkiaUnrefQueue(); - - // The picture has been prepared on the UI thread. - dart_state->GetTaskRunners().GetIOTaskRunner()->PostTask( - fml::MakeCopyable([picture = std::move(picture), // - bounds_size, // - resource_context = std::move(resource_context), // - ui_task_runner = std::move(ui_task_runner), // - image_callback = std::move(image_callback), // - unref_queue = std::move(unref_queue) // - ]() mutable { - // Snapshot the picture on the IO thread that contains an optional - // GrContext. - auto image = CreateSceneSnapshot(resource_context.get(), - std::move(picture), bounds_size); - - // Send the image back to the UI thread for submission back to the - // framework. - ui_task_runner->PostTask( - fml::MakeCopyable([image = std::move(image), // - image_callback = std::move(image_callback), // - unref_queue = std::move(unref_queue) // - ]() mutable { - auto dart_state = image_callback->dart_state().lock(); - if (!dart_state) { - // The root isolate could have died in the meantime. - return; - } - tonic::DartState::Scope scope(dart_state); - - if (!image) { - tonic::DartInvoke(image_callback->Get(), {Dart_Null()}); - return; - } - - auto dart_image = CanvasImage::Create(); - dart_image->set_image({std::move(image), std::move(unref_queue)}); - auto raw_dart_image = tonic::ToDart(std::move(dart_image)); - - // All done! - tonic::DartInvoke(image_callback->Get(), {raw_dart_image}); - })); - })); - - return Dart_Null(); + return Picture::RasterizeToImage(picture, width, height, raw_image_callback); } std::unique_ptr Scene::takeLayerTree() { diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index bf1f1b78d826e..308a652c1e5ed 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 76137c464969d..39eabaaba8598 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -76,11 +76,15 @@ void SceneBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) { SceneBuilder::SceneBuilder() = default; SceneBuilder::~SceneBuilder() = default; -void SceneBuilder::pushTransform(const tonic::Float64List& matrix4) { +fml::RefPtr SceneBuilder::pushTransform( + tonic::Float64List& matrix4) { SkMatrix sk_matrix = ToSkMatrix(matrix4); - auto layer = std::make_unique(); + auto layer = std::make_shared(); layer->set_transform(sk_matrix); - PushLayer(std::move(layer)); + PushLayer(layer); + // matrix4 has to be released before we can return another Dart object + matrix4.Release(); + return EngineLayer::MakeRetained(layer); } fml::RefPtr SceneBuilder::pushOffset(double dx, double dy) { @@ -91,65 +95,78 @@ fml::RefPtr SceneBuilder::pushOffset(double dx, double dy) { return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushClipRect(double left, - double right, - double top, - double bottom, - int clipBehavior) { +fml::RefPtr SceneBuilder::pushClipRect(double left, + double right, + double top, + double bottom, + int clipBehavior) { SkRect clipRect = SkRect::MakeLTRB(left, top, right, bottom); flow::Clip clip_behavior = static_cast(clipBehavior); - auto layer = std::make_unique(clip_behavior); + auto layer = std::make_shared(clip_behavior); layer->set_clip_rect(clipRect); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushClipRRect(const RRect& rrect, int clipBehavior) { +fml::RefPtr SceneBuilder::pushClipRRect(const RRect& rrect, + int clipBehavior) { flow::Clip clip_behavior = static_cast(clipBehavior); - auto layer = std::make_unique(clip_behavior); + auto layer = std::make_shared(clip_behavior); layer->set_clip_rrect(rrect.sk_rrect); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushClipPath(const CanvasPath* path, int clipBehavior) { +fml::RefPtr SceneBuilder::pushClipPath(const CanvasPath* path, + int clipBehavior) { flow::Clip clip_behavior = static_cast(clipBehavior); FML_DCHECK(clip_behavior != flow::Clip::none); - auto layer = std::make_unique(clip_behavior); + auto layer = std::make_shared(clip_behavior); layer->set_clip_path(path->path()); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushOpacity(int alpha) { - auto layer = std::make_unique(); +fml::RefPtr SceneBuilder::pushOpacity(int alpha, + double dx, + double dy) { + auto layer = std::make_shared(); layer->set_alpha(alpha); - PushLayer(std::move(layer)); + layer->set_offset(SkPoint::Make(dx, dy)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushColorFilter(int color, int blendMode) { - auto layer = std::make_unique(); +fml::RefPtr SceneBuilder::pushColorFilter(int color, + int blendMode) { + auto layer = std::make_shared(); layer->set_color(static_cast(color)); layer->set_blend_mode(static_cast(blendMode)); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushBackdropFilter(ImageFilter* filter) { - auto layer = std::make_unique(); +fml::RefPtr SceneBuilder::pushBackdropFilter(ImageFilter* filter) { + auto layer = std::make_shared(); layer->set_filter(filter->filter()); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } -void SceneBuilder::pushShaderMask(Shader* shader, - double maskRectLeft, - double maskRectRight, - double maskRectTop, - double maskRectBottom, - int blendMode) { +fml::RefPtr SceneBuilder::pushShaderMask(Shader* shader, + double maskRectLeft, + double maskRectRight, + double maskRectTop, + double maskRectBottom, + int blendMode) { SkRect rect = SkRect::MakeLTRB(maskRectLeft, maskRectTop, maskRectRight, maskRectBottom); - auto layer = std::make_unique(); + auto layer = std::make_shared(); layer->set_shader(shader->shader()); layer->set_mask_rect(rect); layer->set_blend_mode(static_cast(blendMode)); - PushLayer(std::move(layer)); + PushLayer(layer); + return EngineLayer::MakeRetained(layer); } fml::RefPtr SceneBuilder::pushPhysicalShape(const CanvasPath* path, diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h index 8674d004ab5d8..2f59be6510f4f 100644 --- a/lib/ui/compositing/scene_builder.h +++ b/lib/ui/compositing/scene_builder.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -33,24 +33,25 @@ class SceneBuilder : public RefCountedDartWrappable { ~SceneBuilder() override; - void pushTransform(const tonic::Float64List& matrix4); + fml::RefPtr pushTransform(tonic::Float64List& matrix4); fml::RefPtr pushOffset(double dx, double dy); - void pushClipRect(double left, - double right, - double top, - double bottom, - int clipBehavior); - void pushClipRRect(const RRect& rrect, int clipBehavior); - void pushClipPath(const CanvasPath* path, int clipBehavior); - void pushOpacity(int alpha); - void pushColorFilter(int color, int blendMode); - void pushBackdropFilter(ImageFilter* filter); - void pushShaderMask(Shader* shader, - double maskRectLeft, - double maskRectRight, - double maskRectTop, - double maskRectBottom, - int blendMode); + fml::RefPtr pushClipRect(double left, + double right, + double top, + double bottom, + int clipBehavior); + fml::RefPtr pushClipRRect(const RRect& rrect, int clipBehavior); + fml::RefPtr pushClipPath(const CanvasPath* path, + int clipBehavior); + fml::RefPtr pushOpacity(int alpha, double dx = 0, double dy = 0); + fml::RefPtr pushColorFilter(int color, int blendMode); + fml::RefPtr pushBackdropFilter(ImageFilter* filter); + fml::RefPtr pushShaderMask(Shader* shader, + double maskRectLeft, + double maskRectRight, + double maskRectTop, + double maskRectBottom, + int blendMode); fml::RefPtr pushPhysicalShape(const CanvasPath* path, double elevation, int color, diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc index c9588eb8c504a..d0441354949c4 100644 --- a/lib/ui/compositing/scene_host.cc +++ b/lib/ui/compositing/scene_host.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/compositing/scene_host.h b/lib/ui/compositing/scene_host.h index 897a4b754b3ec..cad141d0941ba 100644 --- a/lib/ui/compositing/scene_host.h +++ b/lib/ui/compositing/scene_host.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index 7f1cd0c0ecf56..5145f11107733 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -323,7 +323,11 @@ void GetCallbackHandle(Dart_NativeArguments args) { std::string class_name = GetFunctionClassName(func); std::string library_path = GetFunctionLibraryUrl(func); - if (name.empty()) { + // `name` is empty if `func` can't be used as a callback. This is the case + // when `func` is not a function object or is not a static function. Anonymous + // closures (e.g. `(int a, int b) => a + b;`) also cannot be used as + // callbacks, so `func` must be a tear-off of a named static function. + if (!Dart_IsTearOff(func) || name.empty()) { Dart_SetReturnValue(args, Dart_Null()); return; } diff --git a/lib/ui/dart_runtime_hooks.h b/lib/ui/dart_runtime_hooks.h index 50247eec06734..d3beefa46bd36 100644 --- a/lib/ui/dart_runtime_hooks.h +++ b/lib/ui/dart_runtime_hooks.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 181f7345f8ef2..f6aa54c1cb946 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,6 +27,7 @@ #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/text/paragraph.h" #include "flutter/lib/ui/text/paragraph_builder.h" +#include "flutter/lib/ui/versions.h" #include "flutter/lib/ui/window/window.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/logging/dart_error.h" @@ -87,6 +88,7 @@ void DartUI::InitForGlobal() { SceneHost::RegisterNatives(g_natives); SemanticsUpdate::RegisterNatives(g_natives); SemanticsUpdateBuilder::RegisterNatives(g_natives); + Versions::RegisterNatives(g_natives); Vertices::RegisterNatives(g_natives); Window::RegisterNatives(g_natives); diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index e9a315ac3694e..67246f6ef8498 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -16,6 +16,7 @@ dart_ui_files = [ "$flutter_root/lib/ui/semantics.dart", "$flutter_root/lib/ui/text.dart", "$flutter_root/lib/ui/ui.dart", + "$flutter_root/lib/ui/versions.dart", "$flutter_root/lib/ui/window.dart", ] diff --git a/lib/ui/dart_ui.h b/lib/ui/dart_ui.h index dc5fecac3f5f6..8dd4a3270ed6e 100644 --- a/lib/ui/dart_ui.h +++ b/lib/ui/dart_ui.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/dart_wrapper.h b/lib/ui/dart_wrapper.h index ae20bcfa68d71..ef895513dda64 100644 --- a/lib/ui/dart_wrapper.h +++ b/lib/ui/dart_wrapper.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/geometry.dart b/lib/ui/geometry.dart index df3faa39850db..2e8a79980c2dc 100644 --- a/lib/ui/geometry.dart +++ b/lib/ui/geometry.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -73,7 +73,7 @@ abstract class OffsetBase { /// /// This is a partial ordering. It is possible for two values to be neither /// less, nor greater than, nor equal to, another. - bool operator >=(OffsetBase other) => _dx > other._dx && _dy >= other._dy; + bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy; /// Equality operator. Compares an [Offset] or [Size] to another [Offset] or /// [Size], and returns true if the horizontal and vertical values of the @@ -121,6 +121,15 @@ class Offset extends OffsetBase { /// and the second sets [dy], the vertical component. const Offset(double dx, double dy) : super(dx, dy); + /// Creates an offset from its [direction] and [distance]. + /// + /// The direction is in radians clockwise from the positive x-axis. + /// + /// The distance can be omitted, to create a unit vector (distance = 1.0). + factory Offset.fromDirection(double direction, [ double distance = 1.0 ]) { + return new Offset(distance * math.cos(direction), distance * math.sin(direction)); + } + /// The x component of the offset. /// /// The y component is given by [dy]. @@ -135,12 +144,12 @@ class Offset extends OffsetBase { /// /// If you need this value to compare it to another [Offset]'s distance, /// consider using [distanceSquared] instead, since it is cheaper to compute. - double get distance => math.sqrt(_dx * _dx + _dy * _dy); + double get distance => math.sqrt(dx * dx + dy * dy); /// The square of the magnitude of the offset. /// /// This is cheaper than computing the [distance] itself. - double get distanceSquared => _dx * _dx + _dy * _dy; + double get distanceSquared => dx * dx + dy * dy; /// The angle of this offset as radians clockwise from the positive x-axis, in /// the range -[pi] to [pi], assuming positive values of the x-axis go to the @@ -321,12 +330,12 @@ class Offset extends OffsetBase { if (other is! Offset) return false; final Offset typedOther = other; - return _dx == typedOther._dx && - _dy == typedOther._dy; + return dx == typedOther.dx && + dy == typedOther.dy; } @override - int get hashCode => hashValues(_dx, _dy); + int get hashCode => hashValues(dx, dy); @override String toString() => 'Offset(${dx?.toStringAsFixed(1)}, ${dy?.toStringAsFixed(1)})'; @@ -373,6 +382,30 @@ class Size extends OffsetBase { /// The vertical extent of this size. double get height => _dy; + /// The aspect ratio of this size. + /// + /// This returns the [width] divided by the [height]. + /// + /// If the [width] is zero, the result will be zero. If the [height] is zero + /// (and the [width] is not), the result will be [double.infinity] or + /// [double.negativeInfinity] as determined by the sign of [width]. + /// + /// See also: + /// + /// * [AspectRatio], a widget for giving a child widget a specific aspect + /// ratio. + /// * [FittedBox], a widget that (in most modes) attempts to maintain a + /// child widget's aspect ratio while changing its size. + double get aspectRatio { + if (height != 0.0) + return width / height; + if (width > 0.0) + return double.infinity; + if (width < 0.0) + return double.negativeInfinity; + return 0.0; + } + /// An empty size, one with a zero width and a zero height. static const Size zero = const Size(0.0, 0.0); @@ -660,7 +693,7 @@ class Rect { /// A rectangle with left, top, right, and bottom edges all at zero. static final Rect zero = new Rect._(); - static const double _giantScalar = 1.0E+9; // matches kGiantRect from default_layer_builder.cc + static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h /// A rectangle that covers the entire coordinate space. /// diff --git a/lib/ui/hash_codes.dart b/lib/ui/hash_codes.dart index bece69b0887e9..a3a24d7b7dd54 100644 --- a/lib/ui/hash_codes.dart +++ b/lib/ui/hash_codes.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/hooks.dart b/lib/ui/hooks.dart index 20980a0a1c41f..c7b18b6d34d60 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -1,18 +1,23 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + part of dart.ui; +// ignore: unused_element String _decodeUTF8(ByteData message) { return message != null ? utf8.decoder.convert(message.buffer.asUint8List()) : null; } +// ignore: unused_element dynamic _decodeJSON(String message) { return message != null ? json.decode(message) : null; } @pragma('vm:entry-point') +// ignore: unused_element void _updateWindowMetrics(double devicePixelRatio, double width, double height, @@ -42,24 +47,58 @@ void _updateWindowMetrics(double devicePixelRatio, typedef _LocaleClosure = String Function(); -String _localeClosure() => window._locale.toString(); +String _localeClosure() { + if (window.locale == null) { + return null; + } + return window.locale.toString(); +} @pragma('vm:entry-point') +// ignore: unused_element _LocaleClosure _getLocaleClosure() => _localeClosure; @pragma('vm:entry-point') -void _updateLocale(String languageCode, String countryCode, String scriptCode, String variantCode) { - window._locale = new Locale(languageCode, countryCode); +// ignore: unused_element +void _updateLocales(List locales) { + const int stringsPerLocale = 4; + final int numLocales = locales.length ~/ stringsPerLocale; + window._locales = new List(numLocales); + for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) { + final String countryCode = locales[localeIndex * stringsPerLocale + 1]; + final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; + + window._locales[localeIndex] = new Locale.fromSubtags( + languageCode: locales[localeIndex * stringsPerLocale], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + ); + } _invoke(window.onLocaleChanged, window._onLocaleChangedZone); } @pragma('vm:entry-point') +// ignore: unused_element void _updateUserSettingsData(String jsonData) { final Map data = json.decode(jsonData); + if (data.isEmpty) { + return; + } _updateTextScaleFactor(data['textScaleFactor'].toDouble()); _updateAlwaysUse24HourFormat(data['alwaysUse24HourFormat']); + _updatePlatformBrightness(data['platformBrightness']); +} + +@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); @@ -69,13 +108,20 @@ void _updateAlwaysUse24HourFormat(bool alwaysUse24HourFormat) { window._alwaysUse24HourFormat = alwaysUse24HourFormat; } +void _updatePlatformBrightness(String brightnessName) { + window._platformBrightness = brightnessName == 'dark' ? Brightness.dark : Brightness.light; + _invoke(window.onPlatformBrightnessChanged, window._onPlatformBrightnessChangedZone); +} + @pragma('vm:entry-point') +// ignore: unused_element void _updateSemanticsEnabled(bool enabled) { window._semanticsEnabled = enabled; _invoke(window.onSemanticsEnabledChanged, window._onSemanticsEnabledChangedZone); } @pragma('vm:entry-point') +// ignore: unused_element void _updateAccessibilityFeatures(int values) { final AccessibilityFeatures newFeatures = new AccessibilityFeatures._(values); if (newFeatures == window._accessibilityFeatures) @@ -102,12 +148,14 @@ void _dispatchPlatformMessage(String name, ByteData data, int responseId) { } @pragma('vm:entry-point') +// ignore: unused_element void _dispatchPointerDataPacket(ByteData packet) { if (window.onPointerDataPacket != null) _invoke1(window.onPointerDataPacket, window._onPointerDataPacketZone, _unpackPointerDataPacket(packet)); } @pragma('vm:entry-point') +// ignore: unused_element void _dispatchSemanticsAction(int id, int action, ByteData args) { _invoke3( window.onSemanticsAction, @@ -119,15 +167,45 @@ void _dispatchSemanticsAction(int id, int action, ByteData args) { } @pragma('vm:entry-point') +// ignore: unused_element void _beginFrame(int microseconds) { _invoke1(window.onBeginFrame, window._onBeginFrameZone, new Duration(microseconds: microseconds)); } @pragma('vm:entry-point') +// ignore: unused_element void _drawFrame() { _invoke(window.onDrawFrame, window._onDrawFrameZone); } +// 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); + +@pragma('vm:entry-point') +// ignore: unused_element +void _runMainZoned(Function startMainIsolateFunction, Function userMainFunction) { + startMainIsolateFunction((){ + runZoned>(() { + const List empty_args = []; + 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)(empty_args, ''); + } else if (userMainFunction is _UnaryFunction) { + (userMainFunction as dynamic)(empty_args); + } else { + userMainFunction(); + } + }, onError: (Object error, StackTrace stackTrace) { + _reportUnhandledException(error.toString(), stackTrace.toString()); + }); + }, null); +} + +void _reportUnhandledException(String error, String stackTrace) native 'Window_reportUnhandledException'; + /// Invokes [callback] inside the given [zone]. void _invoke(void callback(), Zone zone) { if (callback == null) @@ -157,6 +235,7 @@ void _invoke1(void callback(A a), Zone zone, A arg) { } /// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. +// ignore: unused_element void _invoke2(void callback(A1 a1, A2 a2), Zone zone, A1 arg1, A2 arg2) { if (callback == null) return; @@ -190,7 +269,7 @@ void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1 // // * pointer_data.cc // * FlutterView.java -const int _kPointerDataFieldCount = 19; +const int _kPointerDataFieldCount = 24; PointerDataPacket _unpackPointerDataPacket(ByteData packet) { const int kStride = Int64List.bytesPerElement; @@ -204,6 +283,7 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) { timeStamp: new 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), physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), @@ -214,12 +294,16 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) { 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) + 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); } diff --git a/lib/ui/io_manager.h b/lib/ui/io_manager.h new file mode 100644 index 0000000000000..cf260e0abb6b3 --- /dev/null +++ b/lib/ui/io_manager.h @@ -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. + +#ifndef FLUTTER_LIB_UI_IO_MANAGER_H_ +#define FLUTTER_LIB_UI_IO_MANAGER_H_ + +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace blink { +// Interface for methods that manage access to the resource GrContext and Skia +// unref queue. Meant to be implemented by the owner of the resource GrContext, +// i.e. the shell's IOManager. +class IOManager { + public: + virtual fml::WeakPtr GetResourceContext() const = 0; + + virtual fml::RefPtr GetSkiaUnrefQueue() const = 0; +}; + +} // namespace blink + +#endif // FLUTTER_LIB_UI_IO_MANAGER_H_ diff --git a/lib/ui/isolate_name_server.dart b/lib/ui/isolate_name_server.dart index ef32ec203d401..59d85877a1acd 100644 --- a/lib/ui/isolate_name_server.dart +++ b/lib/ui/isolate_name_server.dart @@ -1,34 +1,70 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 dart.ui; -/// Contains methods to allow for simple sharing of SendPorts across isolates. -abstract class IsolateNameServer { - /// Looks up the [SendPort] associated with a given name. Returns null - /// if the name does not exist. +/// Static methods to allow for simple sharing of [SendPort]s across [Isolate]s. +/// +/// All isolates share a global mapping of names to ports. An isolate can +/// register a [SendPort] with a given name using [registerPortWithName]; +/// another isolate can then look up that port using [lookupPortByName]. +/// +/// To create a [SendPort], first create a [ReceivePort], then use +/// [ReceivePort.sendPort]. +/// +/// Since multiple isolates can each obtain the same [SendPort] associated with +/// a particular [ReceivePort], the protocol built on top of this mechanism +/// should typically consist of a single message. If more elaborate two-way +/// communication or multiple-message communication is necessary, it is +/// recommended to establish a separate communication channel in that first +/// message (e.g. by passing a dedicated [SendPort]). +class IsolateNameServer { + // This class is only a namespace, and should not be instantiated or + // extended directly. + factory IsolateNameServer._() => null; + + /// Looks up the [SendPort] associated with a given name. + /// + /// Returns null if the name does not exist. To register the name in the first + /// place, consider [registerPortWithName]. /// - /// `name` must not be null. + /// The `name` argument must not be null. static SendPort lookupPortByName(String name) { assert(name != null, "'name' cannot be null."); return _lookupPortByName(name); } - /// Registers a SendPort with a given name. Returns true if registration is - /// successful, false if the name entry already exists. + /// Registers a [SendPort] with a given name. + /// + /// Returns true if registration is successful, and false if the name entry + /// already existed (in which case the earlier registration is left + /// unchanged). To remove a registration, consider [removePortNameMapping]. + /// + /// Once a port has been registered with a name, it can be obtained from any + /// [Isolate] using [lookupPortByName]. /// - /// `port` and `name` must not be null. + /// Multiple isolates should avoid attempting to register ports with the same + /// name, as there is an inherent race condition in doing so. + /// + /// The `port` and `name` arguments must not be null. static bool registerPortWithName(SendPort port, String name) { assert(port != null, "'port' cannot be null."); assert(name != null, "'name' cannot be null."); return _registerPortWithName(port, name); } - /// Removes a name to SendPort mapping given a name. Returns true if the - /// mapping was successfully removed, false if the mapping does not exist. + /// Removes a name-to-[SendPort] mapping given its name. + /// + /// Returns true if the mapping was successfully removed, false if the mapping + /// did not exist. To add a registration, consider [registerPortWithName]. + /// + /// Generally, removing a port name mapping is an inherently racy operation + /// (another isolate could have obtained the name just prior to the name being + /// removed, and thus would still be able to communicate over the port even + /// after it has been removed). /// - /// `name` must not be null. + /// The `name` argument must not be null. static bool removePortNameMapping(String name) { assert(name != null, "'name' cannot be null."); return _removePortNameMapping(name); diff --git a/lib/ui/isolate_name_server/isolate_name_server.cc b/lib/ui/isolate_name_server/isolate_name_server.cc index 9d78d145bb00f..deae4fe6f8f16 100644 --- a/lib/ui/isolate_name_server/isolate_name_server.cc +++ b/lib/ui/isolate_name_server/isolate_name_server.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,10 @@ namespace blink { +IsolateNameServer::IsolateNameServer() {} + +IsolateNameServer::~IsolateNameServer() = default; + Dart_Port IsolateNameServer::LookupIsolatePortByName(const std::string& name) { std::lock_guard lock(mutex_); return LookupIsolatePortByNameUnprotected(name); diff --git a/lib/ui/isolate_name_server/isolate_name_server.h b/lib/ui/isolate_name_server/isolate_name_server.h index 906d943328967..db62ad871f771 100644 --- a/lib/ui/isolate_name_server/isolate_name_server.h +++ b/lib/ui/isolate_name_server/isolate_name_server.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,7 +17,9 @@ namespace blink { class IsolateNameServer { public: - IsolateNameServer() {} + IsolateNameServer(); + + ~IsolateNameServer(); // Looks up the Dart_Port associated with a given name. Returns ILLEGAL_PORT // if the name does not exist. diff --git a/lib/ui/isolate_name_server/isolate_name_server_natives.cc b/lib/ui/isolate_name_server/isolate_name_server_natives.cc index 543cd168b5223..7f4467886f4fa 100644 --- a/lib/ui/isolate_name_server/isolate_name_server_natives.cc +++ b/lib/ui/isolate_name_server/isolate_name_server_natives.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/isolate_name_server/isolate_name_server_natives.h b/lib/ui/isolate_name_server/isolate_name_server_natives.h index 2ebbe6e445010..cc3e1438cfbee 100644 --- a/lib/ui/isolate_name_server/isolate_name_server_natives.h +++ b/lib/ui/isolate_name_server/isolate_name_server_natives.h @@ -1,10 +1,11 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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_ISOLATE_NAME_SERVER_NATIVES_H_ #define FLUTTER_LIB_UI_ISOLATE_NAME_SERVER_NATIVES_H_ +#include #include "third_party/dart/runtime/include/dart_api.h" namespace tonic { diff --git a/lib/ui/lerp.dart b/lib/ui/lerp.dart index 2107969be57bb..0c69476779b73 100644 --- a/lib/ui/lerp.dart +++ b/lib/ui/lerp.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/natives.dart b/lib/ui/natives.dart index 49a582c4e930f..27913bdd9a519 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -1,7 +1,9 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(dnfield): remove unused_element ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + part of dart.ui; // Corelib 'print' implementation. @@ -28,7 +30,7 @@ Future _scheduleFrame( } @pragma('vm:entry-point') -void _setupHooks() { +void _setupHooks() { // ignore: unused_element assert(() { // In debug mode, register the schedule frame extension. developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); @@ -36,13 +38,30 @@ void _setupHooks() { }()); } -void saveCompilationTrace(String filePath) { +/// Returns runtime Dart compilation trace as a UTF-8 encoded memory buffer. +/// +/// The buffer contains a list of symbols compiled by the Dart JIT at runtime up +/// to the point when this function was called. This list can be saved to a text +/// file and passed to tools such as `flutter build` or Dart `gen_snapshot` in +/// order to pre-compile this code offline. +/// +/// The list has one symbol per line of the following format: +/// `,,\n`. +/// +/// Here are some examples: +/// +/// ``` +/// dart:core,Duration,get:inMilliseconds +/// package:flutter/src/widgets/binding.dart,::,runApp +/// file:///.../my_app.dart,::,main +/// ``` +/// +/// 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; - - final File file = new File(filePath); - file.writeAsBytesSync(result); + return result; } dynamic _saveCompilationTrace() native 'SaveCompilationTrace'; @@ -53,17 +72,9 @@ int _getCallbackHandle(Function closure) native 'GetCallbackHandle'; Function _getCallbackFromHandle(int handle) native 'GetCallbackFromHandle'; // Required for gen_snapshot to work correctly. -int _isolateId; +int _isolateId; // ignore: unused_element @pragma('vm:entry-point') -Function _getPrintClosure() => _print; -@pragma('vm:entry-point') -Function _getScheduleMicrotaskClosure() => _scheduleMicrotask; - -// Though the "main" symbol is not included in any of the libraries imported -// above, the builtin library will be included manually during VM setup. This -// symbol is only necessary for precompilation. It is marked as a stanalone -// entry point into the VM. This prevents the precompiler from tree shaking -// away "main". +Function _getPrintClosure() => _print; // ignore: unused_element @pragma('vm:entry-point') -Function _getMainClosure() => main; +Function _getScheduleMicrotaskClosure() => _scheduleMicrotask; // ignore: unused_element diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 1b2dc7a6acaac..e7b0e01c9ea70 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -59,7 +59,7 @@ Color _scaleAlpha(Color a, double factor) { /// /// Consider the light teal of the Flutter logo. It is fully opaque, with a red /// channel value of 0x42 (66), a green channel value of 0xA5 (165), and a blue -/// channel value of 0xF5 (245). In the common "hash syntax" for colour values, +/// channel value of 0xF5 (245). In the common "hash syntax" for color values, /// it would be described as `#42A5F5`. /// /// Here are some ways it could be constructed: @@ -803,7 +803,7 @@ enum FilterQuality { /// Fastest possible filtering, albeit also the lowest quality. /// - /// Typically this implies nearest-neighbour filtering. + /// Typically this implies nearest-neighbor filtering. none, /// Better quality than [none], faster than [medium]. @@ -814,7 +814,7 @@ enum FilterQuality { /// Better quality than [low], faster than [high]. /// /// Typically this implies a combination of bilinear interpolation and - /// pyramidal parametric prefiltering (mipmaps). + /// pyramidal parametric pre-filtering (mipmaps). medium, /// Best possible quality filtering, albeit also the slowest. @@ -879,7 +879,7 @@ enum StrokeCap { enum StrokeJoin { /// Joins between line segments form sharp corners. /// - /// {@animation joinMiterEnum 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the @@ -895,7 +895,7 @@ enum StrokeJoin { /// Joins between line segments are semi-circular. /// - /// {@animation joinRoundEnum 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the @@ -910,7 +910,7 @@ enum StrokeJoin { /// Joins between line segments connect the corners of the butt ends of the /// line segments to give a beveled appearance. /// - /// {@animation joinBevelEnum 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} /// /// The center of the line segment is colored in the diagram above to /// highlight the join, but in normal usage the join is the same color as the @@ -1018,14 +1018,6 @@ enum Clip { antiAliasWithSaveLayer, } -/// The global default value of whether and how to clip a widget. This is only for -/// temporary migration from default-to-clip to default-to-NOT-clip. -/// -// TODO(liyuqian): Set it to Clip.none. (https://github.com/flutter/flutter/issues/18057) -// We currently have Clip.antiAlias to preserve our old behaviors. -@Deprecated("Do not use this as it'll soon be removed after we set the default behavior to Clip.none.") -const Clip defaultClipBehavior = Clip.antiAlias; - // If we actually run on big endian machines, we'll need to do something smarter // here. We don't use [Endian.Host] because it's not a compile-time // constant and can't propagate into the set/get calls. @@ -1090,7 +1082,8 @@ class Paint { // Binary format must match the deserialization code in paint.cc. List _objects; static const int _kShaderIndex = 0; - static const int _kObjectCount = 1; // Must be one larger than the largest index. + static const int _kColorFilterMatrixIndex = 1; + static const int _kObjectCount = 2; // Must be one larger than the largest index. /// Whether to apply anti-aliasing to lines and images drawn on the /// canvas. @@ -1209,11 +1202,11 @@ class Paint { /// /// Some examples of joins: /// - /// {@animation joinMiterStrokeJoin 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// - /// {@animation joinRoundStrokeJoin 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/round_join.mp4} /// - /// {@animation joinBevelStrokeJoin 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/bevel_join.mp4} /// /// The centers of the line segments are colored in the diagrams above to /// highlight the joins, but in normal usage the join is the same color as the @@ -1248,11 +1241,11 @@ class Paint { /// Defaults to 4.0. Using zero as a limit will cause a [StrokeJoin.bevel] /// join to be used all the time. /// - /// {@animation joinMiter0Limit 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_0_join.mp4} /// - /// {@animation joinMiter4Limit 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_4_join.mp4} /// - /// {@animation joinMiter6Limit 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4} + /// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/dart-ui/miter_6_join.mp4} /// /// The centers of the line segments are colored in the diagrams above to /// highlight the joins, but in normal usage the join is the same color as the @@ -1344,25 +1337,49 @@ class Paint { /// /// When a shape is being drawn, [colorFilter] overrides [color] and [shader]. ColorFilter get colorFilter { - final bool isNull = _data.getInt32(_kColorFilterOffset, _kFakeHostEndian) == 0; - if (isNull) - return null; - return new ColorFilter.mode( - new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), - BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)] - ); + switch (_data.getInt32(_kColorFilterOffset, _kFakeHostEndian)) { + case ColorFilter._TypeNone: + return null; + case ColorFilter._TypeMode: + return new ColorFilter.mode( + new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), + BlendMode.values[_data.getInt32(_kColorFilterBlendModeOffset, _kFakeHostEndian)], + ); + case ColorFilter._TypeMatrix: + return new ColorFilter.matrix(_objects[_kColorFilterMatrixIndex]); + case ColorFilter._TypeLinearToSrgbGamma: + return const ColorFilter.linearToSrgbGamma(); + case ColorFilter._TypeSrgbToLinearGamma: + return const ColorFilter.srgbToLinearGamma(); + } + + return null; } + set colorFilter(ColorFilter value) { if (value == null) { - _data.setInt32(_kColorFilterOffset, 0, _kFakeHostEndian); + _data.setInt32(_kColorFilterOffset, ColorFilter._TypeNone, _kFakeHostEndian); _data.setInt32(_kColorFilterColorOffset, 0, _kFakeHostEndian); _data.setInt32(_kColorFilterBlendModeOffset, 0, _kFakeHostEndian); + + if (_objects != null) { + _objects[_kColorFilterMatrixIndex] = null; + } } else { - assert(value._color != null); - assert(value._blendMode != null); - _data.setInt32(_kColorFilterOffset, 1, _kFakeHostEndian); - _data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian); - _data.setInt32(_kColorFilterBlendModeOffset, value._blendMode.index, _kFakeHostEndian); + _data.setInt32(_kColorFilterOffset, value._type, _kFakeHostEndian); + + if (value._type == ColorFilter._TypeMode) { + assert(value._color != null); + assert(value._blendMode != null); + + _data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian); + _data.setInt32(_kColorFilterBlendModeOffset, value._blendMode.index, _kFakeHostEndian); + } else if (value._type == ColorFilter._TypeMatrix) { + assert(value._matrix != null); + + _objects ??= new List(_kObjectCount); + _objects[_kColorFilterMatrixIndex] = Float32List.fromList(value._matrix); + } } } @@ -1503,11 +1520,17 @@ class _ImageInfo { /// /// To draw an [Image], use one of the methods on the [Canvas] class, such as /// [Canvas.drawImage]. +/// +/// See also: +/// +/// * [Image](https://api.flutter.dev/flutter/widgets/Image-class.html), the class in the [widgets] library. +/// +@pragma('vm:entry-point') class Image extends NativeFieldWrapperClass2 { - /// This class is created by the engine, and should not be instantiated - /// or extended directly. - /// - /// To obtain an [Image] object, use [instantiateImageCodec]. + // This class is created by the engine, and should not be instantiated + // or extended directly. + // + // To obtain an [Image] object, use [instantiateImageCodec]. @pragma('vm:entry-point') Image._(); @@ -1550,6 +1573,7 @@ typedef ImageDecoderCallback = void Function(Image result); /// /// To obtain an instance of the [FrameInfo] interface, see /// [Codec.getNextFrame]. +@pragma('vm:entry-point') class FrameInfo extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @@ -1568,12 +1592,20 @@ class FrameInfo extends NativeFieldWrapperClass2 { } /// A handle to an image codec. +/// +/// This class is created by the engine, and should not be instantiated +/// or extended directly. +/// +/// To obtain an instance of the [Codec] interface, see +/// [instantiateImageCodec]. +@pragma('vm:entry-point') class Codec extends NativeFieldWrapperClass2 { - /// This class is created by the engine, and should not be instantiated - /// or extended directly. - /// - /// To obtain an instance of the [Codec] interface, see - /// [instantiateImageCodec]. + // + // This class is created by the engine, and should not be instantiated + // or extended directly. + // + // To obtain an instance of the [Codec] interface, see + // [instantiateImageCodec]. @pragma('vm:entry-point') Codec._(); @@ -1636,8 +1668,8 @@ String _instantiateImageCodec(Uint8List list, _Callback callback, _ImageI /// Loads a single image frame from a byte array into an [Image] object. /// -/// This is a convenience wrapper around [instantiateImageCodec]. -/// Prefer using [instantiateImageCodec] which also supports multi frame images. +/// This is a convenience wrapper around [instantiateImageCodec]. Prefer using +/// [instantiateImageCodec] which also supports multi frame images. void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) { _decodeImageFromListAsync(list, callback); } @@ -1654,7 +1686,7 @@ Future _decodeImageFromListAsync(Uint8List list, /// [pixels] is the pixel data in the encoding described by [format]. /// /// [rowBytes] is the number of bytes consumed by each row of pixels in the -/// data buffer. If unspecified, it defaults to [width] multipled by the +/// data buffer. If unspecified, it defaults to [width] multiplied by the /// number of bytes per pixel in the provided [format]. /// /// The [decodedCacheRatioCap] is the default maximum multiple of the compressed @@ -1762,6 +1794,7 @@ enum PathOperation { } /// A handle for the framework to hold and retain an engine layer across frames. +@pragma('vm:entry-point') class EngineLayer extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @@ -1771,21 +1804,22 @@ class EngineLayer extends NativeFieldWrapperClass2 { /// A complex, one-dimensional subset of a plane. /// -/// A path consists of a number of subpaths, and a _current point_. +/// A path consists of a number of sub-paths, and a _current point_. /// -/// Subpaths consist of segments of various types, such as lines, -/// arcs, or beziers. Subpaths can be open or closed, and can +/// Sub-paths consist of segments of various types, such as lines, +/// arcs, or beziers. Sub-paths can be open or closed, and can /// self-intersect. /// -/// Closed subpaths enclose a (possibly discontiguous) region of the +/// Closed sub-paths enclose a (possibly discontiguous) region of the /// plane based on the current [fillType]. /// /// The _current point_ is initially at the origin. After each -/// operation adding a segment to a subpath, the current point is +/// operation adding a segment to a sub-path, the current point is /// updated to the end of that segment. /// /// 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 { /// Create a new empty [Path] object. @pragma('vm:entry-point') @@ -1810,10 +1844,10 @@ class Path extends NativeFieldWrapperClass2 { int _getFillType() native 'Path_getFillType'; void _setFillType(int fillType) native 'Path_setFillType'; - /// Starts a new subpath at the given coordinate. + /// Starts a new sub-path at the given coordinate. void moveTo(double x, double y) native 'Path_moveTo'; - /// Starts a new subpath at the given offset from the current point. + /// Starts a new sub-path at the given offset from the current point. void relativeMoveTo(double dx, double dy) native 'Path_relativeMoveTo'; /// Adds a straight line segment from the current point to the given @@ -1864,7 +1898,7 @@ class Path extends NativeFieldWrapperClass2 { /// If the `forceMoveTo` argument is false, adds a straight line /// segment and an arc segment. /// - /// If the `forceMoveTo` argument is true, starts a new subpath + /// If the `forceMoveTo` argument is true, starts a new sub-path /// consisting of an arc segment. /// /// In either case, the arc segment consists of the arc that follows @@ -1942,7 +1976,7 @@ class Path extends NativeFieldWrapperClass2 { bool largeArc, bool clockwise) native 'Path_relativeArcToPoint'; - /// Adds a new subpath that consists of four lines that outline the + /// Adds a new sub-path that consists of four lines that outline the /// given rectangle. void addRect(Rect rect) { assert(_rectIsValid(rect)); @@ -1950,7 +1984,7 @@ class Path extends NativeFieldWrapperClass2 { } void _addRect(double left, double top, double right, double bottom) native 'Path_addRect'; - /// Adds a new subpath that consists of a curve that forms the + /// Adds a new sub-path that consists of a curve that forms the /// ellipse that fills the given rectangle. /// /// To add a circle, pass an appropriate rectangle as `oval`. [Rect.fromCircle] @@ -1961,7 +1995,7 @@ class Path extends NativeFieldWrapperClass2 { } void _addOval(double left, double top, double right, double bottom) native 'Path_addOval'; - /// Adds a new subpath with one arc segment that consists of the arc + /// Adds a new sub-path with one arc segment that consists of the arc /// that follows the edge of the oval bounded by the given /// rectangle, from startAngle radians around the oval up to /// startAngle + sweepAngle radians around the oval, with zero @@ -1976,7 +2010,7 @@ class Path extends NativeFieldWrapperClass2 { void _addArc(double left, double top, double right, double bottom, double startAngle, double sweepAngle) native 'Path_addArc'; - /// Adds a new subpath with a sequence of line segments that connect the given + /// Adds a new sub-path with a sequence of line segments that connect the given /// points. /// /// If `close` is true, a final line segment will be added that connects the @@ -1989,7 +2023,7 @@ class Path extends NativeFieldWrapperClass2 { } void _addPolygon(Float32List points, bool close) native 'Path_addPolygon'; - /// Adds a new subpath that consists of the straight lines and + /// Adds a new sub-path that consists of the straight lines and /// curves needed to form the rounded rectangle described by the /// argument. void addRRect(RRect rrect) { @@ -1998,7 +2032,7 @@ class Path extends NativeFieldWrapperClass2 { } void _addRRect(Float32List rrect) native 'Path_addRRect'; - /// Adds a new subpath that consists of the given `path` offset by the given + /// Adds a new sub-path that consists of the given `path` offset by the given /// `offset`. /// /// If `matrix4` is specified, the path will be transformed by this matrix @@ -2036,11 +2070,11 @@ class Path extends NativeFieldWrapperClass2 { void _extendWithPath(Path path, double dx, double dy) native 'Path_extendWithPath'; void _extendWithPathAndMatrix(Path path, double dx, double dy, Float64List matrix) native 'Path_extendWithPathAndMatrix'; - /// Closes the last subpath, as if a straight line had been drawn - /// from the current point to the first point of the subpath. + /// Closes the last sub-path, as if a straight line had been drawn + /// from the current point to the first point of the sub-path. void close() native 'Path_close'; - /// Clears the [Path] object of all subpaths, returning it to the + /// Clears the [Path] object of all sub-paths, returning it to the /// same state it had when it was created. The _current point_ is /// reset to the origin. void reset() native 'Path_reset'; @@ -2059,7 +2093,7 @@ class Path extends NativeFieldWrapperClass2 { bool _contains(double x, double y) native 'Path_contains'; /// Returns a copy of the path with all the segments of every - /// subpath translated by the given offset. + /// sub-path translated by the given offset. Path shift(Offset offset) { assert(_offsetIsValid(offset)); return _shift(offset.dx, offset.dy); @@ -2067,7 +2101,7 @@ class Path extends NativeFieldWrapperClass2 { Path _shift(double dx, double dy) native 'Path_shift'; /// Returns a copy of the path with all the segments of every - /// subpath transformed by the given matrix. + /// sub-path transformed by the given matrix. Path transform(Float64List matrix4) { assert(_matrix4IsValid(matrix4)); return _transform(matrix4); @@ -2185,7 +2219,7 @@ class Tangent { /// valid until the next one is obtained. class PathMetrics extends collection.IterableBase { PathMetrics._(Path path, bool forceClosed) : - _iterator = new PathMetricIterator._(new PathMetric._(path, forceClosed)); + _iterator = new PathMetricIterator._(new _PathMeasure(path, forceClosed)); final Iterator _iterator; @@ -2195,22 +2229,18 @@ class PathMetrics extends collection.IterableBase { /// Tracks iteration from one segment of a path to the next for measurement. class PathMetricIterator implements Iterator { - PathMetricIterator._(this._pathMetric); + PathMetricIterator._(this._pathMeasure) : assert(_pathMeasure != null); PathMetric _pathMetric; - bool _firstTime = true; + _PathMeasure _pathMeasure; @override - PathMetric get current => _firstTime ? null : _pathMetric; + PathMetric get current => _pathMetric; @override bool moveNext() { - // PathMetric isn't a normal iterable - it's already initialized to its - // first Path. Should only call _moveNext when done with the first one. - if (_firstTime == true) { - _firstTime = false; - return true; - } else if (_pathMetric?._moveNext() == true) { + if (_pathMeasure._nextContour()) { + _pathMetric = PathMetric._(_pathMeasure); return true; } _pathMetric = null; @@ -2218,23 +2248,52 @@ class PathMetricIterator implements Iterator { } } -/// Utilities for measuring a [Path] and extracting subpaths. +/// Utilities for measuring a [Path] and extracting sub-paths. /// /// Iterate over the object returned by [Path.computeMetrics] to obtain /// [PathMetric] objects. /// -/// Once created, metrics will only be valid while the iterator is at the given -/// contour. When the next contour's [PathMetric] is obtained, this object -/// becomes invalid. -class PathMetric extends NativeFieldWrapperClass2 { - /// Create a new empty [Path] object. - PathMetric._(Path path, bool forceClosed) { _constructor(path, forceClosed); } - void _constructor(Path path, bool forceClosed) native 'PathMeasure_constructor'; +/// Once created, the methods on this class will only be valid while the +/// iterator is at the contour for which they were created. It will also only be +/// valid for the path as it was specified when [Path.computeMetrics] was called. +/// If additional contours are added or any contours are updated, the metrics +/// need to be recomputed. +class PathMetric { + PathMetric._(this._measure) + : assert(_measure != null), + length = _measure.length(_measure.currentContourIndex), + isClosed = _measure.isClosed(_measure.currentContourIndex), + contourIndex = _measure.currentContourIndex; /// Return the total length of the current contour. - double get length native 'PathMeasure_getLength'; + final double length; + + /// Whether the contour is closed. + /// + /// Returns true if the contour ends with a call to [Path.close] (which may + /// have been implied when using [Path.addRect]) or if `forceClosed` was + /// specified as true in the call to [Path.computeMetrics]. Returns false + /// otherwise. + final bool isClosed; + + /// The zero-based index of the contour. + /// + /// [Path] objects are made up of zero or more contours. The first contour is + /// created once a drawing command (e.g. [Path.lineTo]) is issued. A + /// [Path.moveTo] command after a drawing command may create a new contour, + /// although it may not if optimizations are applied that determine the move + /// command did not actually result in moving the pen. + /// + /// This property is only valid with reference to its original iterator and + /// 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. + final int contourIndex; + + final _PathMeasure _measure; + - /// Computes the position of hte current contour at the given offset, and the + /// Computes the position of the current contour at the given offset, and the /// angle of the path at that point. /// /// For example, calling this method with a distance of 1.41 for a line from @@ -2245,7 +2304,38 @@ class PathMetric extends NativeFieldWrapperClass2 { /// /// The distance is clamped to the [length] of the current contour. Tangent getTangentForOffset(double distance) { - final Float32List posTan = _getPosTan(distance); + return _measure.getTangentForOffset(contourIndex, distance); + } + + /// Given a start and stop 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. + Path extractPath(double start, double end, {bool startWithMoveTo: true}) { + return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); + } + + @override + String toString() => '$runtimeType{length: $length, isClosed: $isClosed, contourIndex:$contourIndex}'; +} + +class _PathMeasure extends NativeFieldWrapperClass2 { + _PathMeasure(Path path, bool forceClosed) { + currentContourIndex = -1; // nextContour will increment this to the zero based index. + _constructor(path, forceClosed); + } + void _constructor(Path path, bool forceClosed) native 'PathMeasure_constructor'; + + double length(int contourIndex) { + assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + return _length(contourIndex); + } + double _length(int contourIndex) native 'PathMeasure_getLength'; + + Tangent getTangentForOffset(int contourIndex, double distance) { + assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + final Float32List posTan = _getPosTan(contourIndex, distance); // first entry == 0 indicates that Skia returned false if (posTan[0] == 0.0) { return null; @@ -2256,34 +2346,37 @@ class PathMetric extends NativeFieldWrapperClass2 { ); } } - Float32List _getPosTan(double distance) native 'PathMeasure_getPosTan'; + Float32List _getPosTan(int contourIndex, double distance) native 'PathMeasure_getPosTan'; - /// Given a start and stop 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. - Path extractPath(double start, double end, {bool startWithMoveTo: true}) native 'PathMeasure_getSegment'; + Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo: true}) { + assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + return _extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); + } + Path _extractPath(int contourIndex, double start, double end, {bool startWithMoveTo: true}) native 'PathMeasure_getSegment'; - /// Whether the contour is closed. - /// - /// Returns true if the contour ends with a call to [Path.close] (which may - /// have been implied when using [Path.addRect]) or if `forceClosed` was - /// specified as true in the call to [Path.computeMetrics]. Returns false - /// otherwise. - bool get isClosed native 'PathMeasure_isClosed'; + bool isClosed(int contourIndex) { + assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + return _isClosed(contourIndex); + } + bool _isClosed(int contourIndex) native 'PathMeasure_isClosed'; // Move to the next contour in the path. // // A path can have a next contour if [Path.moveTo] was called after drawing began. // Return true if one exists, or false. // - // This is not exactly congruent with a regular [Iterator.moveNext]. - // Typically, [Iterator.moveNext] should be called before accessing the - // [Iterator.current]. In this case, the [PathMetric] is valid before - // calling `_moveNext` - `_moveNext` should be called after the first - // iteration is done instead of before. - bool _moveNext() native 'PathMeasure_nextContour'; + // Before Skia introduced an SkPathContourMeasureIter, this didn't work like + // a normal iterator. Now it does. + bool _nextContour() { + final bool next = _nativeNextContour(); + if (next) { + currentContourIndex++; + } + return next; + } + bool _nativeNextContour() native 'PathMeasure_nextContour'; + + int currentContourIndex; } /// Styles to use for blurs in [MaskFilter] objects. @@ -2381,25 +2474,84 @@ class ColorFilter { /// to the [Paint.blendMode], using the output of this filter as the source /// and the background as the destination. const ColorFilter.mode(Color color, BlendMode blendMode) - : _color = color, _blendMode = blendMode; + : _color = color, + _blendMode = blendMode, + _matrix = null, + _type = _TypeMode; + + /// Construct a color filter that transforms a color by a 4x5 matrix. The + /// matrix is in row-major order and the translation column is specified in + /// unnormalized, 0...255, space. + const ColorFilter.matrix(List matrix) + : _color = null, + _blendMode = null, + _matrix = matrix, + _type = _TypeMatrix; + + /// Construct a color filter that applies the sRGB gamma curve to the RGB + /// channels. + const ColorFilter.linearToSrgbGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeLinearToSrgbGamma; + + /// Creates a color filter that applies the inverse of the sRGB gamma curve + /// to the RGB channels. + const ColorFilter.srgbToLinearGamma() + : _color = null, + _blendMode = null, + _matrix = null, + _type = _TypeSrgbToLinearGamma; final Color _color; final BlendMode _blendMode; + final List _matrix; + final int _type; + + // The type of SkColorFilter class to create for Skia. + // These constants must be kept in sync with ColorFilterType in paint.cc. + static const int _TypeNone = 0; // null + 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 ==(dynamic other) { - if (other is! ColorFilter) + if (other is! ColorFilter) { return false; + } final ColorFilter typedOther = other; - return _color == typedOther._color && - _blendMode == typedOther._blendMode; + + if (_type != typedOther._type) { + return false; + } + if (!_listEquals(_matrix, typedOther._matrix)) { + return false; + } + + return _color == typedOther._color && _blendMode == typedOther._blendMode; } @override - int get hashCode => hashValues(_color, _blendMode); + int get hashCode => hashValues(_color, _blendMode, hashList(_matrix), _type); @override - String toString() => 'ColorFilter($_color, $_blendMode)'; + 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.'; + } + } } /// A filter operation to apply to a raster image. @@ -2531,6 +2683,11 @@ Float32List _encodeTwoPoints(Offset pointA, Offset pointB) { /// /// There are several types of gradients, represented by the various constructors /// on this class. +/// +/// See also: +/// +/// * [Gradient](https://api.flutter.dev/flutter/painting/Gradient-class.html), the class in the [painting] library. +/// class Gradient extends Shader { void _constructor() native 'Gradient_constructor'; @@ -2908,8 +3065,8 @@ class Canvas extends NativeFieldWrapperClass2 { /// ## Using saveLayer with clips /// /// When a rectangular clip operation (from [clipRect]) is not axis-aligned - /// with the raster buffer, or when the clip operation is not rectalinear (e.g. - /// because it is a rounded rectangle clip created by [clipRRect] or an + /// with the raster buffer, or when the clip operation is not rectilinear + /// (e.g. because it is a rounded rectangle clip created by [clipRRect] or an /// arbitrarily complicated path clip created by [clipPath]), the edge of the /// clip needs to be anti-aliased. /// @@ -3261,7 +3418,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// Draws the given [Path] with the given [Paint]. Whether this shape is /// filled or stroked (or both) is controlled by [Paint.style]. If the path is - /// filled, then subpaths within it are implicitly closed (see [Path.close]). + /// filled, then sub-paths within it are implicitly closed (see [Path.close]). void drawPath(Path path, Paint paint) { assert(path != null); // path is checked on the engine side assert(paint != null); @@ -3580,6 +3737,7 @@ class Canvas extends NativeFieldWrapperClass2 { /// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via /// 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 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @@ -3595,7 +3753,15 @@ class Picture extends NativeFieldWrapperClass2 { /// /// Although the image is returned synchronously, the picture is actually /// rasterized the first time the image is drawn and then cached. - Image toImage(int width, int height) native 'Picture_toImage'; + Future toImage(int width, int height) { + if (width <= 0 || height <= 0) + throw new Exception('Invalid image dimensions.'); + return _futurize( + (_Callback callback) => _toImage(width, height, callback) + ); + } + + String _toImage(int width, int height, _Callback callback) native 'Picture_toImage'; /// Release the resources used by this object. The object is no longer usable /// after this method is called. @@ -3647,7 +3813,7 @@ class Shadow { /// /// The default shadow is a black shadow with zero offset and zero blur. /// Default shadows should be completely covered by the casting element, - /// and not be visble. + /// and not be visible. /// /// Transparency should be adjusted through the [color] alpha. /// @@ -3706,9 +3872,10 @@ class Shadow { /// To honor this as well, the shape should be translated by [offset] before /// being filled using this [Paint]. /// - /// This class does not provide a way to disable shadows to avoid inconsistencies - /// in shadow blur rendering, primarily as a method of reducing test flakiness. - /// [toPaint] should be overriden in subclasses to provide this functionality. + /// This class does not provide a way to disable shadows to avoid + /// inconsistencies in shadow blur rendering, primarily as a method of + /// reducing test flakiness. [toPaint] should be overridden in subclasses to + /// provide this functionality. Paint toPaint() { return Paint() ..color = color @@ -3796,23 +3963,6 @@ class Shadow { @override int get hashCode => hashValues(color, offset, blurRadius); - /// 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 Shadows in the same order. Returns - /// false otherwise. - static bool _shadowsListEquals(List a, List b) { - // Compare _shadows - if (a == null) - return b == null; - if (b == null || a.length != b.length) - return false; - for (int index = 0; index < a.length; ++index) - if (a[index] != b[index]) - return false; - return true; - } - // Serialize [shadows] into ByteData. The format is a single uint_32_t at // the beginning indicating the number of shadows, followed by _kBytesPerShadow // bytes for each shadow. diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index a691b752d1652..691a41949ecd7 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index c4c870d317170..98632b9235edc 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/codec.cc b/lib/ui/painting/codec.cc index 7287966c6b122..0b1de9c2c7889 100644 --- a/lib/ui/painting/codec.cc +++ b/lib/ui/painting/codec.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,10 +17,6 @@ #include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/typed_data/uint8_list.h" -#ifdef ERROR -#undef ERROR -#endif - using tonic::DartInvoke; using tonic::DartPersistentValue; using tonic::ToDart; @@ -74,7 +70,7 @@ static sk_sp DecodeImage(fml::WeakPtr context, // This indicates that we do not want a "linear blending" decode. sk_sp dstColorSpace = nullptr; return SkImage::MakeCrossContextFromEncoded( - context.get(), std::move(buffer), false, dstColorSpace.get(), true); + context.get(), std::move(buffer), true, dstColorSpace.get(), true); } else { // Defer decoding until time of draw later on the GPU thread. Can happen // when GL operations are currently forbidden such as in the background @@ -135,7 +131,7 @@ fml::RefPtr InitCodecUncompressed( sk_sp skImage; if (context) { SkPixmap pixmap(image_info.sk_info, buffer->data(), image_info.row_bytes); - skImage = SkImage::MakeCrossContextFromPixmap(context.get(), pixmap, false, + skImage = SkImage::MakeCrossContextFromPixmap(context.get(), pixmap, true, nullptr, true); } else { skImage = SkImage::MakeRasterData(image_info.sk_info, std::move(buffer), @@ -287,7 +283,7 @@ void InstantiateImageCodec(Dart_NativeArguments args) { auto buffer = SkData::MakeWithCopy(list.data(), list.num_elements()); - auto dart_state = UIDartState::Current(); + auto* dart_state = UIDartState::Current(); const auto& task_runners = dart_state->GetTaskRunners(); task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable( @@ -375,47 +371,58 @@ MultiFrameCodec::MultiFrameCodec(std::unique_ptr codec, compressedSizeBytes_ = codec_->getInfo().computeMinByteSize(); frameBitmaps_.clear(); decodedCacheSize_ = 0; - // Initialize the frame cache, marking frames that are required for other - // dependent frames to render. + nextFrameIndex_ = 0; + // Go through our frame information and mark which frames are required in + // order to decode subsequent ones. + requiredFrames_.clear(); for (size_t frameIndex = 0; frameIndex < frameInfos_.size(); frameIndex++) { - const auto& frameInfo = frameInfos_[frameIndex]; - if (frameInfo.fRequiredFrame != SkCodec::kNoFrame) { - frameBitmaps_[frameInfo.fRequiredFrame] = - std::make_unique(/*required=*/true); - } - if (frameBitmaps_.count(frameIndex) < 1) { - frameBitmaps_[frameIndex] = - std::make_unique(/*required=*/false); + const int requiredFrame = frameInfos_[frameIndex].fRequiredFrame; + if (requiredFrame != SkCodec::kNoFrame) { + requiredFrames_[requiredFrame] = true; } } - nextFrameIndex_ = 0; +} + +MultiFrameCodec::~MultiFrameCodec() {} + +int MultiFrameCodec::frameCount() { + return frameInfos_.size(); +} + +int MultiFrameCodec::repetitionCount() { + return repetitionCount_; } sk_sp MultiFrameCodec::GetNextFrameImage( fml::WeakPtr resourceContext) { // Populate this bitmap from the cache if it exists - DecodedFrame& cacheEntry = *frameBitmaps_[nextFrameIndex_]; - SkBitmap bitmap = - cacheEntry.bitmap_ != nullptr ? *cacheEntry.bitmap_ : SkBitmap(); - if (!bitmap.getPixels()) { // We haven't decoded this frame yet + SkBitmap bitmap = frameBitmaps_[nextFrameIndex_] != nullptr + ? *frameBitmaps_[nextFrameIndex_] + : SkBitmap(); + const bool frameAlreadyCached = bitmap.getPixels(); + if (!frameAlreadyCached) { const SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType); bitmap.allocPixels(info); SkCodec::Options options; options.fFrameIndex = nextFrameIndex_; - const int requiredFrame = frameInfos_[nextFrameIndex_].fRequiredFrame; - if (requiredFrame != SkCodec::kNone) { - const SkBitmap* requiredBitmap = - frameBitmaps_[requiredFrame]->bitmap_.get(); - if (requiredBitmap == nullptr) { + const int requiredFrameIndex = frameInfos_[nextFrameIndex_].fRequiredFrame; + if (requiredFrameIndex != SkCodec::kNoFrame) { + if (lastRequiredFrame_ == nullptr) { FML_LOG(ERROR) << "Frame " << nextFrameIndex_ << " depends on frame " - << requiredFrame << " which has not been cached."; + << requiredFrameIndex + << " and no required frames are cached."; return NULL; + } else if (lastRequiredFrameIndex_ != requiredFrameIndex) { + FML_DLOG(INFO) << "Required frame " << requiredFrameIndex + << " is not cached. Using " << lastRequiredFrameIndex_ + << " instead"; } - if (requiredBitmap->getPixels() && - copy_to(&bitmap, requiredBitmap->colorType(), *requiredBitmap)) { - options.fPriorFrame = requiredFrame; + if (lastRequiredFrame_->getPixels() && + copy_to(&bitmap, lastRequiredFrame_->colorType(), + *lastRequiredFrame_)) { + options.fPriorFrame = requiredFrameIndex; } } @@ -425,24 +432,30 @@ sk_sp MultiFrameCodec::GetNextFrameImage( return NULL; } - // Cache the bitmap if this is a required frame or if we're still under our - // ratio cap. const size_t cachedFrameSize = bitmap.computeByteSize(); - if (cacheEntry.required_ || - ((decodedCacheSize_ + cachedFrameSize) / compressedSizeBytes_) <= - decodedCacheRatioCap_) { - cacheEntry.bitmap_ = std::make_unique(bitmap); + const bool shouldCache = ((decodedCacheSize_ + cachedFrameSize) / + compressedSizeBytes_) <= decodedCacheRatioCap_; + if (shouldCache) { + frameBitmaps_[nextFrameIndex_] = std::make_shared(bitmap); decodedCacheSize_ += cachedFrameSize; } } + // Hold onto this if we need it to decode future frames. + if (requiredFrames_[nextFrameIndex_]) { + lastRequiredFrame_ = frameAlreadyCached + ? frameBitmaps_[nextFrameIndex_] + : std::make_shared(bitmap); + lastRequiredFrameIndex_ = nextFrameIndex_; + } + if (resourceContext) { SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(), bitmap.pixelRef()->rowBytes()); // This indicates that we do not want a "linear blending" decode. sk_sp dstColorSpace = nullptr; return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap, - false, dstColorSpace.get()); + true, dstColorSpace.get()); } else { // Defer decoding until time of draw later on the GPU thread. Can happen // when GL operations are currently forbidden such as in the background @@ -485,7 +498,7 @@ Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) { return ToDart("Callback must be a function"); } - auto dart_state = UIDartState::Current(); + auto* dart_state = UIDartState::Current(); const auto& task_runners = dart_state->GetTaskRunners(); @@ -503,6 +516,19 @@ Dart_Handle MultiFrameCodec::getNextFrame(Dart_Handle callback_handle) { return Dart_Null(); } +SingleFrameCodec::SingleFrameCodec(fml::RefPtr frame) + : frame_(std::move(frame)) {} + +SingleFrameCodec::~SingleFrameCodec() {} + +int SingleFrameCodec::frameCount() { + return 1; +} + +int SingleFrameCodec::repetitionCount() { + return 0; +} + Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) { if (!Dart_IsClosure(callback_handle)) { return ToDart("Callback must be a function"); diff --git a/lib/ui/painting/codec.h b/lib/ui/painting/codec.h index e171c4ca08e54..0c104bf788e4b 100644 --- a/lib/ui/painting/codec.h +++ b/lib/ui/painting/codec.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,15 +36,15 @@ class Codec : public RefCountedDartWrappable { class MultiFrameCodec : public Codec { public: - int frameCount() { return frameInfos_.size(); } - int repetitionCount() { return repetitionCount_; } - Dart_Handle getNextFrame(Dart_Handle args); + int frameCount() override; + int repetitionCount() override; + Dart_Handle getNextFrame(Dart_Handle args) override; private: MultiFrameCodec(std::unique_ptr codec, const float decodedCacheRatioCap); - ~MultiFrameCodec() {} + ~MultiFrameCodec() override; sk_sp GetNextFrameImage(fml::WeakPtr resourceContext); @@ -65,20 +65,16 @@ class MultiFrameCodec : public Codec { size_t decodedCacheSize_; std::vector frameInfos_; - // A struct linking the bitmap of a frame to whether it's required to render - // other dependent frames. - struct DecodedFrame { - std::unique_ptr bitmap_ = nullptr; - const bool required_; - - DecodedFrame(bool required) : required_(required) {} - }; + std::map requiredFrames_; // A cache of previously loaded bitmaps, indexed by the frame they belong to. - // Always holds at least the frames marked as required for reuse by - // [SkCodec::getFrameInfo()]. Will cache other non-essential frames until - // [decodedCacheSize_] : [compressedSize_] exceeds [decodedCacheRatioCap_]. - std::map> frameBitmaps_; + // Caches all frames until [decodedCacheSize_] : [compressedSize_] exceeds + // [decodedCacheRatioCap_]. + std::map> frameBitmaps_; + // The last decoded frame that's required to decode any subsequent frames. + std::shared_ptr lastRequiredFrame_; + // The index of the last decoded required frame. + int lastRequiredFrameIndex_ = -1; FML_FRIEND_MAKE_REF_COUNTED(MultiFrameCodec); FML_FRIEND_REF_COUNTED_THREAD_SAFE(MultiFrameCodec); @@ -86,13 +82,13 @@ class MultiFrameCodec : public Codec { class SingleFrameCodec : public Codec { public: - int frameCount() { return 1; } - int repetitionCount() { return 0; } - Dart_Handle getNextFrame(Dart_Handle args); + int frameCount() override; + int repetitionCount() override; + Dart_Handle getNextFrame(Dart_Handle args) override; private: - SingleFrameCodec(fml::RefPtr frame) : frame_(std::move(frame)) {} - ~SingleFrameCodec() {} + SingleFrameCodec(fml::RefPtr frame); + ~SingleFrameCodec() override; fml::RefPtr frame_; diff --git a/lib/ui/painting/engine_layer.cc b/lib/ui/painting/engine_layer.cc index 5ff6e96f61574..f365e37cb8fbc 100644 --- a/lib/ui/painting/engine_layer.cc +++ b/lib/ui/painting/engine_layer.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,8 +15,18 @@ using tonic::ToDart; namespace blink { +EngineLayer::EngineLayer(std::shared_ptr layer) + : layer_(layer) {} + EngineLayer::~EngineLayer() = default; +size_t EngineLayer::GetAllocationSize() { + // 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; +}; + IMPLEMENT_WRAPPERTYPEINFO(ui, EngineLayer); #define FOR_EACH_BINDING(V) // nothing to bind diff --git a/lib/ui/painting/engine_layer.h b/lib/ui/painting/engine_layer.h index e79b5f8e63967..a95e58a9e2e6a 100644 --- a/lib/ui/painting/engine_layer.h +++ b/lib/ui/painting/engine_layer.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,6 +22,9 @@ class EngineLayer : public RefCountedDartWrappable { public: ~EngineLayer() override; + + size_t GetAllocationSize() override; + static fml::RefPtr MakeRetained( std::shared_ptr layer) { return fml::MakeRefCounted(layer); @@ -32,8 +35,7 @@ class EngineLayer : public RefCountedDartWrappable { std::shared_ptr Layer() const { return layer_; } private: - explicit EngineLayer(std::shared_ptr layer) - : layer_(layer) {} + explicit EngineLayer(std::shared_ptr layer); std::shared_ptr layer_; FML_FRIEND_MAKE_REF_COUNTED(EngineLayer); diff --git a/lib/ui/painting/frame_info.cc b/lib/ui/painting/frame_info.cc index 03af5b4feb070..40359a02dce77 100644 --- a/lib/ui/painting/frame_info.cc +++ b/lib/ui/painting/frame_info.cc @@ -1,5 +1,5 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,6 +18,11 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, FrameInfo); FOR_EACH_BINDING(DART_NATIVE_CALLBACK) +FrameInfo::FrameInfo(fml::RefPtr image, int durationMillis) + : image_(std::move(image)), durationMillis_(durationMillis) {} + +FrameInfo::~FrameInfo(){}; + void FrameInfo::RegisterNatives(tonic::DartLibraryNatives* natives) { natives->Register({FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } diff --git a/lib/ui/painting/frame_info.h b/lib/ui/painting/frame_info.h index 462ab513e0683..57ff23d91c43a 100644 --- a/lib/ui/painting/frame_info.h +++ b/lib/ui/painting/frame_info.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -25,9 +25,9 @@ class FrameInfo final : public RefCountedDartWrappable { static void RegisterNatives(tonic::DartLibraryNatives* natives); private: - FrameInfo(fml::RefPtr image, int durationMillis) - : image_(std::move(image)), durationMillis_(durationMillis) {} - ~FrameInfo(){}; + FrameInfo(fml::RefPtr image, int durationMillis); + + ~FrameInfo() override; const fml::RefPtr image_; const int durationMillis_; diff --git a/lib/ui/painting/gradient.cc b/lib/ui/painting/gradient.cc index 25b6595f6bf54..287e8e78621cc 100644 --- a/lib/ui/painting/gradient.cc +++ b/lib/ui/painting/gradient.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/gradient.h b/lib/ui/painting/gradient.h index 33450c2583812..83e2a45e0d22a 100644 --- a/lib/ui/painting/gradient.h +++ b/lib/ui/painting/gradient.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image.cc b/lib/ui/painting/image.cc index d6ed502c033cd..bbb3b69736ed6 100644 --- a/lib/ui/painting/image.cc +++ b/lib/ui/painting/image.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image.h b/lib/ui/painting/image.h index 44e5cf4edc7c9..a2db734b57516 100644 --- a/lib/ui/painting/image.h +++ b/lib/ui/painting/image.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -39,7 +39,7 @@ class CanvasImage final : public RefCountedDartWrappable { image_ = std::move(image); } - virtual size_t GetAllocationSize() override; + size_t GetAllocationSize() override; static void RegisterNatives(tonic::DartLibraryNatives* natives); diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index b689269cf492b..cee3896dae7b3 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -25,10 +25,6 @@ using tonic::DartInvoke; using tonic::DartPersistentValue; using tonic::ToDart; -#ifdef ERROR -#undef ERROR -#endif - namespace blink { namespace { diff --git a/lib/ui/painting/image_encoding.h b/lib/ui/painting/image_encoding.h index 39e3144f12e07..e4d66f0c8a0c9 100644 --- a/lib/ui/painting/image_encoding.h +++ b/lib/ui/painting/image_encoding.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image_filter.cc b/lib/ui/painting/image_filter.cc index 85735060b6bda..94017beeb3ab3 100644 --- a/lib/ui/painting/image_filter.cc +++ b/lib/ui/painting/image_filter.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image_filter.h b/lib/ui/painting/image_filter.h index bd5d2bfb99ae7..0cc0259113996 100644 --- a/lib/ui/painting/image_filter.h +++ b/lib/ui/painting/image_filter.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image_shader.cc b/lib/ui/painting/image_shader.cc index e233c633c16bd..389a214270618 100644 --- a/lib/ui/painting/image_shader.cc +++ b/lib/ui/painting/image_shader.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/image_shader.h b/lib/ui/painting/image_shader.h index 17a5795e02558..1365577fa1de7 100644 --- a/lib/ui/painting/image_shader.h +++ b/lib/ui/painting/image_shader.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/matrix.cc b/lib/ui/painting/matrix.cc index 81c2f293b2f3d..3fe56783b7bcd 100644 --- a/lib/ui/painting/matrix.cc +++ b/lib/ui/painting/matrix.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/matrix.h b/lib/ui/painting/matrix.h index 25307b1b387c0..0a1d35064bde1 100644 --- a/lib/ui/painting/matrix.h +++ b/lib/ui/painting/matrix.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/paint.cc b/lib/ui/painting/paint.cc index 9b876542a61a7..4e67a48916eb1 100644 --- a/lib/ui/painting/paint.cc +++ b/lib/ui/painting/paint.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,6 +11,7 @@ #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkString.h" #include "third_party/tonic/typed_data/dart_byte_data.h" +#include "third_party/tonic/typed_data/float32_list.h" namespace blink { @@ -35,7 +36,8 @@ constexpr size_t kDataByteCount = 75; // 4 * (last index + 1) // Indices for objects. constexpr int kShaderIndex = 0; -constexpr int kObjectCount = 1; // One larger than largest object index. +constexpr int kColorFilterMatrixIndex = 1; +constexpr int kObjectCount = 2; // One larger than largest object index. // Must be kept in sync with the default in painting.dart. constexpr uint32_t kColorDefault = 0xFF000000; @@ -61,18 +63,63 @@ constexpr SkScalar invert_colors[20] = { // Must be kept in sync with the MaskFilter private constants in painting.dart. enum MaskFilterType { Null, Blur }; +// Must be kept in sync with the ColorFilter private constants in painting.dart. +enum ColorFilterType { + None, + Mode, + Matrix, + LinearToSRGBGamma, + SRGBToLinearGamma +}; + +sk_sp ExtractColorFilter(const uint32_t* uint_data, + Dart_Handle* values) { + switch (uint_data[kColorFilterIndex]) { + case Mode: { + SkColor color = uint_data[kColorFilterColorIndex]; + SkBlendMode blend_mode = + static_cast(uint_data[kColorFilterBlendModeIndex]); + + return SkColorFilter::MakeModeFilter(color, blend_mode); + } + case Matrix: { + Dart_Handle matrixHandle = values[kColorFilterMatrixIndex]; + if (!Dart_IsNull(matrixHandle)) { + FML_DCHECK(Dart_IsList(matrixHandle)); + intptr_t length = 0; + Dart_ListLength(matrixHandle, &length); + + FML_CHECK(length == 20); + + tonic::Float32List decoded(matrixHandle); + return SkColorFilter::MakeMatrixFilterRowMajor255(decoded.data()); + } + return nullptr; + } + case LinearToSRGBGamma: { + return SkColorFilter::MakeLinearToSRGBGamma(); + } + case SRGBToLinearGamma: { + return SkColorFilter::MakeSRGBToLinearGamma(); + } + default: + FML_DLOG(ERROR) << "Out of range value received for kColorFilterIndex."; + return nullptr; + } +} + Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { is_null_ = Dart_IsNull(paint_data); if (is_null_) return; + Dart_Handle values[kObjectCount]; if (!Dart_IsNull(paint_objects)) { FML_DCHECK(Dart_IsList(paint_objects)); intptr_t length = 0; Dart_ListLength(paint_objects, &length); FML_CHECK(length == kObjectCount); - Dart_Handle values[kObjectCount]; if (Dart_IsError(Dart_ListGetRange(paint_objects, 0, kObjectCount, values))) return; @@ -128,22 +175,20 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) { paint_.setFilterQuality(static_cast(filter_quality)); if (uint_data[kColorFilterIndex] && uint_data[kInvertColorIndex]) { - SkColor color = uint_data[kColorFilterColorIndex]; - SkBlendMode blend_mode = - static_cast(uint_data[kColorFilterBlendModeIndex]); - sk_sp color_filter = - SkColorFilter::MakeModeFilter(color, blend_mode); - sk_sp invert_filter = - SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors); - paint_.setColorFilter(invert_filter->makeComposed(color_filter)); + sk_sp color_filter = ExtractColorFilter(uint_data, values); + if (color_filter) { + sk_sp invert_filter = + SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors); + paint_.setColorFilter(invert_filter->makeComposed(color_filter)); + } } else if (uint_data[kInvertColorIndex]) { paint_.setColorFilter( SkColorFilter::MakeMatrixFilterRowMajor255(invert_colors)); } else if (uint_data[kColorFilterIndex]) { - SkColor color = uint_data[kColorFilterColorIndex]; - SkBlendMode blend_mode = - static_cast(uint_data[kColorFilterBlendModeIndex]); - paint_.setColorFilter(SkColorFilter::MakeModeFilter(color, blend_mode)); + sk_sp color_filter = ExtractColorFilter(uint_data, values); + if (color_filter) { + paint_.setColorFilter(color_filter); + } } switch (uint_data[kMaskFilterIndex]) { diff --git a/lib/ui/painting/paint.h b/lib/ui/painting/paint.h index 2dd4ea14d91b6..67495736b489f 100644 --- a/lib/ui/painting/paint.h +++ b/lib/ui/painting/paint.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/path.cc b/lib/ui/painting/path.cc index 2066fbe929cfa..b6daf002d40ff 100644 --- a/lib/ui/painting/path.cc +++ b/lib/ui/painting/path.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/path.h b/lib/ui/painting/path.h index 995cf1351262b..c4bffa61f68e4 100644 --- a/lib/ui/painting/path.h +++ b/lib/ui/painting/path.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/path_measure.cc b/lib/ui/painting/path_measure.cc index 8533e5627b721..6eff5c310cf91 100644 --- a/lib/ui/painting/path_measure.cc +++ b/lib/ui/painting/path_measure.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -49,9 +49,9 @@ fml::RefPtr CanvasPathMeasure::Create(const CanvasPath* path, const SkPath skPath = path->path(); SkScalar resScale = 1; pathMeasure->path_measure_ = - std::make_unique(skPath, forceClosed, resScale); + std::make_unique(skPath, forceClosed, resScale); } else { - pathMeasure->path_measure_ = std::make_unique(); + pathMeasure->path_measure_ = std::make_unique(); } return pathMeasure; } @@ -61,39 +61,53 @@ CanvasPathMeasure::CanvasPathMeasure() {} CanvasPathMeasure::~CanvasPathMeasure() {} void CanvasPathMeasure::setPath(const CanvasPath* path, bool isClosed) { - const SkPath* skPath = &(path->path()); - path_measure_->setPath(skPath, isClosed); + const SkPath& skPath = path->path(); + path_measure_->reset(skPath, isClosed); } -float CanvasPathMeasure::getLength() { - return path_measure_->getLength(); +float CanvasPathMeasure::getLength(int contourIndex) { + if (static_cast>::size_type>( + contourIndex) < measures_.size()) { + return measures_[contourIndex]->length(); + } + return -1; } -tonic::Float32List CanvasPathMeasure::getPosTan(float distance) { +tonic::Float32List CanvasPathMeasure::getPosTan(int contourIndex, + float distance) { + tonic::Float32List posTan(Dart_NewTypedData(Dart_TypedData_kFloat32, 5)); + posTan[0] = 0; // dart code will check for this for failure + if (static_cast>::size_type>( + contourIndex) >= measures_.size()) { + return posTan; + } + SkPoint pos; SkVector tan; - bool success = path_measure_->getPosTan(distance, &pos, &tan); + bool success = measures_[contourIndex]->getPosTan(distance, &pos, &tan); - tonic::Float32List posTan(Dart_NewTypedData(Dart_TypedData_kFloat32, 5)); if (success) { posTan[0] = 1; // dart code will check for this for success posTan[1] = pos.x(); posTan[2] = pos.y(); posTan[3] = tan.x(); posTan[4] = tan.y(); - } else { - posTan[0] = 0; // dart code will check for this for failure } return posTan; } -fml::RefPtr CanvasPathMeasure::getSegment(float startD, +fml::RefPtr CanvasPathMeasure::getSegment(int contourIndex, + float startD, float stopD, bool startWithMoveTo) { + if (static_cast>::size_type>( + contourIndex) >= measures_.size()) { + return CanvasPath::Create(); + } SkPath dst; bool success = - path_measure_->getSegment(startD, stopD, &dst, startWithMoveTo); + measures_[contourIndex]->getSegment(startD, stopD, &dst, startWithMoveTo); if (!success) { return CanvasPath::Create(); } else { @@ -101,12 +115,21 @@ fml::RefPtr CanvasPathMeasure::getSegment(float startD, } } -bool CanvasPathMeasure::isClosed() { - return path_measure_->isClosed(); +bool CanvasPathMeasure::isClosed(int contourIndex) { + if (static_cast>::size_type>( + contourIndex) < measures_.size()) { + return measures_[contourIndex]->isClosed(); + } + return false; } bool CanvasPathMeasure::nextContour() { - return path_measure_->nextContour(); + auto measure = path_measure_->next(); + if (measure) { + measures_.push_back(std::move(measure)); + return true; + } + return false; } } // namespace blink diff --git a/lib/ui/painting/path_measure.h b/lib/ui/painting/path_measure.h index ad21f4d358371..c19bc29e614de 100644 --- a/lib/ui/painting/path_measure.h +++ b/lib/ui/painting/path_measure.h @@ -1,14 +1,16 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_PATH_MEASURE_H_ #define FLUTTER_LIB_UI_PAINTING_PATH_MEASURE_H_ +#include + #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/path.h" +#include "third_party/skia/include/core/SkContourMeasure.h" #include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/core/SkPathMeasure.h" #include "third_party/tonic/typed_data/float64_list.h" namespace tonic { @@ -30,22 +32,24 @@ class CanvasPathMeasure : public RefCountedDartWrappable { bool forceClosed); void setPath(const CanvasPath* path, bool isClosed); - float getLength(); - tonic::Float32List getPosTan(float distance); - fml::RefPtr getSegment(float startD, + float getLength(int contourIndex); + tonic::Float32List getPosTan(int contourIndex, float distance); + fml::RefPtr getSegment(int contourIndex, + float startD, float stopD, bool startWithMoveTo); - bool isClosed(); + bool isClosed(int contourIndex); bool nextContour(); static void RegisterNatives(tonic::DartLibraryNatives* natives); - const SkPathMeasure& pathMeasure() const { return *path_measure_; } + const SkContourMeasureIter& pathMeasure() const { return *path_measure_; } private: CanvasPathMeasure(); - std::unique_ptr path_measure_; + std::unique_ptr path_measure_; + std::vector> measures_; }; } // namespace blink diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index cbadebfc0a818..ac132d05a96e0 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -1,9 +1,10 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/picture.h" +#include "flutter/fml/make_copyable.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/ui_dart_state.h" #include "third_party/skia/include/core/SkImage.h" @@ -11,6 +12,8 @@ #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/dart_persistent_value.h" +#include "third_party/tonic/logging/dart_invoke.h" namespace blink { @@ -32,12 +35,14 @@ Picture::Picture(flow::SkiaGPUObject picture) Picture::~Picture() = default; -fml::RefPtr Picture::toImage(int width, int height) { - fml::RefPtr image = CanvasImage::Create(); - image->set_image(UIDartState::CreateGPUObject(SkImage::MakeFromPicture( - picture_.get(), SkISize::Make(width, height), nullptr, nullptr, - SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()))); - return image; +Dart_Handle Picture::toImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { + if (!picture_.get()) { + return tonic::ToDart("Picture is null"); + } + + return RasterizeToImage(picture_.get(), width, height, raw_image_callback); } void Picture::dispose() { @@ -52,4 +57,81 @@ size_t Picture::GetAllocationSize() { } } +Dart_Handle Picture::RasterizeToImage(sk_sp picture, + 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"); + } + + if (width == 0 || height == 0) { + return tonic::ToDart("Image dimensions for scene were invalid."); + } + + auto* dart_state = UIDartState::Current(); + auto image_callback = std::make_unique( + dart_state, raw_image_callback); + auto unref_queue = dart_state->GetSkiaUnrefQueue(); + auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); + auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner(); + auto snapshot_delegate = dart_state->GetSnapshotDelegate(); + + // We can't create an image on this task runner because we don't have a + // graphics context. Even if we did, it would be slow anyway. Also, this + // thread owns the sole reference to the layer tree. So we flatten the layer + // tree into a picture and use that as the thread transport mechanism. + + auto picture_bounds = SkISize::Make(width, height); + + auto ui_task = fml::MakeCopyable( + [ui_task_runner, image_callback = std::move(image_callback), + unref_queue](sk_sp raster_image) mutable { + // Send the raster image back to the UI thread for submission to the + // framework. + fml::TaskRunner::RunNowOrPostTask( + ui_task_runner, + fml::MakeCopyable([raster_image, + image_callback = std::move(image_callback), + unref_queue]() mutable { + auto dart_state = image_callback->dart_state().lock(); + if (!dart_state) { + // The root isolate could have died in the meantime. + return; + } + tonic::DartState::Scope scope(dart_state); + + if (!raster_image) { + tonic::DartInvoke(image_callback->Get(), {Dart_Null()}); + return; + } + + auto dart_image = CanvasImage::Create(); + dart_image->set_image( + {std::move(raster_image), std::move(unref_queue)}); + auto* raw_dart_image = tonic::ToDart(std::move(dart_image)); + + // All done! + tonic::DartInvoke(image_callback->Get(), {raw_dart_image}); + })); + }); + + auto gpu_task = fml::MakeCopyable([gpu_task_runner, picture, picture_bounds, + snapshot_delegate, ui_task]() { + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, [snapshot_delegate, + picture, picture_bounds, + ui_task]() { + // Snapshot the picture on the GPU thread. This thread has access to the + // GPU contexts that may contain the sole references to texture backed + // images in the picture. + ui_task(snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds)); + }); + }); + + // Kick things off on the GPU. + gpu_task(); + + return Dart_Null(); +} + } // namespace blink diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index 01e7ecd596a7f..c1ff32a9bd7e2 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,14 +27,21 @@ class Picture : public RefCountedDartWrappable { sk_sp picture() const { return picture_.get(); } - fml::RefPtr toImage(int width, int height); + Dart_Handle toImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); void dispose(); - virtual size_t GetAllocationSize() override; + size_t GetAllocationSize() override; static void RegisterNatives(tonic::DartLibraryNatives* natives); + static Dart_Handle RasterizeToImage(sk_sp picture, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); + private: explicit Picture(flow::SkiaGPUObject picture); diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index 9896f2f117a30..1cce5042ae204 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index 4e5cd861ce97b..90e5a4c03f365 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -23,7 +23,7 @@ class PictureRecorder : public RefCountedDartWrappable { public: static fml::RefPtr Create(); - ~PictureRecorder(); + ~PictureRecorder() override; SkCanvas* BeginRecording(SkRect bounds); fml::RefPtr endRecording(); diff --git a/lib/ui/painting/rrect.cc b/lib/ui/painting/rrect.cc index 1436b95888df1..cd6a7d4456d33 100644 --- a/lib/ui/painting/rrect.cc +++ b/lib/ui/painting/rrect.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/rrect.h b/lib/ui/painting/rrect.h index c06769bfecc2c..c4a9045e674df 100644 --- a/lib/ui/painting/rrect.h +++ b/lib/ui/painting/rrect.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/shader.cc b/lib/ui/painting/shader.cc index 7999b84dfcc29..1331135917e71 100644 --- a/lib/ui/painting/shader.cc +++ b/lib/ui/painting/shader.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/shader.h b/lib/ui/painting/shader.h index 5fa0ce33c1d15..cfda1f341df35 100644 --- a/lib/ui/painting/shader.h +++ b/lib/ui/painting/shader.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/painting/vertices.cc b/lib/ui/painting/vertices.cc index 6fc519cd777e2..0f29ad593b65a 100644 --- a/lib/ui/painting/vertices.cc +++ b/lib/ui/painting/vertices.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -73,7 +73,7 @@ void Vertices::init(SkVertices::VertexMode vertex_mode, } if (colors.data()) { // SkVertices::Builder assumes equal numbers of elements - FML_DCHECK(positions.num_elements() == colors.num_elements()); + FML_DCHECK(positions.num_elements() / 2 == colors.num_elements()); DecodeInts(colors, builder.colors()); } diff --git a/lib/ui/painting/vertices.h b/lib/ui/painting/vertices.h index d487138385781..38ad28ac746f5 100644 --- a/lib/ui/painting/vertices.h +++ b/lib/ui/painting/vertices.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/plugins.dart b/lib/ui/plugins.dart index 0ae2b68ddbfc8..816b99aa17bc4 100644 --- a/lib/ui/plugins.dart +++ b/lib/ui/plugins.dart @@ -1,13 +1,13 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 dart.ui; -/// An wrapper for a raw callback handle. +/// A wrapper for a raw callback handle. +/// +/// This is the return type for [PluginUtilities.getCallbackHandle]. class CallbackHandle { - final int _handle; - /// Create an instance using a raw callback handle. /// /// Only values produced by a call to [CallbackHandle.toRawHandle] should be @@ -15,20 +15,31 @@ class CallbackHandle { CallbackHandle.fromRawHandle(this._handle) : assert(_handle != null, "'_handle' must not be null."); - /// Get the raw callback handle to pass over a [MethodChannel] or isolate - /// port. + final int _handle; + + /// Get the raw callback handle to pass over a [MethodChannel] or [SendPort] + /// (to pass to another [Isolate]). int toRawHandle() => _handle; @override - int get hashCode => _handle; + bool operator ==(dynamic other) { + if (runtimeType != other.runtimeType) + return false; + final CallbackHandle typedOther = other; + return _handle == typedOther._handle; + } @override - bool operator ==(dynamic other) => - (other is CallbackHandle) && (_handle == other._handle); + int get hashCode => _handle.hashCode; } /// Functionality for Flutter plugin authors. -abstract class PluginUtilities { +/// +/// See also: +/// +/// * [IsolateNameServer], which provides utilities for dealing with +/// [Isolate]s. +class PluginUtilities { // This class is only a namespace, and should not be instantiated or // extended directly. factory PluginUtilities._() => null; @@ -41,7 +52,7 @@ abstract class PluginUtilities { /// Get a handle to a named top-level or static callback function which can /// be easily passed between isolates. /// - /// `callback` must not be null. + /// The `callback` argument must not be null. /// /// Returns a [CallbackHandle] that can be provided to /// [PluginUtilities.getCallbackFromHandle] to retrieve a tear-off of the @@ -58,7 +69,7 @@ abstract class PluginUtilities { /// Get a tear-off of a named top-level or static callback represented by a /// handle. /// - /// `handle` must not be null. + /// The `handle` argument must not be null. /// /// If `handle` is not a valid handle returned by /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a diff --git a/lib/ui/plugins/callback_cache.cc b/lib/ui/plugins/callback_cache.cc index 81273d80e0daa..b2d4cc0d72d44 100644 --- a/lib/ui/plugins/callback_cache.cc +++ b/lib/ui/plugins/callback_cache.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -134,7 +134,7 @@ void DartCallbackCache::LoadCacheFromDisk() { return; } const auto entries = d.GetArray(); - for (auto it = entries.begin(); it != entries.end(); ++it) { + for (auto* it = entries.begin(); it != entries.end(); ++it) { const auto root_obj = it->GetObject(); const auto representation = root_obj[kRepresentationKey].GetObject(); diff --git a/lib/ui/plugins/callback_cache.h b/lib/ui/plugins/callback_cache.h index b13002d332f9b..997b84426a4e5 100644 --- a/lib/ui/plugins/callback_cache.h +++ b/lib/ui/plugins/callback_cache.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/pointer.dart b/lib/ui/pointer.dart index 98a4f92385e95..fd5f3a13e25aa 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -54,6 +54,18 @@ enum PointerDeviceKind { unknown } +/// The kind of [PointerDeviceKind.signal]. +enum PointerSignalKind { + /// The event is not associated with a pointer signal. + none, + + /// A pointer-generated scroll (e.g., mouse wheel or trackpad scroll). + scroll, + + /// An unknown pointer signal kind. + unknown +} + /// Information about the state of a pointer. class PointerData { /// Creates an object that represents the state of a pointer. @@ -61,6 +73,7 @@ class PointerData { this.timeStamp: Duration.zero, this.change: PointerChange.cancel, this.kind: PointerDeviceKind.touch, + this.signalKind, this.device: 0, this.physicalX: 0.0, this.physicalY: 0.0, @@ -71,12 +84,16 @@ class PointerData { this.pressureMax: 0.0, this.distance: 0.0, this.distanceMax: 0.0, + this.size: 0.0, this.radiusMajor: 0.0, this.radiusMinor: 0.0, this.radiusMin: 0.0, this.radiusMax: 0.0, this.orientation: 0.0, - this.tilt: 0.0 + this.tilt: 0.0, + this.platformData: 0, + this.scrollDeltaX: 0.0, + this.scrollDeltaY: 0.0, }); /// Time of event dispatch, relative to an arbitrary timeline. @@ -88,6 +105,9 @@ class PointerData { /// The kind of input device for which the event was generated. final PointerDeviceKind kind; + /// The kind of signal for a pointer signal event. + final PointerSignalKind signalKind; + /// Unique identifier for the pointing device, reused across interactions. final int device; @@ -137,6 +157,14 @@ class PointerData { /// 0.0. final double distanceMax; + /// The area of the screen being pressed, scaled to a value between 0 and 1. + /// The value of size can be used to determine fat touch events. This value + /// is only set on Android, and is a device specific approximation within + /// the range of detectable values. So, for example, the value of 0.1 could + /// mean a touch with the tip of the finger, 0.2 a touch with full finger, + /// and 0.3 the full palm. + final double size; + /// The radius of the contact ellipse along the major axis, in logical pixels. final double radiusMajor; @@ -190,6 +218,19 @@ class PointerData { /// the stylus is flat on that surface). final double tilt; + /// Opaque platform-specific data associated with the event. + final int platformData; + + /// For events with signalKind of PointerSignalKind.scroll: + /// + /// The amount to scroll in the x direction, in physical pixels. + final double scrollDeltaX; + + /// For events with signalKind of PointerSignalKind.scroll: + /// + /// The amount to scroll in the y direction, in physical pixels. + final double scrollDeltaY; + @override String toString() => '$runtimeType(x: $physicalX, y: $physicalY)'; @@ -199,6 +240,7 @@ class PointerData { 'timeStamp: $timeStamp, ' 'change: $change, ' 'kind: $kind, ' + 'signalKind: $signalKind, ' 'device: $device, ' 'physicalX: $physicalX, ' 'physicalY: $physicalY, ' @@ -208,12 +250,16 @@ class PointerData { 'pressureMax: $pressureMax, ' 'distance: $distance, ' 'distanceMax: $distanceMax, ' + 'size: $size, ' 'radiusMajor: $radiusMajor, ' 'radiusMinor: $radiusMinor, ' 'radiusMin: $radiusMin, ' 'radiusMax: $radiusMax, ' 'orientation: $orientation, ' - 'tilt: $tilt' + 'tilt: $tilt, ' + 'platformData: $platformData, ' + 'scrollDeltaX: $scrollDeltaX, ' + 'scrollDeltaY: $scrollDeltaY' ')'; } } diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index d8125d0ae58bc..55fb0367c5eba 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,10 @@ part of dart.ui; /// The possible actions that can be conveyed from the operating system /// accessibility APIs to a semantics node. +// +// When changes are made to this class, the equivalent APIs in +// `lib/ui/semantics/semantics_node.h` and in each of the embedders *must* be +// updated. class SemanticsAction { const SemanticsAction._(this.index); @@ -124,7 +128,7 @@ class SemanticsAction { /// Paste the current content of the clipboard. static const SemanticsAction paste = const SemanticsAction._(_kPasteIndex); - /// Indicates that the nodes has gained accessibility focus. + /// Indicates that the node has gained accessibility focus. /// /// This handler is invoked when the node annotated with this handler gains /// the accessibility focus. The accessibility focus is the @@ -137,7 +141,7 @@ class SemanticsAction { /// Accessibility focus and input focus can be held by two different nodes! static const SemanticsAction didGainAccessibilityFocus = const SemanticsAction._(_kDidGainAccessibilityFocusIndex); - /// Indicates that the nodes has lost accessibility focus. + /// Indicates that the node has lost accessibility focus. /// /// This handler is invoked when the node annotated with this handler /// loses the accessibility focus. The accessibility focus is @@ -161,7 +165,7 @@ class SemanticsAction { /// A [Snackbar], for example, may have a dismiss action to indicate to the /// user that it can be removed after it is no longer relevant. On Android, /// (with TalkBack) special hint text is spoken when focusing the node and - /// a custom action is availible in the local context menu. On iOS, + /// a custom action is available in the local context menu. On iOS, /// (with VoiceOver) users can perform a standard gesture to dismiss it. static const SemanticsAction dismiss = const SemanticsAction._(_kDismissIndex); @@ -260,6 +264,10 @@ class SemanticsAction { } /// A Boolean value that can be associated with a semantics node. +// +// When changes are made to this class, the equivalent APIs in +// `lib/ui/semantics/semantics_node.h` and in each of the embedders *must* be +// updated. class SemanticsFlag { static const int _kHasCheckedStateIndex = 1 << 0; static const int _kIsCheckedIndex = 1 << 1; @@ -404,7 +412,7 @@ class SemanticsFlag { /// that the node's semantic label can be used to announce an edge triggered /// semantics update. /// - /// Semantic nodes annotated with this flag will still recieve a11y focus. + /// Semantic nodes annotated with this flag will still receive a11y focus. /// /// Updating this label within the same active route subtree will not cause /// additional announcements. @@ -470,9 +478,9 @@ class SemanticsFlag { /// to move focus to an offscreen child. /// /// For example, a [ListView] widget has implicit scrolling so that users can - /// easily move to the next visible set of children. A [TabBar] widget does - /// not have implicit scrolling, so that users can navigate into the tab - /// body when reaching the end of the tab bar. + /// easily move the accessibility focus to the next set of children. A + /// [PageView] widget does not have implicit scrolling, so that users don't + /// navigate to the next page when reaching the end of the current one. static const SemanticsFlag hasImplicitScrolling = const SemanticsFlag._(_kHasImplicitScrollingIndex); /// The possible semantics flags. @@ -550,6 +558,7 @@ class SemanticsFlag { /// /// Once created, the [SemanticsUpdate] objects can be passed to /// [Window.updateSemantics] to update the semantics conveyed to the user. +@pragma('vm:entry-point') class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Creates an empty [SemanticsUpdateBuilder] object. @pragma('vm:entry-point') @@ -592,6 +601,11 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// The fields 'textSelectionBase' and 'textSelectionExtent' describe the /// currently selected text within `value`. /// + /// The field `platformViewId` references the platform view, whose semantics + /// nodes will be added as children to this node. If a platform view is + /// specified, `childrenInHitTestOrder` and `childrenInTraversalOrder` must be + /// empty. + /// /// For scrollable nodes `scrollPosition` describes the current scroll /// position in logical pixel. `scrollExtentMax` and `scrollExtentMin` /// describe the maximum and minimum in-rage values that `scrollPosition` can @@ -606,17 +620,28 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// /// The `transform` is a matrix that maps this node's coordinate system into /// its parent's coordinate system. + /// + /// The `elevation` describes the distance in z-direction between this node + /// and the `elevation` of the parent. + /// + /// The `thickness` describes how much space this node occupies in the + /// z-direction starting at `elevation`. Basically, in the z-direction the + /// node starts at `elevation` above the parent and ends at `elevation` + + /// `thickness` above the parent. void updateNode({ int id, int flags, int actions, int textSelectionBase, int textSelectionExtent, + int platformViewId, int scrollChildren, int scrollIndex, double scrollPosition, double scrollExtentMax, double scrollExtentMin, + double elevation, + double thickness, Rect rect, String label, String hint, @@ -627,8 +652,6 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { Float64List transform, Int32List childrenInTraversalOrder, Int32List childrenInHitTestOrder, - @Deprecated('use additionalActions instead') - Int32List customAcccessibilityActions, Int32List additionalActions, }) { if (transform.length != 16) @@ -639,6 +662,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { actions, textSelectionBase, textSelectionExtent, + platformViewId, scrollChildren, scrollIndex, scrollPosition, @@ -648,6 +672,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { rect.top, rect.right, rect.bottom, + elevation, + thickness, label, hint, value, @@ -657,7 +683,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { transform, childrenInTraversalOrder, childrenInHitTestOrder, - additionalActions ?? customAcccessibilityActions, + additionalActions, ); } void _updateNode( @@ -666,6 +692,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { int actions, int textSelectionBase, int textSelectionExtent, + int platformViewId, int scrollChildren, int scrollIndex, double scrollPosition, @@ -675,6 +702,8 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { double top, double right, double bottom, + double elevation, + double thickness, String label, String hint, String value, @@ -689,7 +718,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// Update the custom semantics action associated with the given `id`. /// - /// The name of the action exposed to the user is the `label`. For overriden + /// The name of the action exposed to the user is the `label`. For overridden /// standard actions this value is ignored. /// /// The `hint` should describe what happens when an action occurs, not the @@ -699,7 +728,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// The text direction of the `hint` and `label` is the same as the global /// window. /// - /// For overriden standard actions, `overrideId` corresponds with a + /// For overridden standard actions, `overrideId` corresponds with a /// [SemanticsAction.index] value. For custom actions this argument should not be /// provided. void updateCustomAction({int id, String label, String hint, int overrideId = -1}) { @@ -723,6 +752,7 @@ class SemanticsUpdateBuilder extends NativeFieldWrapperClass2 { /// /// Semantics updates can be applied to the system's retained semantics tree /// using the [Window.updateSemantics] method. +@pragma('vm:entry-point') class SemanticsUpdate extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated /// or extended directly. diff --git a/lib/ui/semantics/custom_accessibility_action.cc b/lib/ui/semantics/custom_accessibility_action.cc index 7f404e817027c..886bced770c7d 100644 --- a/lib/ui/semantics/custom_accessibility_action.cc +++ b/lib/ui/semantics/custom_accessibility_action.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,4 +10,4 @@ CustomAccessibilityAction::CustomAccessibilityAction() = default; CustomAccessibilityAction::~CustomAccessibilityAction() = default; -} +} // namespace blink diff --git a/lib/ui/semantics/custom_accessibility_action.h b/lib/ui/semantics/custom_accessibility_action.h index 3eb8356416143..ce9415aa00630 100644 --- a/lib/ui/semantics/custom_accessibility_action.h +++ b/lib/ui/semantics/custom_accessibility_action.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/semantics/semantics_node.cc b/lib/ui/semantics/semantics_node.cc index 563ca1d894976..fd44758aa31a9 100644 --- a/lib/ui/semantics/semantics_node.cc +++ b/lib/ui/semantics/semantics_node.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,8 +8,12 @@ namespace blink { +constexpr int32_t kMinPlatfromViewId = -1; + SemanticsNode::SemanticsNode() = default; +SemanticsNode::SemanticsNode(const SemanticsNode& other) = default; + SemanticsNode::~SemanticsNode() = default; bool SemanticsNode::HasAction(SemanticsAction action) { @@ -20,4 +24,8 @@ bool SemanticsNode::HasFlag(SemanticsFlags flag) { return (flags & static_cast(flag)) != 0; } +bool SemanticsNode::IsPlatformViewNode() const { + return platformViewId > kMinPlatfromViewId; +} + } // namespace blink diff --git a/lib/ui/semantics/semantics_node.h b/lib/ui/semantics/semantics_node.h index e5d405d38e716..6d570b42c75a9 100644 --- a/lib/ui/semantics/semantics_node.h +++ b/lib/ui/semantics/semantics_node.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,7 +16,8 @@ namespace blink { -// Must match the SemanticsAction enum in semantics.dart. +// Must match the SemanticsAction enum in semantics.dart and in each of the +// embedders. enum class SemanticsAction : int32_t { kTap = 1 << 0, kLongPress = 1 << 1, @@ -37,6 +38,8 @@ enum class SemanticsAction : int32_t { kDidLoseAccessibilityFocus = 1 << 16, kCustomAction = 1 << 17, kDismiss = 1 << 18, + kMoveCursorForwardByWordIndex = 1 << 19, + kMoveCursorBackwardByWordIndex = 1 << 20, }; const int kScrollableSemanticsActions = @@ -65,25 +68,35 @@ enum class SemanticsFlags : int32_t { kIsLiveRegion = 1 << 15, kHasToggledState = 1 << 16, kIsToggled = 1 << 17, + kHasImplicitScrolling = 1 << 18, }; struct SemanticsNode { SemanticsNode(); + + SemanticsNode(const SemanticsNode& other); + ~SemanticsNode(); bool HasAction(SemanticsAction action); bool HasFlag(SemanticsFlags flag); + // Whether this node is for embeded platform views. + bool IsPlatformViewNode() const; + int32_t id = 0; int32_t flags = 0; int32_t actions = 0; int32_t textSelectionBase = -1; int32_t textSelectionExtent = -1; + int32_t platformViewId = -1; int32_t scrollChildren = 0; int32_t scrollIndex = 0; double scrollPosition = std::nan(""); double scrollExtentMax = std::nan(""); double scrollExtentMin = std::nan(""); + double elevation = 0.0; + double thickness = 0.0; std::string label; std::string hint; std::string value; diff --git a/lib/ui/semantics/semantics_update.cc b/lib/ui/semantics/semantics_update.cc index a35befce6c384..6bb51751f46fa 100644 --- a/lib/ui/semantics/semantics_update.cc +++ b/lib/ui/semantics/semantics_update.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/semantics/semantics_update.h b/lib/ui/semantics/semantics_update.h index 21e75f60528a3..df43c425c39ba 100644 --- a/lib/ui/semantics/semantics_update.h +++ b/lib/ui/semantics/semantics_update.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/semantics/semantics_update_builder.cc b/lib/ui/semantics/semantics_update_builder.cc index 5536029565518..75cfdcf40c95a 100644 --- a/lib/ui/semantics/semantics_update_builder.cc +++ b/lib/ui/semantics/semantics_update_builder.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -41,6 +41,7 @@ void SemanticsUpdateBuilder::updateNode( int actions, int textSelectionBase, int textSelectionExtent, + int platformViewId, int scrollChildren, int scrollIndex, double scrollPosition, @@ -50,6 +51,8 @@ void SemanticsUpdateBuilder::updateNode( double top, double right, double bottom, + double elevation, + double thickness, std::string label, std::string hint, std::string value, @@ -66,12 +69,15 @@ void SemanticsUpdateBuilder::updateNode( node.actions = actions; node.textSelectionBase = textSelectionBase; node.textSelectionExtent = textSelectionExtent; + node.platformViewId = platformViewId; node.scrollChildren = scrollChildren; node.scrollIndex = scrollIndex; node.scrollPosition = scrollPosition; node.scrollExtentMax = scrollExtentMax; node.scrollExtentMin = scrollExtentMin; node.rect = SkRect::MakeLTRB(left, top, right, bottom); + node.elevation = elevation; + node.thickness = thickness; node.label = label; node.hint = hint; node.value = value; diff --git a/lib/ui/semantics/semantics_update_builder.h b/lib/ui/semantics/semantics_update_builder.h index a9422afba6cae..fa6201c3d84c1 100644 --- a/lib/ui/semantics/semantics_update_builder.h +++ b/lib/ui/semantics/semantics_update_builder.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -29,6 +29,7 @@ class SemanticsUpdateBuilder int actions, int textSelectionBase, int textSelectionExtent, + int platformViewId, int scrollChildren, int scrollIndex, double scrollPosition, @@ -38,6 +39,8 @@ class SemanticsUpdateBuilder double top, double right, double bottom, + double elevation, + double thickness, std::string label, std::string hint, std::string value, diff --git a/lib/ui/snapshot_delegate.h b/lib/ui/snapshot_delegate.h new file mode 100644 index 0000000000000..a124ff5b7ef21 --- /dev/null +++ b/lib/ui/snapshot_delegate.h @@ -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. + +#ifndef FLUTTER_LIB_UI_SNAPSHOT_DELEGATE_H_ +#define FLUTTER_LIB_UI_SNAPSHOT_DELEGATE_H_ + +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkPicture.h" + +namespace blink { + +class SnapshotDelegate { + public: + virtual sk_sp MakeRasterSnapshot(sk_sp picture, + SkISize picture_size) = 0; +}; + +} // namespace blink + +#endif // FLUTTER_LIB_UI_SNAPSHOT_DELEGATE_H_ diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 664cbaedef276..d198d2b19c1ae 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -218,6 +218,23 @@ enum TextDecorationStyle { wavy } +/// 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; +} + // This encoding must match the C++ version of ParagraphBuilder::pushStyle. // // The encoded array buffer has 8 elements. @@ -248,10 +265,12 @@ Int32List _encodeTextStyle( TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, + double decorationThickness, FontWeight fontWeight, FontStyle fontStyle, TextBaseline textBaseline, String fontFamily, + List fontFamilyFallback, double fontSize, double letterSpacing, double wordSpacing, @@ -290,46 +309,54 @@ Int32List _encodeTextStyle( result[0] |= 1 << 7; result[7] = textBaseline.index; } - if (fontFamily != null) { + if (decorationThickness != null) { result[0] |= 1 << 8; + } + if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) { + result[0] |= 1 << 9; // Passed separately to native. } if (fontSize != null) { - result[0] |= 1 << 9; + result[0] |= 1 << 10; // Passed separately to native. } if (letterSpacing != null) { - result[0] |= 1 << 10; + result[0] |= 1 << 11; // Passed separately to native. } if (wordSpacing != null) { - result[0] |= 1 << 11; + result[0] |= 1 << 12; // Passed separately to native. } if (height != null) { - result[0] |= 1 << 12; + result[0] |= 1 << 13; // Passed separately to native. } if (locale != null) { - result[0] |= 1 << 13; + result[0] |= 1 << 14; // Passed separately to native. } if (background != null) { - result[0] |= 1 << 14; + result[0] |= 1 << 15; // Passed separately to native. } if (foreground != null) { - result[0] |= 1 << 15; + result[0] |= 1 << 16; // Passed separately to native. } if (shadows != null) { - result[0] |= 1 << 16; + result[0] |= 1 << 17; // Passed separately to native. } return result; } /// An opaque object that determines the size, position, and rendering of text. +/// +/// See also: +/// +/// * [TextStyle](https://api.flutter.dev/flutter/painting/TextStyle-class.html), the class in the [painting] library. +/// class TextStyle { /// Creates a new TextStyle object. /// @@ -337,14 +364,23 @@ class TextStyle { /// * `decoration`: The decorations to paint near the text (e.g., an underline). /// * `decorationColor`: The color in which to paint the text decorations. /// * `decorationStyle`: The style in which to paint the text decorations (e.g., dashed). + /// * `decorationThickness`: The thickness of the decoration as a muliplier on the thickness specified by the font. /// * `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). - /// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). + /// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). If a `fontFamilyFallback` is + /// provided and `fontFamily` is not, then the first font family in `fontFamilyFallback` will take the position of + /// the preferred font family. When a higher priority font cannot be found or does not contain a glyph, a lower + /// priority font will be used. + /// * `fontFamilyFallback`: An ordered list of the names of the fonts to fallback on when a glyph cannot + /// be found in a higher priority font. When the `fontFamily` is null, the first font family in this list + /// is used as the preferred font. Internally, the 'fontFamily` is concatenated to the front of this list. + /// When no font family is provided through 'fontFamilyFallback' (null or empty) or `fontFamily`, then the + /// platform default font will be used. /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting the text. /// * `letterSpacing`: The amount of space (in logical pixels) to add between each letter. /// * `wordSpacing`: The amount of space (in logical pixels) to add at each sequence of white-space (i.e. between each word). /// * `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 multiple of the font size. + /// * `height`: The height of this text span, as a multiplier of the font size. /// * `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. @@ -353,10 +389,12 @@ class TextStyle { TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, + double decorationThickness, FontWeight fontWeight, FontStyle fontStyle, TextBaseline textBaseline, String fontFamily, + List fontFamilyFallback, double fontSize, double letterSpacing, double wordSpacing, @@ -374,10 +412,12 @@ class TextStyle { decoration, decorationColor, decorationStyle, + decorationThickness, fontWeight, fontStyle, textBaseline, fontFamily, + fontFamilyFallback, fontSize, letterSpacing, wordSpacing, @@ -388,10 +428,12 @@ class TextStyle { shadows, ), _fontFamily = fontFamily ?? '', + _fontFamilyFallback = fontFamilyFallback, _fontSize = fontSize, _letterSpacing = letterSpacing, _wordSpacing = wordSpacing, _height = height, + _decorationThickness = decorationThickness, _locale = locale, _background = background, _foreground = foreground, @@ -399,10 +441,12 @@ class TextStyle { final Int32List _encoded; final String _fontFamily; + final List _fontFamilyFallback; final double _fontSize; final double _letterSpacing; final double _wordSpacing; final double _height; + final double _decorationThickness; final Locale _locale; final Paint _background; final Paint _foreground; @@ -420,6 +464,7 @@ class TextStyle { _letterSpacing != typedOther._letterSpacing || _wordSpacing != typedOther._wordSpacing || _height != typedOther._height || + _decorationThickness != typedOther._decorationThickness || _locale != typedOther._locale || _background != typedOther._background || _foreground != typedOther._foreground) @@ -428,40 +473,48 @@ class TextStyle { if (_encoded[index] != typedOther._encoded[index]) return false; } - if (!Shadow._shadowsListEquals(_shadows, typedOther._shadows)) + if (!_listEquals(_shadows, typedOther._shadows)) + return false; + if (!_listEquals(_fontFamilyFallback, typedOther._fontFamilyFallback)) return false; return true; } @override - int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground); + int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, _shadows, _decorationThickness); @override String toString() { return 'TextStyle(' - 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? new Color(_encoded[1]) : "unspecified"}, ' - 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? new TextDecoration._(_encoded[2]) : "unspecified"}, ' - 'decorationColor: ${_encoded[0] & 0x00008 == 0x00008 ? new Color(_encoded[3]) : "unspecified"}, ' - 'decorationStyle: ${_encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "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] & 0x00100 == 0x00100 ? _fontFamily : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x00200 == 0x00200 ? _fontSize : "unspecified"}, ' - 'letterSpacing: ${ _encoded[0] & 0x00400 == 0x00400 ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_height}x" : "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x02000 == 0x02000 ? _locale : "unspecified"}, ' - 'background: ${ _encoded[0] & 0x04000 == 0x04000 ? _background : "unspecified"}, ' - 'foreground: ${ _encoded[0] & 0x08000 == 0x08000 ? _foreground : "unspecified"}, ' - 'shadows: ${ _encoded[0] & 0x10000 == 0x10000 ? _shadows : "unspecified"}' + 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? new Color(_encoded[1]) : "unspecified"}, ' + 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? new TextDecoration._(_encoded[2]) : "unspecified"}, ' + 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? new 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"}, ' + 'fontFamily: ${ _encoded[0] & 0x00200 == 0x00200 + && _fontFamily != null ? _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"}' ')'; } } // This encoding must match the C++ version ParagraphBuilder::build. // -// The encoded array buffer has 5 elements. +// The encoded array buffer has 6 elements. // // - Element 0: A bit mask indicating which fields are non-null. // Bit 0 is unused. Bits 1-n are set if the corresponding index in the @@ -470,21 +523,24 @@ class TextStyle { // // - Element 1: The enum index of the |textAlign|. // -// - Element 2: The index of the |fontWeight|. +// - Element 2: The enum index of the |textDirection|. +// +// - Element 3: The index of the |fontWeight|. // -// - Element 3: The enum index of the |fontStyle|. +// - Element 4: The enum index of the |fontStyle|. // -// - Element 4: The value of |maxLines|. +// - Element 5: The value of |maxLines|. // Int32List _encodeParagraphStyle( TextAlign textAlign, TextDirection textDirection, - FontWeight fontWeight, - FontStyle fontStyle, int maxLines, String fontFamily, double fontSize, - double lineHeight, + double height, + FontWeight fontWeight, + FontStyle fontStyle, + StrutStyle strutStyle, String ellipsis, Locale locale, ) { @@ -517,18 +573,22 @@ Int32List _encodeParagraphStyle( result[0] |= 1 << 7; // Passed separately to native. } - if (lineHeight != null) { + if (height != null) { result[0] |= 1 << 8; // Passed separately to native. } - if (ellipsis != null) { + if (strutStyle != null) { result[0] |= 1 << 9; // Passed separately to native. } - if (locale != null) { + if (ellipsis != null) { result[0] |= 1 << 10; // Passed separately to native. } + if (locale != null) { + result[0] |= 1 << 11; + // Passed separately to native. + } return result; } @@ -548,12 +608,6 @@ class ParagraphStyle { /// directionality of the paragraph, as well as the meaning of /// [TextAlign.start] and [TextAlign.end] in the `textAlign` field. /// - /// * `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). - /// /// * `maxLines`: The maximum number of lines painted. Lines beyond this /// number are silently dropped. For example, if `maxLines` is 1, then only /// one line is rendered. If `maxLines` is null, but `ellipsis` is not null, @@ -561,14 +615,25 @@ class ParagraphStyle { /// dropped. The width constraints are those set in the /// [ParagraphConstraints] object passed to the [Paragraph.layout] method. /// - /// * `fontFamily`: The name of the font to use when painting the text (e.g., - /// Roboto). + /// * `fontFamily`: The name of the font family to apply when painting the text, + /// in the absence of a `textStyle` being attached to the span. /// - /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting - /// the text. + /// * `fontSize`: The fallback size of glyphs (in logical pixels) to + /// use when painting the text. This is used when there is no [TextStyle]. + /// + /// * `height`: The height of the spans as a multiplier of the font size. The + /// fallback height to use when no height is provided in through + /// [TextStyle.height]. + /// + /// * `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). /// - /// * `lineHeight`: The minimum height of the line boxes, as a multiple of the - /// font size. + /// * `strutStyle`: The properties of the strut. Strut defines a set of minimum + /// vertical line height related metrics and can be used to obtain more + /// advanced line spacing behavior. /// /// * `ellipsis`: String used to ellipsize overflowing text. If `maxLines` is /// not null, then the `ellipsis`, if any, is applied to the last rendered @@ -583,36 +648,40 @@ class ParagraphStyle { ParagraphStyle({ TextAlign textAlign, TextDirection textDirection, - FontWeight fontWeight, - FontStyle fontStyle, int maxLines, String fontFamily, double fontSize, - double lineHeight, + double height, + FontWeight fontWeight, + FontStyle fontStyle, + StrutStyle strutStyle, String ellipsis, Locale locale, }) : _encoded = _encodeParagraphStyle( textAlign, textDirection, - fontWeight, - fontStyle, maxLines, fontFamily, fontSize, - lineHeight, + height, + fontWeight, + fontStyle, + strutStyle, ellipsis, locale, ), _fontFamily = fontFamily, _fontSize = fontSize, - _lineHeight = lineHeight, + _height = height, + _strutStyle = strutStyle, _ellipsis = ellipsis, _locale = locale; final Int32List _encoded; final String _fontFamily; final double _fontSize; - final double _lineHeight; + final double _height; + final StrutStyle _strutStyle; final String _ellipsis; final Locale _locale; @@ -625,7 +694,8 @@ class ParagraphStyle { final ParagraphStyle typedOther = other; if (_fontFamily != typedOther._fontFamily || _fontSize != typedOther._fontSize || - _lineHeight != typedOther._lineHeight || + _height != typedOther._height || + _strutStyle != typedOther._strutStyle || _ellipsis != typedOther._ellipsis || _locale != typedOther._locale) return false; @@ -637,7 +707,7 @@ class ParagraphStyle { } @override - int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _lineHeight, _ellipsis, _locale); + int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale); @override String toString() { @@ -649,13 +719,176 @@ class ParagraphStyle { 'maxLines: ${ _encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' 'fontFamily: ${ _encoded[0] & 0x040 == 0x040 ? _fontFamily : "unspecified"}, ' 'fontSize: ${ _encoded[0] & 0x080 == 0x080 ? _fontSize : "unspecified"}, ' - 'lineHeight: ${ _encoded[0] & 0x100 == 0x100 ? "${_lineHeight}x" : "unspecified"}, ' + 'height: ${ _encoded[0] & 0x100 == 0x100 ? "${_height}x" : "unspecified"}, ' 'ellipsis: ${ _encoded[0] & 0x200 == 0x200 ? "\"$_ellipsis\"" : "unspecified"}, ' 'locale: ${ _encoded[0] & 0x400 == 0x400 ? _locale : "unspecified"}' ')'; } } +// Serialize strut properties into ByteData. This encoding errs towards +// 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. +// +// We serialize this more thoroughly than ParagraphStyle because it is +// much more likely that the strut is empty/null and we wish to add +// minimal overhead for non-strut cases. +ByteData _encodeStrut( + String fontFamily, + List fontFamilyFallback, + double fontSize, + double height, + double leading, + FontWeight fontWeight, + FontStyle fontStyle, + bool forceStrutHeight) { + if (fontFamily == null && + fontSize == null && + height == null && + leading == null && + fontWeight == null && + fontStyle == null && + forceStrutHeight == null) + return ByteData(0); + + final ByteData data = ByteData(15); // Max size is 15 bytes + int bitmask = 0; // 8 bit mask + int byteCount = 1; + if (fontWeight != null) { + bitmask |= 1 << 0; + data.setInt8(byteCount, fontWeight.index); + byteCount += 1; + } + if (fontStyle != null) { + bitmask |= 1 << 1; + data.setInt8(byteCount, fontStyle.index); + byteCount += 1; + } + if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)){ + bitmask |= 1 << 2; + // passed separately to native + } + if (fontSize != null) { + bitmask |= 1 << 3; + data.setFloat32(byteCount, fontSize, _kFakeHostEndian); + byteCount += 4; + } + if (height != null) { + bitmask |= 1 << 4; + data.setFloat32(byteCount, height, _kFakeHostEndian); + byteCount += 4; + } + if (leading != null) { + bitmask |= 1 << 5; + 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; + } + + data.setInt8(0, bitmask); + + return ByteData.view(data.buffer, 0, byteCount); +} + +/// See also: +/// +/// * [StrutStyle](https://api.flutter.dev/flutter/painting/StrutStyle-class.html), the class in the [painting] library. +/// +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. + /// + /// * `height`: The minimum height of the line boxes, as a multiplier of the + /// font size. The lines of the paragraph will be at least `(height + 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 + /// `(height + 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 `(height + leading) * fontSize`. Otherwise, it + /// will be determined by the Ascent + half-leading of the first text. + StrutStyle({ + String fontFamily, + List fontFamilyFallback, + double fontSize, + double height, + double leading, + FontWeight fontWeight, + FontStyle fontStyle, + bool forceStrutHeight, + }) : _encoded = _encodeStrut( + fontFamily, + fontFamilyFallback, + fontSize, + height, + leading, + fontWeight, + fontStyle, + forceStrutHeight, + ), + _fontFamily = fontFamily, + _fontFamilyFallback = fontFamilyFallback; + + final ByteData _encoded; // Most of the data for strut is encoded. + final String _fontFamily; + final List _fontFamilyFallback; + + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + final StrutStyle typedOther = other; + if (_fontFamily != typedOther._fontFamily) + return false; + final Int8List encodedList = _encoded.buffer.asInt8List(); + final Int8List otherEncodedList = typedOther._encoded.buffer.asInt8List(); + for (int index = 0; index < _encoded.lengthInBytes; index += 1) { + if (encodedList[index] != otherEncodedList[index]) + return false; + } + if (!_listEquals(_fontFamilyFallback, typedOther._fontFamilyFallback)) + return false; + return true; + } + + @override + int get hashCode => hashValues(hashList(_encoded.buffer.asInt8List()), _fontFamily); + +} + /// A direction in which text flows. /// /// Some languages are written from the left to the right (for example, English, @@ -752,6 +985,7 @@ enum TextDirection { /// A rectangle enclosing a run of text. /// /// This is similar to [Rect] but includes an inherent [TextDirection]. +@pragma('vm:entry-point') class TextBox { /// Creates an object that describes a box containing text. const TextBox.fromLTRBD( @@ -832,28 +1066,70 @@ class TextBox { String toString() => 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)'; } -/// Whether a [TextPosition] is visually upstream or downstream of its offset. +/// A way to disambiguate a [TextPosition] when its offset could match two +/// different locations in the rendered string. /// -/// For example, when a text position exists at a line break, a single offset has -/// two visual positions, one prior to the line break (at the end of the first -/// line) and one after the line break (at the start of the second line). A text -/// affinity disambiguates between those cases. (Something similar happens with -/// between runs of bidirectional text.) +/// For example, at an offset where the rendered text wraps, there are two +/// visual positions that the offset could represent: one prior to the line +/// break (at the end of the first line) and one after the line break (at the +/// start of the second line). A text affinity disambiguates between these two +/// cases. +/// +/// This affects only line breaks caused by wrapping, not explicit newline +/// characters. For newline characters, the position is fully specified by the +/// offset alone, and there is no ambiguity. +/// +/// [TextAffinity] also affects bidirectional text at the interface between LTR +/// and RTL text. Consider the following string, where the lowercase letters +/// will be displayed as LTR and the uppercase letters RTL: "helloHELLO". When +/// rendered, the string would appear visually as "helloOLLEH". An offset of 5 +/// would be ambiguous without a corresponding [TextAffinity]. Looking at the +/// string in code, the offset represents the position just after the "o" and +/// just before the "H". When rendered, this offset could be either in the +/// middle of the string to the right of the "o" or at the end of the string to +/// the right of the "H". enum TextAffinity { - /// The position has affinity for the upstream side of the text position. + /// The position has affinity for the upstream side of the text position, i.e. + /// in the direction of the beginning of the string. + /// + /// In the example of an offset at the place where text is wrapping, upstream + /// indicates the end of the first line. /// - /// For example, if the offset of the text position is a line break, the - /// position represents the end of the first line. + /// In the bidirectional text example "helloHELLO", an offset of 5 with + /// [TextAffinity] upstream would appear in the middle of the rendered text, + /// just to the right of the "o". See the definition of [TextAffinity] for the + /// full example. upstream, - /// The position has affinity for the downstream side of the text position. + /// The position has affinity for the downstream side of the text position, + /// i.e. in the direction of the end of the string. /// - /// For example, if the offset of the text position is a line break, the - /// position represents the start of the second line. + /// In the example of an offset at the place where text is wrapping, + /// downstream indicates the beginning of the second line. + /// + /// In the bidirectional text example "helloHELLO", an offset of 5 with + /// [TextAffinity] downstream would appear at the end of the rendered text, + /// just to the right of the "H". See the definition of [TextAffinity] for the + /// full example. downstream, } -/// A visual position in a string of text. +/// A position in a string of text. +/// +/// A TextPosition can be used to locate a position in a string in code (using +/// the [offset] property), and it can also be used to locate the same position +/// visually in a rendered string of text (using [offset] and, when needed to +/// resolve ambiguity, [affinity]). +/// +/// The location of an offset in a rendered string is ambiguous in two cases. +/// One happens when rendered text is forced to wrap. In this case, the offset +/// where the wrap occurs could visually appear either at the end of the first +/// line or the beginning of the second line. The second way is with +/// bidirectional text. An offset at the interface between two different text +/// directions could have one of two locations in the rendered text. +/// +/// See the documentation for [TextAffinity] for more information on how +/// TextAffinity disambiguates situations like these. class TextPosition { /// Creates an object representing a particular position in a string. /// @@ -864,21 +1140,21 @@ class TextPosition { }) : assert(offset != null), assert(affinity != null); - /// The index of the character that immediately follows the position. + /// The index of the character that immediately follows the position in the + /// string representation of the text. /// /// For example, given the string `'Hello'`, offset 0 represents the cursor /// being before the `H`, while offset 5 represents the cursor being just /// after the `o`. final int offset; - /// If the offset has more than one visual location (e.g., occurs at a line - /// break), which of the two locations is represented by this position. + /// Disambiguates cases where the position in the string given by [offset] + /// could represent two different visual positions in the rendered text. For + /// example, this can happen when text is forced to wrap, or when one string + /// of text is rendered with multiple text directions. /// - /// For example, if the text `'AB'` had a forced line break between the `A` - /// and the `B`, then the downstream affinity at offset 1 represents the - /// cursor being just after the `A` on the first line, while the upstream - /// affinity at offset 1 represents the cursor being just before the `B` on - /// the first line. + /// See the documentation for [TextAffinity] for more information on how + /// TextAffinity disambiguates situations like these. final TextAffinity affinity; @override @@ -906,10 +1182,10 @@ class TextPosition { /// The only constraint that can be specified is the [width]. See the discussion /// at [width] for more details. class ParagraphConstraints { - /// Creates constraints for laying out a pargraph. + /// Creates constraints for laying out a paragraph. /// /// The [width] argument must not be null. - ParagraphConstraints({ + const ParagraphConstraints({ this.width, }) : assert(width != null); @@ -947,6 +1223,67 @@ class ParagraphConstraints { String toString() => '$runtimeType(width: $width)'; } +/// Defines various ways to vertically bound the boxes returned by +/// [Paragraph.getBoxesForRange]. +enum BoxHeightStyle { + /// Provide tight bounding boxes that fit heights per run. This style may result + /// in uneven bounding boxes that do not nicely connect with adjacent boxes. + tight, + + /// The height of the boxes will be the maximum height of all runs in the + /// line. All boxes in the same line will be the same height. This does not + /// guarantee that the boxes will cover the entire vertical height of the line + /// when there is additional line spacing. + /// + /// See [RectHeightStyle.includeLineSpacingTop], [RectHeightStyle.includeLineSpacingMiddle], + /// and [RectHeightStyle.includeLineSpacingBottom] for styles that will cover + /// the entire line. + max, + + /// Extends the top and bottom edge of the bounds to fully cover any line + /// spacing. + /// + /// 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} + /// 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 + /// and below the last line due to no additional space present there. + /// {@endtemplate} + includeLineSpacingMiddle, + + /// Extends the top edge of the bounds to fully cover any line spacing. + /// + /// The line spacing will be added to the top of the box. + /// + /// {@macro flutter.dart:ui.rectHeightStyle.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} + includeLineSpacingBottom, +} + +/// Defines various ways to horizontally bound the boxes returned by +/// [Paragraph.getBoxesForRange]. +enum BoxWidthStyle { + // Provide tight bounding boxes that fit widths to the runs of each line + // independently. + tight, + + /// Adds up to two additional boxes as needed at the beginning and/or end + /// of each line so that the widths of the boxes in line are the same width + /// as the widest line in the paragraph. The additional boxes on each line + /// are only added when the relevant box at the relevant edge of that line + /// does not span the maximum width of the paragraph. + max, +} + /// A paragraph of text. /// /// A paragraph retains the size and position of each glyph in the text and can @@ -956,6 +1293,7 @@ class ParagraphConstraints { /// /// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph] /// method. +@pragma('vm:entry-point') class Paragraph extends NativeFieldWrapperClass2 { /// This class is created by the engine, and should not be instantiated /// or extended directly. @@ -1010,7 +1348,22 @@ class Paragraph extends NativeFieldWrapperClass2 { void _layout(double width) native 'Paragraph_layout'; /// Returns a list of text boxes that enclose the given text range. - List getBoxesForRange(int start, int end) native 'Paragraph_getRectsForRange'; + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization + /// of how the boxes are bound vertically and horizontally. Both style + /// parameters default to the tight option, which will provide close-fitting + /// boxes and will not account for any line spacing. + /// + /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. + /// + /// 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); + assert(boxWidthStyle != null); + return _getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index); + } + + List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle) native 'Paragraph_getRectsForRange'; /// Returns the text position closest to the given offset. TextPosition getPositionForOffset(Offset offset) { @@ -1034,7 +1387,7 @@ class Paragraph extends NativeFieldWrapperClass2 { /// Builds a [Paragraph] containing text with the given styling information. /// -/// To set the paragraph's alignment, truncation, and ellipsising behavior, pass +/// To set the paragraph's alignment, truncation, and ellipsizing behavior, pass /// an appropriately-configured [ParagraphStyle] object to the [new /// ParagraphBuilder] constructor. /// @@ -1051,14 +1404,79 @@ class ParagraphBuilder extends NativeFieldWrapperClass2 { /// Creates a [ParagraphBuilder] object, which is used to create a /// [Paragraph]. @pragma('vm:entry-point') - ParagraphBuilder(ParagraphStyle style) { _constructor(style._encoded, style._fontFamily, style._fontSize, style._lineHeight, style._ellipsis, _encodeLocale(style._locale)); } - void _constructor(Int32List encoded, String fontFamily, double fontSize, double lineHeight, String ellipsis, String locale) native 'ParagraphBuilder_constructor'; + ParagraphBuilder(ParagraphStyle style) { + 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); + } + _constructor( + style._encoded, + style._strutStyle?._encoded, + style._fontFamily, + strutFontFamilies, + style._fontSize, + style._height, + style._ellipsis, + _encodeLocale(style._locale) + ); + } + + void _constructor( + Int32List encoded, + ByteData strutData, + String fontFamily, + List strutFontFamily, + double fontSize, + double height, + String ellipsis, + String locale + ) native 'ParagraphBuilder_constructor'; /// Applies the given style to the added text until [pop] is called. /// /// See [pop] for details. - void pushStyle(TextStyle style) => _pushStyle(style._encoded, style._fontFamily, style._fontSize, style._letterSpacing, style._wordSpacing, style._height, _encodeLocale(style._locale), style._background?._objects, style._background?._data, style._foreground?._objects, style._foreground?._data, Shadow._encodeShadows(style._shadows)); - void _pushStyle(Int32List encoded, String fontFamily, double fontSize, double letterSpacing, double wordSpacing, double height, String locale, List backgroundObjects, ByteData backgroundData, List foregroundObjects, ByteData foregroundData, ByteData shadowsData) native 'ParagraphBuilder_pushStyle'; + void pushStyle(TextStyle style) { + final List fullFontFamilies = []; + if (style._fontFamily != null) + fullFontFamilies.add(style._fontFamily); + if (style._fontFamilyFallback != null) + fullFontFamilies.addAll(style._fontFamilyFallback); + _pushStyle( + style._encoded, + fullFontFamilies, + style._fontSize, + style._letterSpacing, + style._wordSpacing, + style._height, + style._decorationThickness, + _encodeLocale(style._locale), + style._background?._objects, + style._background?._data, + style._foreground?._objects, + style._foreground?._data, + Shadow._encodeShadows(style._shadows) + ); + } + + void _pushStyle( + Int32List encoded, + List fontFamilies, + double fontSize, + double letterSpacing, + double wordSpacing, + double height, + double decorationThickness, + String locale, + List backgroundObjects, + ByteData backgroundData, + List foregroundObjects, + ByteData foregroundData, + ByteData shadowsData + ) native 'ParagraphBuilder_pushStyle'; static String _encodeLocale(Locale locale) => locale?.toString() ?? ''; diff --git a/lib/ui/text/asset_manager_font_provider.cc b/lib/ui/text/asset_manager_font_provider.cc index b0be57f13e4a9..da2d0a15be9d7 100644 --- a/lib/ui/text/asset_manager_font_provider.cc +++ b/lib/ui/text/asset_manager_font_provider.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,7 +21,7 @@ void MappingReleaseProc(const void* ptr, void* context) { } // anonymous namespace AssetManagerFontProvider::AssetManagerFontProvider( - fml::RefPtr asset_manager) + std::shared_ptr asset_manager) : asset_manager_(asset_manager) {} AssetManagerFontProvider::~AssetManagerFontProvider() = default; @@ -65,7 +65,7 @@ void AssetManagerFontProvider::RegisterAsset(std::string family_name, } AssetManagerFontStyleSet::AssetManagerFontStyleSet( - fml::RefPtr asset_manager) + std::shared_ptr asset_manager) : asset_manager_(asset_manager) {} AssetManagerFontStyleSet::~AssetManagerFontStyleSet() = default; @@ -123,4 +123,12 @@ SkTypeface* AssetManagerFontStyleSet::matchStyle(const SkFontStyle& pattern) { return SkRef(assets_[0].typeface.get()); } +AssetManagerFontStyleSet::TypefaceAsset::TypefaceAsset(std::string a) + : asset(std::move(a)) {} + +AssetManagerFontStyleSet::TypefaceAsset::TypefaceAsset( + const AssetManagerFontStyleSet::TypefaceAsset& other) = default; + +AssetManagerFontStyleSet::TypefaceAsset::~TypefaceAsset() = default; + } // namespace blink diff --git a/lib/ui/text/asset_manager_font_provider.h b/lib/ui/text/asset_manager_font_provider.h index a6f9544ab2611..d55cf71e3bd93 100644 --- a/lib/ui/text/asset_manager_font_provider.h +++ b/lib/ui/text/asset_manager_font_provider.h @@ -1,10 +1,11 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_TEXT_ASSET_MANAGER_FONT_PROVIDER_H_ #define FLUTTER_LIB_UI_TEXT_ASSET_MANAGER_FONT_PROVIDER_H_ +#include #include #include #include @@ -12,13 +13,14 @@ #include "flutter/assets/asset_manager.h" #include "flutter/fml/macros.h" #include "third_party/skia/include/core/SkFontMgr.h" +#include "third_party/skia/include/core/SkTypeface.h" #include "txt/font_asset_provider.h" namespace blink { class AssetManagerFontStyleSet : public SkFontStyleSet { public: - AssetManagerFontStyleSet(fml::RefPtr asset_manager); + AssetManagerFontStyleSet(std::shared_ptr asset_manager); ~AssetManagerFontStyleSet() override; @@ -37,10 +39,15 @@ class AssetManagerFontStyleSet : public SkFontStyleSet { SkTypeface* matchStyle(const SkFontStyle& pattern) override; private: - fml::RefPtr asset_manager_; + std::shared_ptr asset_manager_; struct TypefaceAsset { - TypefaceAsset(std::string a) : asset(std::move(a)) {} + TypefaceAsset(std::string a); + + TypefaceAsset(const TypefaceAsset& other); + + ~TypefaceAsset(); + std::string asset; sk_sp typeface; }; @@ -51,7 +58,8 @@ class AssetManagerFontStyleSet : public SkFontStyleSet { class AssetManagerFontProvider : public txt::FontAssetProvider { public: - AssetManagerFontProvider(fml::RefPtr asset_manager); + AssetManagerFontProvider(std::shared_ptr asset_manager); + ~AssetManagerFontProvider() override; void RegisterAsset(std::string family_name, std::string asset); @@ -66,7 +74,7 @@ class AssetManagerFontProvider : public txt::FontAssetProvider { SkFontStyleSet* MatchFamily(const std::string& family_name) override; private: - fml::RefPtr asset_manager_; + std::shared_ptr asset_manager_; std::unordered_map registered_families_; std::vector family_names_; diff --git a/lib/ui/text/font_collection.cc b/lib/ui/text/font_collection.cc index 83166f96bbf9d..3f41f6457c83e 100644 --- a/lib/ui/text/font_collection.cc +++ b/lib/ui/text/font_collection.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -67,7 +67,8 @@ std::shared_ptr FontCollection::GetFontCollection() const { return collection_; } -void FontCollection::RegisterFonts(fml::RefPtr asset_manager) { +void FontCollection::RegisterFonts( + std::shared_ptr asset_manager) { std::unique_ptr manifest_mapping = asset_manager->GetAsMapping("FontManifest.json"); if (manifest_mapping == nullptr) { @@ -157,6 +158,7 @@ void FontCollection::LoadFontFromList(const uint8_t* font_data, } else { font_provider.RegisterTypeface(typeface, family_name); } + collection_->ClearFontFamilyCache(); } } // namespace blink diff --git a/lib/ui/text/font_collection.h b/lib/ui/text/font_collection.h index a9bf31d73a2bc..a50eab4b8d529 100644 --- a/lib/ui/text/font_collection.h +++ b/lib/ui/text/font_collection.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -29,7 +29,7 @@ class FontCollection { std::shared_ptr GetFontCollection() const; - void RegisterFonts(fml::RefPtr asset_manager); + void RegisterFonts(std::shared_ptr asset_manager); void RegisterTestFonts(); diff --git a/lib/ui/text/paragraph.cc b/lib/ui/text/paragraph.cc index 032ee1af4d9bd..9c4438f12f19d 100644 --- a/lib/ui/text/paragraph.cc +++ b/lib/ui/text/paragraph.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -84,9 +84,13 @@ void Paragraph::paint(Canvas* canvas, double x, double y) { m_paragraphImpl->paint(canvas, x, y); } -std::vector Paragraph::getRectsForRange(unsigned start, unsigned end) { - return m_paragraphImpl->getRectsForRange(start, end, - txt::Paragraph::RectStyle::kTight); +std::vector Paragraph::getRectsForRange(unsigned start, + unsigned end, + unsigned boxHeightStyle, + unsigned boxWidthStyle) { + return m_paragraphImpl->getRectsForRange( + start, end, static_cast(boxHeightStyle), + static_cast(boxWidthStyle)); } Dart_Handle Paragraph::getPositionForOffset(double dx, double dy) { diff --git a/lib/ui/text/paragraph.h b/lib/ui/text/paragraph.h index e5f5e2bddb703..15df77310c17a 100644 --- a/lib/ui/text/paragraph.h +++ b/lib/ui/text/paragraph.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -42,11 +42,14 @@ class Paragraph : public RefCountedDartWrappable { void layout(double width); void paint(Canvas* canvas, double x, double y); - std::vector getRectsForRange(unsigned start, unsigned end); + std::vector getRectsForRange(unsigned start, + unsigned end, + unsigned boxHeightStyle, + unsigned boxWidthStyle); Dart_Handle getPositionForOffset(double dx, double dy); Dart_Handle getWordBoundary(unsigned offset); - virtual size_t GetAllocationSize() override; + size_t GetAllocationSize() override; static void RegisterNatives(tonic::DartLibraryNatives* natives); diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index ea9f7f5a1f0d0..da8a4366ccbcc 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,20 +36,22 @@ const int tsTextDecorationStyleIndex = 4; const int tsFontWeightIndex = 5; const int tsFontStyleIndex = 6; const int tsTextBaselineIndex = 7; -const int tsFontFamilyIndex = 8; -const int tsFontSizeIndex = 9; -const int tsLetterSpacingIndex = 10; -const int tsWordSpacingIndex = 11; -const int tsHeightIndex = 12; -const int tsLocaleIndex = 13; -const int tsBackgroundIndex = 14; -const int tsForegroundIndex = 15; -const int tsTextShadowsIndex = 16; +const int tsTextDecorationThicknessIndex = 8; +const int tsFontFamilyIndex = 9; +const int tsFontSizeIndex = 10; +const int tsLetterSpacingIndex = 11; +const int tsWordSpacingIndex = 12; +const int tsHeightIndex = 13; +const int tsLocaleIndex = 14; +const int tsBackgroundIndex = 15; +const int tsForegroundIndex = 16; +const int tsTextShadowsIndex = 17; const int tsColorMask = 1 << tsColorIndex; const int tsTextDecorationMask = 1 << tsTextDecorationIndex; const int tsTextDecorationColorMask = 1 << tsTextDecorationColorIndex; const int tsTextDecorationStyleMask = 1 << tsTextDecorationStyleIndex; +const int tsTextDecorationThicknessMask = 1 << tsTextDecorationThicknessIndex; const int tsFontWeightMask = 1 << tsFontWeightIndex; const int tsFontStyleMask = 1 << tsFontStyleIndex; const int tsTextBaselineMask = 1 << tsTextBaselineIndex; @@ -72,9 +74,10 @@ const int psFontStyleIndex = 4; const int psMaxLinesIndex = 5; const int psFontFamilyIndex = 6; const int psFontSizeIndex = 7; -const int psLineHeightIndex = 8; -const int psEllipsisIndex = 9; -const int psLocaleIndex = 10; +const int psHeightIndex = 8; +const int psStrutStyleIndex = 9; +const int psEllipsisIndex = 10; +const int psLocaleIndex = 11; const int psTextAlignMask = 1 << psTextAlignIndex; const int psTextDirectionMask = 1 << psTextDirectionIndex; @@ -83,7 +86,8 @@ const int psFontStyleMask = 1 << psFontStyleIndex; const int psMaxLinesMask = 1 << psMaxLinesIndex; const int psFontFamilyMask = 1 << psFontFamilyIndex; const int psFontSizeMask = 1 << psFontSizeIndex; -const int psLineHeightMask = 1 << psLineHeightIndex; +const int psHeightMask = 1 << psHeightIndex; +const int psStrutStyleMask = 1 << psStrutStyleIndex; const int psEllipsisMask = 1 << psEllipsisIndex; const int psLocaleMask = 1 << psLocaleIndex; @@ -97,6 +101,23 @@ constexpr uint32_t kXOffset = 1; constexpr uint32_t kYOffset = 2; constexpr uint32_t kBlurOffset = 3; +// Strut decoding +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 sFontWeightMask = 1 << sFontWeightIndex; +const int sFontStyleMask = 1 << sFontStyleIndex; +const int sFontFamilyMask = 1 << sFontFamilyIndex; +const int sFontSizeMask = 1 << sFontSizeIndex; +const int sHeightMask = 1 << sHeightIndex; +const int sLeadingMask = 1 << sLeadingIndex; +const int sForceStrutHeightMask = 1 << sForceStrutHeightIndex; + } // namespace static void ParagraphBuilder_constructor(Dart_NativeArguments args) { @@ -115,59 +136,140 @@ FOR_EACH_BINDING(DART_NATIVE_CALLBACK) void ParagraphBuilder::RegisterNatives(tonic::DartLibraryNatives* natives) { natives->Register( - {{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 7, true}, + {{"ParagraphBuilder_constructor", ParagraphBuilder_constructor, 9, true}, FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); } fml::RefPtr ParagraphBuilder::create( tonic::Int32List& encoded, + Dart_Handle strutData, const std::string& fontFamily, + const std::vector& strutFontFamilies, double fontSize, - double lineHeight, + double height, const std::u16string& ellipsis, const std::string& locale) { - return fml::MakeRefCounted(encoded, fontFamily, fontSize, - lineHeight, ellipsis, locale); + return fml::MakeRefCounted(encoded, strutData, fontFamily, + strutFontFamilies, fontSize, + height, ellipsis, locale); } -ParagraphBuilder::ParagraphBuilder(tonic::Int32List& encoded, - const std::string& fontFamily, - double fontSize, - double lineHeight, - const std::u16string& ellipsis, - const std::string& locale) { +// returns true if there is a font family defined. Font family is the only +// parameter passed directly. +void decodeStrut(Dart_Handle strut_data, + const std::vector& strut_font_families, + txt::ParagraphStyle& paragraph_style) { + if (strut_data == Dart_Null()) { + return; + } + + tonic::DartByteData byte_data(strut_data); + if (byte_data.length_in_bytes() == 0) { + return; + } + paragraph_style.strut_enabled = true; + + const uint8_t* uint8_data = static_cast(byte_data.data()); + uint8_t mask = uint8_data[0]; + + // Data is stored in order of increasing size, eg, 8 bit ints will be before + // any 32 bit ints. In addition, the order of decoding is the same order + // as it is encoded, and the order is used to maintain consistency. + size_t byte_count = 1; + if (mask & sFontWeightMask) { + paragraph_style.strut_font_weight = + static_cast(uint8_data[byte_count++]); + } + if (mask & sFontStyleMask) { + paragraph_style.strut_font_style = + static_cast(uint8_data[byte_count++]); + } + + std::vector float_data; + float_data.resize((byte_data.length_in_bytes() - byte_count) / 4); + memcpy(float_data.data(), + static_cast(byte_data.data()) + byte_count, + byte_data.length_in_bytes() - byte_count); + size_t float_count = 0; + if (mask & sFontSizeMask) { + paragraph_style.strut_font_size = float_data[float_count++]; + } + if (mask & sHeightMask) { + paragraph_style.strut_height = float_data[float_count++]; + } + 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; + } + + if (mask & sFontFamilyMask) { + paragraph_style.strut_font_families = strut_font_families; + } else { + // Provide an empty font name so that the platform default font will be + // used. + paragraph_style.strut_font_families.push_back(""); + } +} + +ParagraphBuilder::ParagraphBuilder( + tonic::Int32List& encoded, + Dart_Handle strutData, + const std::string& fontFamily, + const std::vector& strutFontFamilies, + double fontSize, + double height, + const std::u16string& ellipsis, + const std::string& locale) { int32_t mask = encoded[0]; txt::ParagraphStyle style; - if (mask & psTextAlignMask) + + if (mask & psTextAlignMask) { style.text_align = txt::TextAlign(encoded[psTextAlignIndex]); + } - if (mask & psTextDirectionMask) + if (mask & psTextDirectionMask) { style.text_direction = txt::TextDirection(encoded[psTextDirectionIndex]); + } - if (mask & psFontWeightMask) + if (mask & psFontWeightMask) { style.font_weight = static_cast(encoded[psFontWeightIndex]); + } - if (mask & psFontStyleMask) + if (mask & psFontStyleMask) { style.font_style = static_cast(encoded[psFontStyleIndex]); + } - if (mask & psFontFamilyMask) + if (mask & psFontFamilyMask) { style.font_family = fontFamily; + } - if (mask & psFontSizeMask) + if (mask & psFontSizeMask) { style.font_size = fontSize; + } - if (mask & psLineHeightMask) - style.line_height = lineHeight; + if (mask & psHeightMask) { + style.height = height; + } - if (mask & psMaxLinesMask) + if (mask & psStrutStyleMask) { + decodeStrut(strutData, strutFontFamilies, style); + } + + if (mask & psMaxLinesMask) { style.max_lines = encoded[psMaxLinesIndex]; + } - if (mask & psEllipsisMask) + if (mask & psEllipsisMask) { style.ellipsis = ellipsis; + } - if (mask & psLocaleMask) + if (mask & psLocaleMask) { style.locale = locale; + } FontCollection& font_collection = UIDartState::Current()->window()->client()->GetFontCollection(); @@ -202,11 +304,12 @@ void decodeTextShadows(Dart_Handle shadows_data, } void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, - const std::string& fontFamily, + const std::vector& fontFamilies, double fontSize, double letterSpacing, double wordSpacing, double height, + double decorationThickness, const std::string& locale, Dart_Handle background_objects, Dart_Handle background_data, @@ -223,28 +326,35 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, // Only change the style property from the previous value if a new explicitly // set value is available - if (mask & tsColorMask) + if (mask & tsColorMask) { style.color = encoded[tsColorIndex]; + } if (mask & tsTextDecorationMask) { style.decoration = static_cast(encoded[tsTextDecorationIndex]); } - if (mask & tsTextDecorationColorMask) + if (mask & tsTextDecorationColorMask) { style.decoration_color = encoded[tsTextDecorationColorIndex]; + } - if (mask & tsTextDecorationStyleMask) + if (mask & tsTextDecorationStyleMask) { style.decoration_style = static_cast( encoded[tsTextDecorationStyleIndex]); + } + + if (mask & tsTextDecorationThicknessMask) { + style.decoration_thickness_multiplier = decorationThickness; + } if (mask & tsTextBaselineMask) { // TODO(abarth): Implement TextBaseline. The CSS version of this // property wasn't wired up either. } - if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontFamilyMask | - tsFontSizeMask | tsLetterSpacingMask | tsWordSpacingMask)) { + if (mask & (tsFontWeightMask | tsFontStyleMask | tsFontSizeMask | + tsLetterSpacingMask | tsWordSpacingMask)) { if (mask & tsFontWeightMask) style.font_weight = static_cast(encoded[tsFontWeightIndex]); @@ -252,9 +362,6 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, if (mask & tsFontStyleMask) style.font_style = static_cast(encoded[tsFontStyleIndex]); - if (mask & tsFontFamilyMask) - style.font_family = fontFamily; - if (mask & tsFontSizeMask) style.font_size = fontSize; @@ -293,6 +400,11 @@ void ParagraphBuilder::pushStyle(tonic::Int32List& encoded, decodeTextShadows(shadows_data, style.text_shadows); } + if (mask & tsFontFamilyMask) { + style.font_families.insert(style.font_families.end(), fontFamilies.begin(), + fontFamilies.end()); + } + m_paragraphBuilder->PushStyle(style); } diff --git a/lib/ui/text/paragraph_builder.h b/lib/ui/text/paragraph_builder.h index 3e7a8301ddf0b..9ef52fbe4c0d6 100644 --- a/lib/ui/text/paragraph_builder.h +++ b/lib/ui/text/paragraph_builder.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -25,21 +25,25 @@ class ParagraphBuilder : public RefCountedDartWrappable { FML_FRIEND_MAKE_REF_COUNTED(ParagraphBuilder); public: - static fml::RefPtr create(tonic::Int32List& encoded, - const std::string& fontFamily, - double fontSize, - double lineHeight, - const std::u16string& ellipsis, - const std::string& locale); + static fml::RefPtr create( + tonic::Int32List& encoded, + Dart_Handle strutData, + const std::string& fontFamily, + const std::vector& strutFontFamilies, + double fontSize, + double height, + const std::u16string& ellipsis, + const std::string& locale); ~ParagraphBuilder() override; void pushStyle(tonic::Int32List& encoded, - const std::string& fontFamily, + const std::vector& fontFamilies, double fontSize, double letterSpacing, double wordSpacing, double height, + double decorationThickness, const std::string& locale, Dart_Handle background_objects, Dart_Handle background_data, @@ -57,9 +61,11 @@ class ParagraphBuilder : public RefCountedDartWrappable { private: explicit ParagraphBuilder(tonic::Int32List& encoded, + Dart_Handle strutData, const std::string& fontFamily, + const std::vector& strutFontFamilies, double fontSize, - double lineHeight, + double height, const std::u16string& ellipsis, const std::string& locale); diff --git a/lib/ui/text/paragraph_impl.cc b/lib/ui/text/paragraph_impl.cc index 65483a8b68142..eb5f4bb0d9005 100644 --- a/lib/ui/text/paragraph_impl.cc +++ b/lib/ui/text/paragraph_impl.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/text/paragraph_impl.h b/lib/ui/text/paragraph_impl.h index cf71897d250e4..b1b3c51f77107 100644 --- a/lib/ui/text/paragraph_impl.h +++ b/lib/ui/text/paragraph_impl.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,7 +36,8 @@ class ParagraphImpl { virtual std::vector getRectsForRange( unsigned start, unsigned end, - txt::Paragraph::RectStyle rect_style) = 0; + txt::Paragraph::RectHeightStyle rect_height_style, + txt::Paragraph::RectWidthStyle rect_width_style) = 0; virtual Dart_Handle getPositionForOffset(double dx, double dy) = 0; diff --git a/lib/ui/text/paragraph_impl_txt.cc b/lib/ui/text/paragraph_impl_txt.cc index cc55638db2680..5a00fbbda93cd 100644 --- a/lib/ui/text/paragraph_impl_txt.cc +++ b/lib/ui/text/paragraph_impl_txt.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -64,10 +64,11 @@ void ParagraphImplTxt::paint(Canvas* canvas, double x, double y) { std::vector ParagraphImplTxt::getRectsForRange( unsigned start, unsigned end, - txt::Paragraph::RectStyle rect_style) { + txt::Paragraph::RectHeightStyle rect_height_style, + txt::Paragraph::RectWidthStyle rect_width_style) { std::vector result; - std::vector boxes = - m_paragraph->GetRectsForRange(start, end, rect_style); + std::vector boxes = m_paragraph->GetRectsForRange( + start, end, rect_height_style, rect_width_style); for (const txt::Paragraph::TextBox& box : boxes) { result.emplace_back(box.rect, static_cast(box.direction)); diff --git a/lib/ui/text/paragraph_impl_txt.h b/lib/ui/text/paragraph_impl_txt.h index 665d65b15cce2..226526e6e696f 100644 --- a/lib/ui/text/paragraph_impl_txt.h +++ b/lib/ui/text/paragraph_impl_txt.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,7 +31,8 @@ class ParagraphImplTxt : public ParagraphImpl { std::vector getRectsForRange( unsigned start, unsigned end, - txt::Paragraph::RectStyle rect_style) override; + txt::Paragraph::RectHeightStyle rect_height_style, + txt::Paragraph::RectWidthStyle rect_width_style) override; Dart_Handle getPositionForOffset(double dx, double dy) override; Dart_Handle getWordBoundary(unsigned offset) override; diff --git a/lib/ui/text/text_box.cc b/lib/ui/text/text_box.cc index 8e254afe40ad9..2d22d954a8b93 100644 --- a/lib/ui/text/text_box.cc +++ b/lib/ui/text/text_box.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/text/text_box.h b/lib/ui/text/text_box.h index 839142ddcba8d..785f5b37bd3b6 100644 --- a/lib/ui/text/text_box.h +++ b/lib/ui/text/text_box.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/ui.dart b/lib/ui/ui.dart index 8c4480c072cdb..5730f4e1378fa 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,12 +11,12 @@ /// text, layout, and rendering subsystems. library dart.ui; -import 'dart:_internal' hide Symbol; +import 'dart:_internal' hide Symbol; // ignore: import_internal_library, unused_import import 'dart:async'; import 'dart:collection' as collection; import 'dart:convert'; import 'dart:developer' as developer; -import 'dart:io'; +import 'dart:io'; // ignore: unused_import import 'dart:isolate' show SendPort; import 'dart:math' as math; import 'dart:nativewrappers'; @@ -34,4 +34,5 @@ part 'plugins.dart'; part 'pointer.dart'; part 'semantics.dart'; part 'text.dart'; +part 'versions.dart'; part 'window.dart'; diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index 0d762f785262d..b42cbaf5ba607 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,23 +13,26 @@ using tonic::ToDart; namespace blink { -UIDartState::UIDartState(TaskRunners task_runners, - TaskObserverAdd add_callback, - TaskObserverRemove remove_callback, - fml::WeakPtr resource_context, - fml::RefPtr skia_unref_queue, - std::string advisory_script_uri, - std::string advisory_script_entrypoint, - std::string logger_prefix, - IsolateNameServer* isolate_name_server) +UIDartState::UIDartState( + TaskRunners task_runners, + TaskObserverAdd add_callback, + TaskObserverRemove remove_callback, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + std::string logger_prefix, + UnhandledExceptionCallback unhandled_exception_callback, + IsolateNameServer* isolate_name_server) : task_runners_(std::move(task_runners)), add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), - resource_context_(std::move(resource_context)), + snapshot_delegate_(std::move(snapshot_delegate)), + io_manager_(std::move(io_manager)), advisory_script_uri_(std::move(advisory_script_uri)), advisory_script_entrypoint_(std::move(advisory_script_entrypoint)), logger_prefix_(std::move(logger_prefix)), - skia_unref_queue_(std::move(skia_unref_queue)), + unhandled_exception_callback_(unhandled_exception_callback), isolate_name_server_(isolate_name_server) { AddOrRemoveTaskObserver(true /* add */); } @@ -52,7 +55,13 @@ void UIDartState::DidSetIsolate() { // main.dart$main-1234 debug_name << advisory_script_uri_ << "$" << advisory_script_entrypoint_ << "-" << main_port_; - debug_name_ = debug_name.str(); + SetDebugName(debug_name.str()); +} + +void UIDartState::SetDebugName(const std::string debug_name) { + debug_name_ = debug_name; + if (window_) + window_->client()->UpdateIsolateDescription(debug_name_, main_port_); } UIDartState* UIDartState::Current() { @@ -61,6 +70,8 @@ UIDartState* UIDartState::Current() { void UIDartState::SetWindow(std::unique_ptr window) { window_ = std::move(window); + if (window_) + window_->client()->UpdateIsolateDescription(debug_name_, main_port_); } const TaskRunners& UIDartState::GetTaskRunners() const { @@ -68,7 +79,10 @@ const TaskRunners& UIDartState::GetTaskRunners() const { } fml::RefPtr UIDartState::GetSkiaUnrefQueue() const { - return skia_unref_queue_; + if (!io_manager_) { + return nullptr; + } + return io_manager_->GetSkiaUnrefQueue(); } void UIDartState::ScheduleMicrotask(Dart_Handle closure) { @@ -99,8 +113,15 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) { } } +fml::WeakPtr UIDartState::GetSnapshotDelegate() const { + return snapshot_delegate_; +} + fml::WeakPtr UIDartState::GetResourceContext() const { - return resource_context_; + if (!io_manager_) { + return {}; + } + return io_manager_->GetResourceContext(); } IsolateNameServer* UIDartState::GetIsolateNameServer() { @@ -115,4 +136,17 @@ tonic::DartErrorHandleType UIDartState::GetLastError() { return error; } +void UIDartState::ReportUnhandledException(const std::string& error, + const std::string& stack_trace) { + if (unhandled_exception_callback_ && + unhandled_exception_callback_(error, stack_trace)) { + return; + } + + // Either the exception handler was not set or it could not handle the error, + // just log the exception. + FML_LOG(ERROR) << "Unhandled Exception: " << error << std::endl + << stack_trace; +} + } // namespace blink diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index 6d4094a47f2be..f2c63cbacef60 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,7 +14,9 @@ #include "flutter/flow/skia_gpu_object.h" #include "flutter/fml/build_config.h" #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/io_manager.h" #include "flutter/lib/ui/isolate_name_server/isolate_name_server.h" +#include "flutter/lib/ui/snapshot_delegate.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/tonic/dart_microtask_queue.h" @@ -31,6 +33,8 @@ class UIDartState : public tonic::DartState { Dart_Port main_port() const { return main_port_; } + void SetDebugName(const std::string name); + const std::string& debug_name() const { return debug_name_; } const std::string& logger_prefix() const { return logger_prefix_; } @@ -45,18 +49,23 @@ class UIDartState : public tonic::DartState { fml::RefPtr GetSkiaUnrefQueue() const; + fml::WeakPtr GetSnapshotDelegate() const; + fml::WeakPtr GetResourceContext() const; IsolateNameServer* GetIsolateNameServer(); tonic::DartErrorHandleType GetLastError(); + void ReportUnhandledException(const std::string& error, + const std::string& stack_trace); + template static flow::SkiaGPUObject CreateGPUObject(sk_sp object) { if (!object) { return {}; } - auto state = UIDartState::Current(); + auto* state = UIDartState::Current(); FML_DCHECK(state); auto queue = state->GetSkiaUnrefQueue(); return {std::move(object), std::move(queue)}; @@ -66,11 +75,12 @@ class UIDartState : public tonic::DartState { UIDartState(TaskRunners task_runners, TaskObserverAdd add_callback, TaskObserverRemove remove_callback, - fml::WeakPtr resource_context, - fml::RefPtr skia_unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, std::string logger_prefix, + UnhandledExceptionCallback unhandled_exception_callback, IsolateNameServer* isolate_name_server); ~UIDartState() override; @@ -87,15 +97,16 @@ class UIDartState : public tonic::DartState { const TaskRunners task_runners_; const TaskObserverAdd add_callback_; const TaskObserverRemove remove_callback_; - fml::WeakPtr resource_context_; + fml::WeakPtr snapshot_delegate_; + fml::WeakPtr io_manager_; const std::string advisory_script_uri_; const std::string advisory_script_entrypoint_; const std::string logger_prefix_; Dart_Port main_port_ = ILLEGAL_PORT; std::string debug_name_; std::unique_ptr window_; - fml::RefPtr skia_unref_queue_; tonic::DartMicrotaskQueue microtask_queue_; + UnhandledExceptionCallback unhandled_exception_callback_; IsolateNameServer* isolate_name_server_; void AddOrRemoveTaskObserver(bool add); diff --git a/lib/ui/versions.cc b/lib/ui/versions.cc new file mode 100644 index 0000000000000..b044621a86916 --- /dev/null +++ b/lib/ui/versions.cc @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter 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/versions.h" +#include "flutter/common/version/version.h" +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_library_natives.h" + +#include +#include + +using tonic::DartConverter; + +namespace blink { + +// returns a vector with 3 versions. +// Dart, Skia and Flutter engine versions in this order. +void GetVersions(Dart_NativeArguments args) { + const std::vector versions_list = { + GetDartVersion(), GetSkiaVersion(), GetFlutterEngineVersion()}; + Dart_Handle dart_val = + DartConverter>::ToDart(versions_list); + Dart_SetReturnValue(args, dart_val); +} + +void Versions::RegisterNatives(tonic::DartLibraryNatives* natives) { + natives->Register({{"Versions_getVersions", GetVersions, 0, true}}); +} + +} // namespace blink diff --git a/lib/ui/versions.dart b/lib/ui/versions.dart new file mode 100644 index 0000000000000..9121234856082 --- /dev/null +++ b/lib/ui/versions.dart @@ -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. + +part of dart.ui; + +/// Wraps version information for Dart, Skia and Flutter. +class Versions { + + /// Builds a versions object using the information + /// we get from calling the native getVersions. + factory Versions._internal() { + final List versions = _getVersions(); + return Versions._(versions[0], versions[1], versions[2]); + } + + /// Private constructor to capture the versions. + Versions._( + this.dartVersion, + this.skiaVersion, + this.flutterEngineVersion + ) : assert(dartVersion != null), + assert(skiaVersion != null), + assert(flutterEngineVersion != null); + + /// returns a vector with 3 versions. + /// Dart, Skia and Flutter engine versions in this order. + static List _getVersions() native 'Versions_getVersions'; + + final String dartVersion; + final String skiaVersion; + final String flutterEngineVersion; +} + +/// [Versions] singleton. This object exposes Dart, Skia and +/// Flutter engine versions. +final Versions versions = Versions._internal(); diff --git a/lib/ui/versions.h b/lib/ui/versions.h new file mode 100644 index 0000000000000..84737e5eb68e6 --- /dev/null +++ b/lib/ui/versions.h @@ -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. + +#ifndef FLUTTER_LIB_UI_VERSIONS_H_ +#define FLUTTER_LIB_UI_VERSIONS_H_ + +namespace tonic { +class DartLibraryNatives; +} // namespace tonic + +namespace blink { + +class Versions final { + public: + static void RegisterNatives(tonic::DartLibraryNatives* natives); +}; + +} // namespace blink + +#endif // FLUTTER_LIB_UI_VERSIONS_H_ diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 0b1318c997aaf..27a8902d2b3ef 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -116,9 +116,11 @@ class WindowPadding { } } -/// An identifier used to select a user's language and formatting preferences, -/// consisting of a language and a country. This is a subset of locale -/// identifiers as defined by BCP 47. +/// 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 @@ -133,7 +135,8 @@ class WindowPadding { /// [Locale]. class Locale { /// Creates a new Locale object. The first argument is the - /// primary language subtag, the second is the region subtag. + /// primary language subtag, the second is the region (also + /// referred to as 'country') subtag. /// /// For example: /// @@ -143,18 +146,63 @@ class Locale { /// ``` /// /// The primary language subtag must not be null. The region subtag is - /// optional. + /// 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: /// - /// The values are _case sensitive_, and should match the case of the relevant - /// subtags in the [IANA Language Subtag - /// Registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry). - /// Typically this means the primary language subtag should be lowercase and - /// the region subtag should be uppercase. - const Locale(this._languageCode, [ this._countryCode ]) : assert(_languageCode != null); + /// * [new 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](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), + assert(languageCode != ''), + _languageCode = languageCode, + assert(scriptCode != ''), + assert(countryCode != ''), + _countryCode = countryCode; /// The primary language subtag for the locale. /// - /// This must not be null. + /// 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) @@ -166,98 +214,118 @@ class Locale { /// 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`. - String get languageCode => _canonicalizeLanguageCode(_languageCode); + /// + /// 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: + /// + /// * [new Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; final String _languageCode; - static String _canonicalizeLanguageCode(String languageCode) { - // This switch statement is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2018-08-08. - switch (languageCode) { - case 'in': return 'id'; // Indonesian; deprecated 1989-01-01 - case 'iw': return 'he'; // Hebrew; deprecated 1989-01-01 - case 'ji': return 'yi'; // Yiddish; deprecated 1989-01-01 - case 'jw': return 'jv'; // Javanese; deprecated 2001-08-13 - case 'mo': return 'ro'; // Moldavian, Moldovan; deprecated 2008-11-22 - case 'aam': return 'aas'; // Aramanik; deprecated 2015-02-12 - case 'adp': return 'dz'; // Adap; deprecated 2015-02-12 - case 'aue': return 'ktz'; // =/Kx'au//'ein; deprecated 2015-02-12 - case 'ayx': return 'nun'; // Ayi (China); deprecated 2011-08-16 - case 'bgm': return 'bcg'; // Baga Mboteni; deprecated 2016-05-30 - case 'bjd': return 'drl'; // Bandjigali; deprecated 2012-08-12 - case 'ccq': return 'rki'; // Chaungtha; deprecated 2012-08-12 - case 'cjr': return 'mom'; // Chorotega; deprecated 2010-03-11 - case 'cka': return 'cmr'; // Khumi Awa Chin; deprecated 2012-08-12 - case 'cmk': return 'xch'; // Chimakum; deprecated 2010-03-11 - case 'coy': return 'pij'; // Coyaima; deprecated 2016-05-30 - case 'cqu': return 'quh'; // Chilean Quechua; deprecated 2016-05-30 - case 'drh': return 'khk'; // Darkhat; deprecated 2010-03-11 - case 'drw': return 'prs'; // Darwazi; deprecated 2010-03-11 - case 'gav': return 'dev'; // Gabutamon; deprecated 2010-03-11 - case 'gfx': return 'vaj'; // Mangetti Dune !Xung; deprecated 2015-02-12 - case 'ggn': return 'gvr'; // Eastern Gurung; deprecated 2016-05-30 - case 'gti': return 'nyc'; // Gbati-ri; deprecated 2015-02-12 - case 'guv': return 'duz'; // Gey; deprecated 2016-05-30 - case 'hrr': return 'jal'; // Horuru; deprecated 2012-08-12 - case 'ibi': return 'opa'; // Ibilo; deprecated 2012-08-12 - case 'ilw': return 'gal'; // Talur; deprecated 2013-09-10 - case 'jeg': return 'oyb'; // Jeng; deprecated 2017-02-23 - case 'kgc': return 'tdf'; // Kasseng; deprecated 2016-05-30 - case 'kgh': return 'kml'; // Upper Tanudan Kalinga; deprecated 2012-08-12 - case 'koj': return 'kwv'; // Sara Dunjo; deprecated 2015-02-12 - case 'krm': return 'bmf'; // Krim; deprecated 2017-02-23 - case 'ktr': return 'dtp'; // Kota Marudu Tinagas; deprecated 2016-05-30 - case 'kvs': return 'gdj'; // Kunggara; deprecated 2016-05-30 - case 'kwq': return 'yam'; // Kwak; deprecated 2015-02-12 - case 'kxe': return 'tvd'; // Kakihum; deprecated 2015-02-12 - case 'kzj': return 'dtp'; // Coastal Kadazan; deprecated 2016-05-30 - case 'kzt': return 'dtp'; // Tambunan Dusun; deprecated 2016-05-30 - case 'lii': return 'raq'; // Lingkhim; deprecated 2015-02-12 - case 'lmm': return 'rmx'; // Lamam; deprecated 2014-02-28 - case 'meg': return 'cir'; // Mea; deprecated 2013-09-10 - case 'mst': return 'mry'; // Cataelano Mandaya; deprecated 2010-03-11 - case 'mwj': return 'vaj'; // Maligo; deprecated 2015-02-12 - case 'myt': return 'mry'; // Sangab Mandaya; deprecated 2010-03-11 - case 'nad': return 'xny'; // Nijadali; deprecated 2016-05-30 - case 'ncp': return 'kdz'; // Ndaktup; deprecated 2018-03-08 - case 'nnx': return 'ngv'; // Ngong; deprecated 2015-02-12 - case 'nts': return 'pij'; // Natagaimas; deprecated 2016-05-30 - case 'oun': return 'vaj'; // !O!ung; deprecated 2015-02-12 - case 'pcr': return 'adx'; // Panang; deprecated 2013-09-10 - case 'pmc': return 'huw'; // Palumata; deprecated 2016-05-30 - case 'pmu': return 'phr'; // Mirpur Panjabi; deprecated 2015-02-12 - case 'ppa': return 'bfy'; // Pao; deprecated 2016-05-30 - case 'ppr': return 'lcq'; // Piru; deprecated 2013-09-10 - case 'pry': return 'prt'; // Pray 3; deprecated 2016-05-30 - case 'puz': return 'pub'; // Purum Naga; deprecated 2014-02-28 - case 'sca': return 'hle'; // Sansu; deprecated 2012-08-12 - case 'skk': return 'oyb'; // Sok; deprecated 2017-02-23 - case 'tdu': return 'dtp'; // Tempasuk Dusun; deprecated 2016-05-30 - case 'thc': return 'tpo'; // Tai Hang Tong; deprecated 2016-05-30 - case 'thx': return 'oyb'; // The; deprecated 2015-02-12 - case 'tie': return 'ras'; // Tingal; deprecated 2011-08-16 - case 'tkk': return 'twm'; // Takpa; deprecated 2011-08-16 - case 'tlw': return 'weo'; // South Wemale; deprecated 2012-08-12 - case 'tmp': return 'tyj'; // Tai Mène; deprecated 2016-05-30 - case 'tne': return 'kak'; // Tinoc Kallahan; deprecated 2016-05-30 - case 'tnf': return 'prs'; // Tangshewi; deprecated 2010-03-11 - case 'tsf': return 'taj'; // Southwestern Tamang; deprecated 2015-02-12 - case 'uok': return 'ema'; // Uokha; deprecated 2015-02-12 - case 'xba': return 'cax'; // Kamba (Brazil); deprecated 2016-05-30 - case 'xia': return 'acn'; // Xiandao; deprecated 2013-09-10 - case 'xkh': return 'waw'; // Karahawyana; deprecated 2016-05-30 - case 'xsj': return 'suj'; // Subi; deprecated 2015-02-12 - case 'ybd': return 'rki'; // Yangbye; deprecated 2012-08-12 - case 'yma': return 'lrr'; // Yamphe; deprecated 2012-08-12 - case 'ymt': return 'mtm'; // Mator-Taygi-Karagas; deprecated 2015-02-12 - case 'yos': return 'zom'; // Yos; deprecated 2013-09-10 - case 'yuu': return 'yug'; // Yugh; deprecated 2014-02-28 - default: return 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 = const { + '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: + /// + /// * [new Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + final String scriptCode; /// The region subtag for the locale. /// - /// This can be null. + /// 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) @@ -269,22 +337,24 @@ class Locale { /// '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`. - String get countryCode => _canonicalizeRegionCode(_countryCode); + /// + /// See also: + /// + /// * [new Locale.fromSubtags], which describes the conventions for creating + /// [Locale] objects. + String get countryCode => _deprecatedRegionSubtagMap[_countryCode] ?? _countryCode; final String _countryCode; - static String _canonicalizeRegionCode(String regionCode) { - // This switch statement is generated by //flutter/tools/gen_locale.dart - // Mappings generated for language subtag registry as of 2018-08-08. - switch (regionCode) { - case 'BU': return 'MM'; // Burma; deprecated 1989-12-05 - case 'DD': return 'DE'; // German Democratic Republic; deprecated 1990-10-30 - case 'FX': return 'FR'; // Metropolitan France; deprecated 1997-07-14 - case 'TP': return 'TL'; // East Timor; deprecated 2002-05-20 - case 'YD': return 'YE'; // Democratic Yemen; deprecated 1990-08-14 - case 'ZR': return 'CD'; // Zaire; deprecated 1997-07-14 - default: return regionCode; - } - } + // 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 = const { + '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 ==(dynamic other) { @@ -294,23 +364,32 @@ class Locale { return false; final Locale typedOther = other; return languageCode == typedOther.languageCode + && scriptCode == typedOther.scriptCode && countryCode == typedOther.countryCode; } @override - int get hashCode { - int result = 373; - result = 37 * result + languageCode.hashCode; - if (_countryCode != null) - result = 37 * result + countryCode.hashCode; - return result; - } + int get hashCode => hashValues(languageCode, scriptCode, countryCode); + + static Locale cachedLocale; + static String cachedLocaleString; @override String toString() { - if (_countryCode == null) - return languageCode; - return '${languageCode}_$countryCode'; + if (!identical(cachedLocale, this)) { + cachedLocale = this; + cachedLocaleString = _rawToString(); + } + return cachedLocaleString; + } + + String _rawToString() { + final StringBuffer out = StringBuffer(languageCode); + if (scriptCode != null) + out.write('_$scriptCode'); + if (_countryCode != null) + out.write('_$countryCode'); + return out.toString(); } } @@ -426,19 +505,39 @@ class Window { _onMetricsChangedZone = Zone.current; } - /// The system-reported locale. + /// 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` 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; + } + + /// 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. - Locale get locale => _locale; - Locale _locale; + List get locales => _locales; + List _locales; /// A callback that is invoked whenever [locale] changes value. /// @@ -457,6 +556,22 @@ class Window { _onLocaleChangedZone = Zone.current; } + /// 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; + } + 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; + /// The system-reported text scale. /// /// This establishes the text scaling factor to use when rendering text, @@ -496,6 +611,28 @@ class Window { _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 => _platformBrightness; + Brightness _platformBrightness = Brightness.light; + + /// 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; + set onPlatformBrightnessChanged(VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + /// 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 @@ -670,7 +807,7 @@ class Window { AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures; AccessibilityFeatures _accessibilityFeatures; - /// A callback that is invoked when the value of [accessibilityFlags] changes. + /// 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. @@ -684,13 +821,23 @@ class Window { /// Change the retained semantics data about this window. /// - /// If [semanticsEnabled] is true, the user has requested that this funciton + /// 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 'Window_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 'Window_setIsolateDebugName'; + /// Sends a message to a platform-specific plugin. /// /// The `name` parameter determines which plugin receives the message. The @@ -757,6 +904,9 @@ class Window { /// It is not possible to enable these settings from Flutter, instead they are /// used by the platform to indicate that additional accessibility features are /// enabled. +// +// When changes are made to this class, the equivalent APIs in each of the +// embedders *must* be updated. class AccessibilityFeatures { const AccessibilityFeatures._(this._index); @@ -820,6 +970,21 @@ class AccessibilityFeatures { int get hashCode => _index.hashCode; } +/// Describes the contrast of a theme or color palette. +enum Brightness { + /// The color is dark and will require a light text color to achieve readable + /// contrast. + /// + /// For example, the color might be dark grey, requiring white text. + dark, + + /// The color is light and will require a dark text color to achieve readable + /// contrast. + /// + /// For example, the color might be bright white, requiring black text. + light, +} + /// The [Window] singleton. This object exposes the size of the display, the /// core scheduler API, the input event callback, the graphics drawing API, and /// other such core services. diff --git a/lib/ui/window/platform_message.cc b/lib/ui/window/platform_message.cc index ca9d92c74e827..6a8c754d2aa83 100644 --- a/lib/ui/window/platform_message.cc +++ b/lib/ui/window/platform_message.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/platform_message.h b/lib/ui/window/platform_message.h index 6044c94e7c43d..ef26d621600ea 100644 --- a/lib/ui/window/platform_message.h +++ b/lib/ui/window/platform_message.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/platform_message_response.cc b/lib/ui/window/platform_message_response.cc index 4b835efdbe2ad..31f02ca286384 100644 --- a/lib/ui/window/platform_message_response.cc +++ b/lib/ui/window/platform_message_response.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/platform_message_response.h b/lib/ui/window/platform_message_response.h index c31779c24bc08..decd17a46beea 100644 --- a/lib/ui/window/platform_message_response.h +++ b/lib/ui/window/platform_message_response.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/platform_message_response_dart.cc b/lib/ui/window/platform_message_response_dart.cc index e91bcfd4247e4..3a4084958b835 100644 --- a/lib/ui/window/platform_message_response_dart.cc +++ b/lib/ui/window/platform_message_response_dart.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/platform_message_response_dart.h b/lib/ui/window/platform_message_response_dart.h index 274b1e08d91b7..3ff0333937096 100644 --- a/lib/ui/window/platform_message_response_dart.h +++ b/lib/ui/window/platform_message_response_dart.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/pointer_data.cc b/lib/ui/window/pointer_data.cc index c85a1a50b8e72..d52bda18c3f58 100644 --- a/lib/ui/window/pointer_data.cc +++ b/lib/ui/window/pointer_data.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,7 +9,7 @@ namespace blink { // If this value changes, update the pointer data unpacking code in hooks.dart. -static constexpr int kPointerDataFieldCount = 19; +static constexpr int kPointerDataFieldCount = 24; static_assert(sizeof(PointerData) == sizeof(int64_t) * kPointerDataFieldCount, "PointerData has the wrong size"); diff --git a/lib/ui/window/pointer_data.h b/lib/ui/window/pointer_data.h index 6a23c3d689d2e..f58c377044d3d 100644 --- a/lib/ui/window/pointer_data.h +++ b/lib/ui/window/pointer_data.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -30,9 +30,16 @@ struct alignas(8) PointerData { kInvertedStylus, }; + // Must match the PointerSignalKind enum in pointer.dart. + enum class SignalKind : int64_t { + kNone, + kScroll, + }; + int64_t time_stamp; Change change; DeviceKind kind; + SignalKind signal_kind; int64_t device; double physical_x; double physical_y; @@ -43,12 +50,16 @@ struct alignas(8) PointerData { double pressure_max; double distance; double distance_max; + double size; double radius_major; double radius_minor; double radius_min; double radius_max; double orientation; double tilt; + int64_t platformData; + double scroll_delta_x; + double scroll_delta_y; void Clear(); }; diff --git a/lib/ui/window/pointer_data_packet.cc b/lib/ui/window/pointer_data_packet.cc index b276f92fcef78..f560df1a2b7d6 100644 --- a/lib/ui/window/pointer_data_packet.cc +++ b/lib/ui/window/pointer_data_packet.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/pointer_data_packet.h b/lib/ui/window/pointer_data_packet.h index fafa03d7a7d86..c9f54dec7a677 100644 --- a/lib/ui/window/pointer_data_packet.h +++ b/lib/ui/window/pointer_data_packet.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc new file mode 100644 index 0000000000000..1047b3e4da1cf --- /dev/null +++ b/lib/ui/window/viewport_metrics.cc @@ -0,0 +1,35 @@ +// Copyright 2013 The Flutter 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/viewport_metrics.h" + +namespace blink { +ViewportMetrics::ViewportMetrics() = default; + +ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left) + : device_pixel_ratio(p_device_pixel_ratio), + physical_width(p_physical_width), + physical_height(p_physical_height), + physical_padding_top(p_physical_padding_top), + physical_padding_right(p_physical_padding_right), + physical_padding_bottom(p_physical_padding_bottom), + physical_padding_left(p_physical_padding_left), + physical_view_inset_top(p_physical_view_inset_top), + physical_view_inset_right(p_physical_view_inset_right), + physical_view_inset_bottom(p_physical_view_inset_bottom), + physical_view_inset_left(p_physical_view_inset_left) {} + +ViewportMetrics::ViewportMetrics(const ViewportMetrics& other) = default; + +} // namespace blink diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 0ccb50ccc4374..c4daa9ebe7192 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,22 @@ namespace blink { struct ViewportMetrics { + ViewportMetrics(); + + ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left); + + ViewportMetrics(const ViewportMetrics& other); + double device_pixel_ratio = 1.0; double physical_width = 0; double physical_height = 0; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 95be0076198cc..fb53dda3f1028 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,18 +14,13 @@ #include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/typed_data/dart_byte_data.h" -using tonic::DartInvokeField; -using tonic::DartState; -using tonic::StdStringToDart; -using tonic::ToDart; - namespace blink { namespace { void DefaultRouteName(Dart_NativeArguments args) { std::string routeName = UIDartState::Current()->window()->client()->DefaultRouteName(); - Dart_SetReturnValue(args, StdStringToDart(routeName)); + Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); } void ScheduleFrame(Dart_NativeArguments args) { @@ -54,16 +49,47 @@ void UpdateSemantics(Dart_NativeArguments args) { UIDartState::Current()->window()->client()->UpdateSemantics(update); } +void SetIsolateDebugName(Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + const std::string name = + tonic::DartConverter::FromArguments(args, 1, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + UIDartState::Current()->SetDebugName(name); +} + +void ReportUnhandledException(Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + + auto error_name = + tonic::DartConverter::FromArguments(args, 0, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + + auto stack_trace = + tonic::DartConverter::FromArguments(args, 1, exception); + if (exception) { + Dart_ThrowException(exception); + return; + } + + UIDartState::Current()->ReportUnhandledException(std::move(error_name), + std::move(stack_trace)); +} + Dart_Handle SendPlatformMessage(Dart_Handle window, const std::string& name, Dart_Handle callback, - const tonic::DartByteData& data) { + Dart_Handle data_handle) { UIDartState* dart_state = UIDartState::Current(); if (!dart_state->window()) { - // Must release the TypedData buffer before allocating other Dart objects. - data.Release(); - return ToDart("Platform messages can only be sent from the main isolate"); + return tonic::ToDart( + "Platform messages can only be sent from the main isolate"); } fml::RefPtr response; @@ -72,12 +98,12 @@ Dart_Handle SendPlatformMessage(Dart_Handle window, tonic::DartPersistentValue(dart_state, callback), dart_state->GetTaskRunners().GetUITaskRunner()); } - if (Dart_IsNull(data.dart_handle())) { + if (Dart_IsNull(data_handle)) { dart_state->window()->client()->HandlePlatformMessage( fml::MakeRefCounted(name, response)); } else { + tonic::DartByteData data(data_handle); const uint8_t* buffer = static_cast(data.data()); - dart_state->window()->client()->HandlePlatformMessage( fml::MakeRefCounted( name, std::vector(buffer, buffer + data.length_in_bytes()), @@ -136,7 +162,8 @@ Window::Window(WindowClient* client) : client_(client) {} Window::~Window() {} void Window::DidCreateIsolate() { - library_.Set(DartState::Current(), Dart_LookupLibrary(ToDart("dart:ui"))); + library_.Set(tonic::DartState::Current(), + Dart_LookupLibrary(tonic::ToDart("dart:ui"))); } void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { @@ -146,38 +173,33 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - DartInvokeField(library_.value(), "_updateWindowMetrics", - { - ToDart(metrics.device_pixel_ratio), - ToDart(metrics.physical_width), - ToDart(metrics.physical_height), - ToDart(metrics.physical_padding_top), - ToDart(metrics.physical_padding_right), - ToDart(metrics.physical_padding_bottom), - ToDart(metrics.physical_padding_left), - ToDart(metrics.physical_view_inset_top), - ToDart(metrics.physical_view_inset_right), - ToDart(metrics.physical_view_inset_bottom), - ToDart(metrics.physical_view_inset_left), - }); + 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), + })); } -void Window::UpdateLocale(const std::string& language_code, - const std::string& country_code, - const std::string& script_code, - const std::string& variant_code) { +void Window::UpdateLocales(const std::vector& locales) { std::shared_ptr dart_state = library_.dart_state().lock(); if (!dart_state) return; tonic::DartState::Scope scope(dart_state); - - DartInvokeField(library_.value(), "_updateLocale", - { - StdStringToDart(language_code), - StdStringToDart(country_code), - StdStringToDart(script_code), - StdStringToDart(variant_code), - }); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updateLocales", + { + tonic::ToDart>(locales), + })); } void Window::UpdateUserSettingsData(const std::string& data) { @@ -186,10 +208,23 @@ void Window::UpdateUserSettingsData(const std::string& data) { return; tonic::DartState::Scope scope(dart_state); - DartInvokeField(library_.value(), "_updateUserSettingsData", - { - StdStringToDart(data), - }); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateUserSettingsData", + { + tonic::StdStringToDart(data), + })); +} + +void Window::UpdateLifecycleState(const std::string& data) { + std::shared_ptr dart_state = library_.dart_state().lock(); + if (!dart_state) + return; + tonic::DartState::Scope scope(dart_state); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateLifecycleState", + { + tonic::StdStringToDart(data), + })); } void Window::UpdateSemanticsEnabled(bool enabled) { @@ -198,8 +233,8 @@ void Window::UpdateSemanticsEnabled(bool enabled) { return; tonic::DartState::Scope scope(dart_state); - DartInvokeField(library_.value(), "_updateSemanticsEnabled", - {ToDart(enabled)}); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_updateSemanticsEnabled", {tonic::ToDart(enabled)})); } void Window::UpdateAccessibilityFeatures(int32_t values) { @@ -208,8 +243,9 @@ void Window::UpdateAccessibilityFeatures(int32_t values) { return; tonic::DartState::Scope scope(dart_state); - DartInvokeField(library_.value(), "_updateAccessibilityFeatures", - {ToDart(values)}); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), + "_updateAccessibilityFeatures", + {tonic::ToDart(values)})); } void Window::DispatchPlatformMessage(fml::RefPtr message) { @@ -228,9 +264,10 @@ void Window::DispatchPlatformMessage(fml::RefPtr message) { pending_responses_[response_id] = response; } - DartInvokeField( - library_.value(), "_dispatchPlatformMessage", - {ToDart(message->channel()), data_handle, ToDart(response_id)}); + tonic::LogIfError( + tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage", + {tonic::ToDart(message->channel()), data_handle, + tonic::ToDart(response_id)})); } void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { @@ -242,8 +279,8 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { Dart_Handle data_handle = ToByteData(packet.data()); if (Dart_IsError(data_handle)) return; - DartInvokeField(library_.value(), "_dispatchPointerDataPacket", - {data_handle}); + tonic::LogIfError(tonic::DartInvokeField( + library_.value(), "_dispatchPointerDataPacket", {data_handle})); } void Window::DispatchSemanticsAction(int32_t id, @@ -259,9 +296,10 @@ void Window::DispatchSemanticsAction(int32_t id, if (Dart_IsError(args_handle)) return; - DartInvokeField( + tonic::LogIfError(tonic::DartInvokeField( library_.value(), "_dispatchSemanticsAction", - {ToDart(id), ToDart(static_cast(action)), args_handle}); + {tonic::ToDart(id), tonic::ToDart(static_cast(action)), + args_handle})); } void Window::BeginFrame(fml::TimePoint frameTime) { @@ -272,14 +310,14 @@ void Window::BeginFrame(fml::TimePoint frameTime) { int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); - DartInvokeField(library_.value(), "_beginFrame", - { - Dart_NewInteger(microseconds), - }); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame", + { + Dart_NewInteger(microseconds), + })); UIDartState::Current()->FlushMicrotasksNow(); - DartInvokeField(library_.value(), "_drawFrame", {}); + tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {})); } void Window::CompletePlatformMessageEmptyResponse(int response_id) { @@ -313,6 +351,8 @@ void Window::RegisterNatives(tonic::DartLibraryNatives* natives) { {"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true}, {"Window_render", Render, 2, true}, {"Window_updateSemantics", UpdateSemantics, 2, true}, + {"Window_setIsolateDebugName", SetIsolateDebugName, 2, true}, + {"Window_reportUnhandledException", ReportUnhandledException, 2, true}, }); } diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index eb2a8c6613d07..f5624e4bad6af 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -1,11 +1,13 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_WINDOW_H_ #define FLUTTER_LIB_UI_WINDOW_WINDOW_H_ +#include #include +#include #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/semantics/semantics_update.h" @@ -42,6 +44,8 @@ class WindowClient { virtual void UpdateSemantics(SemanticsUpdate* update) = 0; virtual void HandlePlatformMessage(fml::RefPtr message) = 0; virtual FontCollection& GetFontCollection() = 0; + virtual void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) = 0; protected: virtual ~WindowClient(); @@ -59,11 +63,9 @@ class Window final { void DidCreateIsolate(); void UpdateWindowMetrics(const ViewportMetrics& metrics); - void UpdateLocale(const std::string& language_code, - const std::string& country_code, - const std::string& script_code, - const std::string& variant_code); + void UpdateLocales(const std::vector& locales); void UpdateUserSettingsData(const std::string& data); + void UpdateLifecycleState(const std::string& data); void UpdateSemanticsEnabled(bool enabled); void UpdateAccessibilityFeatures(int32_t flags); void DispatchPlatformMessage(fml::RefPtr message); diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 1fa0d73af64e2..401b107ad2adf 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -93,6 +93,7 @@ executable("runtime_unittests") { deps = [ ":runtime", ":runtime_fixtures", + "$flutter_root/common", "$flutter_root/fml", "$flutter_root/lib/snapshot", "$flutter_root/testing", diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index db01e619cba32..8d53f3b13ea62 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,15 +21,11 @@ #include "third_party/tonic/dart_class_provider.h" #include "third_party/tonic/dart_message_handler.h" #include "third_party/tonic/dart_state.h" -#include "third_party/tonic/dart_sticky_error.h" #include "third_party/tonic/file_loader/file_loader.h" +#include "third_party/tonic/logging/dart_invoke.h" #include "third_party/tonic/scopes/dart_api_scope.h" #include "third_party/tonic/scopes/dart_isolate_scope.h" -#ifdef ERROR -#undef ERROR -#endif - namespace blink { std::weak_ptr DartIsolate::CreateRootIsolate( @@ -38,8 +34,8 @@ std::weak_ptr DartIsolate::CreateRootIsolate( fml::RefPtr shared_snapshot, TaskRunners task_runners, std::unique_ptr window, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, Dart_IsolateFlags* flags) { @@ -54,14 +50,14 @@ std::weak_ptr DartIsolate::CreateRootIsolate( // isolate lifecycle is entirely managed by the VM). auto root_embedder_data = std::make_unique>( std::make_shared( - vm, // VM - std::move(isolate_snapshot), // isolate snapshot - std::move(shared_snapshot), // shared snapshot - task_runners, // task runners - std::move(resource_context), // resource context - std::move(unref_queue), // skia unref queue - advisory_script_uri, // advisory URI - advisory_script_entrypoint, // advisory entrypoint + vm, // VM + std::move(isolate_snapshot), // isolate snapshot + std::move(shared_snapshot), // shared snapshot + task_runners, // task runners + std::move(snapshot_delegate), // snapshot delegate + std::move(io_manager), // IO manager + advisory_script_uri, // advisory URI + advisory_script_entrypoint, // advisory entrypoint nullptr // child isolate preparer will be set when this isolate is // prepared to run )); @@ -101,19 +97,20 @@ DartIsolate::DartIsolate(DartVM* vm, fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, TaskRunners task_runners, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, ChildIsolatePreparer child_isolate_preparer) : UIDartState(std::move(task_runners), vm->GetSettings().task_observer_add, vm->GetSettings().task_observer_remove, - std::move(resource_context), - std::move(unref_queue), + std::move(snapshot_delegate), + std::move(io_manager), advisory_script_uri, advisory_script_entrypoint, vm->GetSettings().log_tag, + vm->GetSettings().unhandled_exception_callback, vm->GetIsolateNameServer()), vm_(vm), isolate_snapshot_(std::move(isolate_snapshot)), @@ -152,7 +149,7 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { return false; } - auto isolate_data = static_cast*>( + auto* isolate_data = static_cast*>( Dart_IsolateData(dart_isolate)); if (isolate_data->get() != this) { return false; @@ -348,9 +345,22 @@ bool DartIsolate::PrepareForRunningFromKernel( return false; } - child_isolate_preparer_ = [mapping](DartIsolate* isolate) { - return isolate->PrepareForRunningFromKernel(mapping); - }; + // Child isolate shares root isolate embedder_isolate (lines 691 and 693 + // below). Re-initializing child_isolate_preparer_ lambda while it is being + // executed leads to crashes. + if (child_isolate_preparer_ == nullptr) { + child_isolate_preparer_ = [buffers = + kernel_buffers_](DartIsolate* isolate) { + for (unsigned long i = 0; i < buffers.size(); i++) { + bool last_piece = i + 1 == buffers.size(); + const std::shared_ptr& buffer = buffers.at(i); + if (!isolate->PrepareForRunningFromKernel(buffer, last_piece)) { + return false; + } + } + return true; + }; + } phase_ = Phase::Ready; return true; } @@ -383,33 +393,44 @@ bool DartIsolate::MarkIsolateRunnable() { } FML_WARN_UNUSED_RESULT -bool DartIsolate::Run(const std::string& entrypoint_name) { - TRACE_EVENT0("flutter", "DartIsolate::Run"); - if (phase_ != Phase::Ready) { +static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function) { + if (tonic::LogIfError(user_entrypoint_function)) { + FML_LOG(ERROR) << "Could not resolve main entrypoint function."; return false; } - tonic::DartState::Scope scope(this); + Dart_Handle start_main_isolate_function = + tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")), + "_getStartMainIsolateFunction", {}); - Dart_Handle entrypoint = - Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str())); - if (tonic::LogIfError(entrypoint)) { + if (tonic::LogIfError(start_main_isolate_function)) { + FML_LOG(ERROR) << "Could not resolve main entrypoint trampoline."; return false; } - Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); - if (tonic::LogIfError(isolate_lib)) { + if (tonic::LogIfError(tonic::DartInvokeField( + Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned", + {start_main_isolate_function, user_entrypoint_function}))) { + FML_LOG(ERROR) << "Could not invoke the main entrypoint."; return false; } - Dart_Handle isolate_args[] = { - entrypoint, - Dart_Null(), - }; + return true; +} - if (tonic::LogIfError(Dart_Invoke( - isolate_lib, tonic::ToDart("_startMainIsolate"), - sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) { +FML_WARN_UNUSED_RESULT +bool DartIsolate::Run(const std::string& entrypoint_name) { + TRACE_EVENT0("flutter", "DartIsolate::Run"); + if (phase_ != Phase::Ready) { + return false; + } + + tonic::DartState::Scope scope(this); + + auto user_entrypoint_function = + Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str())); + + if (!InvokeMainEntrypoint(user_entrypoint_function)) { return false; } @@ -428,30 +449,11 @@ bool DartIsolate::RunFromLibrary(const std::string& library_name, tonic::DartState::Scope scope(this); - Dart_Handle library = Dart_LookupLibrary(tonic::ToDart(library_name.c_str())); - if (tonic::LogIfError(library)) { - return false; - } - - Dart_Handle entrypoint = - Dart_GetField(library, tonic::ToDart(entrypoint_name.c_str())); - if (tonic::LogIfError(entrypoint)) { - return false; - } + auto user_entrypoint_function = + Dart_GetField(Dart_LookupLibrary(tonic::ToDart(library_name.c_str())), + tonic::ToDart(entrypoint_name.c_str())); - Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); - if (tonic::LogIfError(isolate_lib)) { - return false; - } - - Dart_Handle isolate_args[] = { - entrypoint, - Dart_Null(), - }; - - if (tonic::LogIfError(Dart_Invoke( - isolate_lib, tonic::ToDart("_startMainIsolate"), - sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) { + if (!InvokeMainEntrypoint(user_entrypoint_function)) { return false; } @@ -477,6 +479,7 @@ bool DartIsolate::Shutdown() { // the isolate to shutdown as a parameter. FML_DCHECK(Dart_CurrentIsolate() == nullptr); Dart_EnterIsolate(vm_isolate); + shutdown_callbacks_.clear(); Dart_ShutdownIsolate(); FML_DCHECK(Dart_CurrentIsolate() == nullptr); } @@ -484,8 +487,6 @@ bool DartIsolate::Shutdown() { } Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( - const char* advisory_script_uri, - const char* advisory_script_entrypoint, const char* package_root, const char* package_config, Dart_IsolateFlags* flags, @@ -514,19 +515,16 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( std::weak_ptr weak_service_isolate = DartIsolate::CreateRootIsolate( - vm.get(), // vm - vm->GetIsolateSnapshot(), // isolate snapshot - vm->GetSharedSnapshot(), // shared snapshot - null_task_runners, // task runners - nullptr, // window - {}, // resource context - {}, // unref queue - advisory_script_uri == nullptr ? "" - : advisory_script_uri, // script uri - advisory_script_entrypoint == nullptr - ? "" - : advisory_script_entrypoint, // script entrypoint - flags // flags + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + vm->GetSharedSnapshot(), // shared snapshot + null_task_runners, // task runners + nullptr, // window + {}, // snapshot delegate + {}, // IO Manager + DART_VM_SERVICE_ISOLATE_NAME, // script uri + DART_VM_SERVICE_ISOLATE_NAME, // script entrypoint + flags // flags ); std::shared_ptr service_isolate = weak_service_isolate.lock(); @@ -570,12 +568,10 @@ Dart_Isolate DartIsolate::DartIsolateCreateCallback( // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service // isolate like normal but dont hold a reference to it at all. We also start // this isolate since we will never again reference it from the engine. - return DartCreateAndStartServiceIsolate(advisory_script_uri, // - advisory_script_entrypoint, // - package_root, // - package_config, // - flags, // - error // + return DartCreateAndStartServiceIsolate(package_root, // + package_config, // + flags, // + error // ); } @@ -618,7 +614,7 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( DartVM* const vm = (*embedder_isolate)->GetDartVM(); if (!is_root_isolate) { - auto raw_embedder_isolate = embedder_isolate.release(); + auto* raw_embedder_isolate = embedder_isolate.release(); blink::TaskRunners null_task_runners(advisory_script_uri, nullptr, nullptr, nullptr, nullptr); @@ -629,8 +625,8 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( (*raw_embedder_isolate)->GetIsolateSnapshot(), // isolate_snapshot (*raw_embedder_isolate)->GetSharedSnapshot(), // shared_snapshot null_task_runners, // task_runners - fml::WeakPtr{}, // resource_context - nullptr, // unref_queue + fml::WeakPtr{}, // snapshot_delegate + fml::WeakPtr{}, // io_manager advisory_script_uri, // advisory_script_uri advisory_script_entrypoint, // advisory_script_entrypoint (*raw_embedder_isolate)->child_isolate_preparer_)); @@ -690,19 +686,7 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( // |Dart_IsolateShutdownCallback| void DartIsolate::DartIsolateShutdownCallback( - std::shared_ptr* embedder_isolate) { - if (!tonic::DartStickyError::IsSet()) { - return; - } - - tonic::DartApiScope api_scope; - Dart_Handle sticky_error = Dart_GetStickyError(); - if (!Dart_IsFatalError(sticky_error)) { - FML_LOG(ERROR) << "Isolate " << tonic::StdStringFromDart(Dart_DebugName()) - << " exited with an error"; - FML_CHECK(tonic::LogIfError(sticky_error)); - } -} + std::shared_ptr* embedder_isolate) {} // |Dart_IsolateCleanupCallback| void DartIsolate::DartIsolateCleanupCallback( @@ -727,4 +711,13 @@ void DartIsolate::AddIsolateShutdownCallback(fml::closure closure) { std::make_unique(std::move(closure))); } +DartIsolate::AutoFireClosure::AutoFireClosure(fml::closure closure) + : closure_(std::move(closure)) {} + +DartIsolate::AutoFireClosure::~AutoFireClosure() { + if (closure_) { + closure_(); + } +} + } // namespace blink diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index f2167364effdd..d720deafed142 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,6 +12,8 @@ #include "flutter/fml/compiler_specific.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "flutter/lib/ui/io_manager.h" +#include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/window.h" #include "flutter/runtime/dart_snapshot.h" @@ -44,8 +46,8 @@ class DartIsolate : public UIDartState { fml::RefPtr shared_snapshot, TaskRunners task_runners, std::unique_ptr window, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, Dart_IsolateFlags* flags = nullptr); @@ -54,8 +56,8 @@ class DartIsolate : public UIDartState { fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, TaskRunners task_runners, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, ChildIsolatePreparer child_isolate_preparer); @@ -86,6 +88,7 @@ class DartIsolate : public UIDartState { DartVM* GetDartVM() const; fml::RefPtr GetIsolateSnapshot() const; + fml::RefPtr GetSharedSnapshot() const; std::weak_ptr GetWeakIsolatePtr(); @@ -95,12 +98,9 @@ class DartIsolate : public UIDartState { class AutoFireClosure { public: - AutoFireClosure(fml::closure closure) : closure_(std::move(closure)) {} - ~AutoFireClosure() { - if (closure_) { - closure_(); - } - } + AutoFireClosure(fml::closure closure); + + ~AutoFireClosure(); private: fml::closure closure_; @@ -114,7 +114,7 @@ class DartIsolate : public UIDartState { const fml::RefPtr shared_snapshot_; std::vector> kernel_buffers_; std::vector> shutdown_callbacks_; - ChildIsolatePreparer child_isolate_preparer_; + ChildIsolatePreparer child_isolate_preparer_ = nullptr; FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate, bool is_root_isolate); @@ -138,8 +138,6 @@ class DartIsolate : public UIDartState { char** error); static Dart_Isolate DartCreateAndStartServiceIsolate( - const char* advisory_script_uri, - const char* advisory_script_entrypoint, const char* package_root, const char* package_config, Dart_IsolateFlags* flags, diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc index cb7797abdcb74..76de89deb061e 100644 --- a/runtime/dart_isolate_unittests.cc +++ b/runtime/dart_isolate_unittests.cc @@ -1,12 +1,21 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/fml/paths.h" #include "flutter/fml/thread.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_vm.h" #include "flutter/testing/testing.h" #include "flutter/testing/thread_test.h" +#include "third_party/tonic/scopes/dart_isolate_scope.h" + +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DEBUG +#define SKIP_IF_AOT() GTEST_SKIP() +#else +#define SKIP_IF_AOT() (void)0 +#endif #define CURRENT_TEST_NAME \ std::string { \ @@ -35,8 +44,8 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { vm->GetSharedSnapshot(), // shared snapshot std::move(task_runners), // task runners nullptr, // window - {}, // resource context - nullptr, // unref qeueue + {}, // snapshot delegate + {}, // io manager "main.dart", // advisory uri "main" // advisory entrypoint ); @@ -46,4 +55,196 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { ASSERT_TRUE(root_isolate->Shutdown()); } +TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fml::closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto weak_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + vm->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint + ); + auto root_isolate = weak_isolate.lock(); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + size_t destruction_callback_count = 0; + root_isolate->AddIsolateShutdownCallback([&destruction_callback_count]() { + ASSERT_NE(Dart_CurrentIsolate(), nullptr); + destruction_callback_count++; + }); + ASSERT_TRUE(root_isolate->Shutdown()); + ASSERT_EQ(destruction_callback_count, 1u); +} + +class AutoIsolateShutdown { + public: + AutoIsolateShutdown(std::shared_ptr isolate) + : isolate_(std::move(isolate)) {} + + ~AutoIsolateShutdown() { + if (isolate_) { + FML_LOG(INFO) << "Shutting down isolate."; + if (!isolate_->Shutdown()) { + FML_LOG(ERROR) << "Could not shutdown isolate."; + } + } + } + + bool IsValid() const { return isolate_ != nullptr; } + + FML_WARN_UNUSED_RESULT + bool RunInIsolateScope(std::function closure) { + if (!isolate_) { + return false; + } + tonic::DartIsolateScope scope(isolate_->isolate()); + tonic::DartApiScope api_scope; + if (closure) { + return closure(); + } + return true; + } + + blink::DartIsolate* get() { + FML_CHECK(isolate_); + return isolate_.get(); + } + + private: + std::shared_ptr isolate_; + + FML_DISALLOW_COPY_AND_ASSIGN(AutoIsolateShutdown); +}; + +std::unique_ptr RunDartCodeInIsolate( + fml::RefPtr task_runner, + std::string entrypoint) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fml::closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + + auto vm = DartVM::ForProcess(settings); + + if (!vm) { + return {}; + } + TaskRunners task_runners(CURRENT_TEST_NAME, // + task_runner, // + task_runner, // + task_runner, // + task_runner // + ); + + auto weak_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + vm->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint + ); + + auto root_isolate = + std::make_unique(weak_isolate.lock()); + + if (!root_isolate->IsValid()) { + FML_LOG(ERROR) << "Could not create isolate."; + return {}; + } + + if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) { + FML_LOG(ERROR) << "Created isolate is in unexpected phase."; + return {}; + } + + auto kernel_file_path = + fml::paths::JoinPaths({testing::GetFixturesPath(), "kernel_blob.bin"}); + + if (!fml::IsFile(kernel_file_path)) { + FML_LOG(ERROR) << "Could not locate kernel file."; + return {}; + } + + auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false, + fml::FilePermission::kRead); + + if (!kernel_file.is_valid()) { + FML_LOG(ERROR) << "Kernel file descriptor was invalid."; + return {}; + } + + auto kernel_mapping = std::make_unique(kernel_file); + + if (kernel_mapping->GetMapping() == nullptr) { + FML_LOG(ERROR) << "Could not setup kernel mapping."; + return {}; + } + + if (!root_isolate->get()->PrepareForRunningFromKernel( + std::move(kernel_mapping))) { + FML_LOG(ERROR) + << "Could not prepare to run the isolate from the kernel file."; + return {}; + } + + if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) { + FML_LOG(ERROR) << "Isolate is in unexpected phase."; + return {}; + } + + if (!root_isolate->get()->Run(entrypoint)) { + FML_LOG(ERROR) << "Could not run the method \"" << entrypoint + << "\" in the isolate."; + return {}; + } + + return root_isolate; +} + +TEST_F(DartIsolateTest, IsolateCanLoadAndRunDartCode) { + SKIP_IF_AOT(); + auto isolate = RunDartCodeInIsolate(GetCurrentTaskRunner(), "main"); + ASSERT_TRUE(isolate); + ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); +} + +TEST_F(DartIsolateTest, IsolateCannotLoadAndRunUnknownDartEntrypoint) { + SKIP_IF_AOT(); + auto isolate = + RunDartCodeInIsolate(GetCurrentTaskRunner(), "thisShouldNotExist"); + ASSERT_FALSE(isolate); +} + +TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) { + SKIP_IF_AOT(); + auto isolate = RunDartCodeInIsolate(GetCurrentTaskRunner(), "main"); + + ASSERT_TRUE(isolate); + + ASSERT_TRUE(isolate->RunInIsolateScope([]() -> bool { + if (tonic::LogIfError(::Dart_Invoke(Dart_RootLibrary(), + tonic::ToDart("sayHi"), 0, nullptr))) { + return false; + } + return true; + })); +} + } // namespace blink diff --git a/runtime/dart_service_isolate.cc b/runtime/dart_service_isolate.cc index 7ffed425cad27..2bedd79f81716 100644 --- a/runtime/dart_service_isolate.cc +++ b/runtime/dart_service_isolate.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/dart_service_isolate.h b/runtime/dart_service_isolate.h index a65b274a05698..cf4137a1e6b49 100644 --- a/runtime/dart_service_isolate.h +++ b/runtime/dart_service_isolate.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/dart_service_isolate_unittests.cc b/runtime/dart_service_isolate_unittests.cc index f2823689f6723..b0e2baf0b62af 100644 --- a/runtime/dart_service_isolate_unittests.cc +++ b/runtime/dart_service_isolate_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/dart_snapshot.cc b/runtime/dart_snapshot.cc index 19d0821d92ae2..a752dd0818379 100644 --- a/runtime/dart_snapshot.cc +++ b/runtime/dart_snapshot.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -41,6 +41,10 @@ static const char* kIsolateInstructionsSymbolSo = SYMBOL_PREFIX "kDartIsolateSnapshotInstructions"; std::unique_ptr ResolveVMData(const Settings& settings) { + if (settings.vm_snapshot_data) { + return DartSnapshotBuffer::CreateWithMapping(settings.vm_snapshot_data()); + } + if (settings.vm_snapshot_data_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( fml::OpenFile(settings.vm_snapshot_data_path.c_str(), false, @@ -66,6 +70,10 @@ std::unique_ptr ResolveVMData(const Settings& settings) { std::unique_ptr ResolveVMInstructions( const Settings& settings) { + if (settings.vm_snapshot_instr) { + return DartSnapshotBuffer::CreateWithMapping(settings.vm_snapshot_instr()); + } + if (settings.vm_snapshot_instr_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( fml::OpenFile(settings.vm_snapshot_instr_path.c_str(), false, @@ -91,6 +99,11 @@ std::unique_ptr ResolveVMInstructions( std::unique_ptr ResolveIsolateData( const Settings& settings) { + if (settings.isolate_snapshot_data) { + return DartSnapshotBuffer::CreateWithMapping( + settings.isolate_snapshot_data()); + } + if (settings.isolate_snapshot_data_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( fml::OpenFile(settings.isolate_snapshot_data_path.c_str(), false, @@ -116,6 +129,11 @@ std::unique_ptr ResolveIsolateData( std::unique_ptr ResolveIsolateInstructions( const Settings& settings) { + if (settings.isolate_snapshot_data) { + return DartSnapshotBuffer::CreateWithMapping( + settings.isolate_snapshot_instr()); + } + if (settings.isolate_snapshot_instr_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( fml::OpenFile(settings.isolate_snapshot_instr_path.c_str(), false, diff --git a/runtime/dart_snapshot.h b/runtime/dart_snapshot.h index c8aa17c3140ab..19d2968833f90 100644 --- a/runtime/dart_snapshot.h +++ b/runtime/dart_snapshot.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/dart_snapshot_buffer.cc b/runtime/dart_snapshot_buffer.cc index 342613f185c96..c697e28f47c4a 100644 --- a/runtime/dart_snapshot_buffer.cc +++ b/runtime/dart_snapshot_buffer.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,26 +31,23 @@ class NativeLibrarySnapshotBuffer final : public DartSnapshotBuffer { FML_DISALLOW_COPY_AND_ASSIGN(NativeLibrarySnapshotBuffer); }; -class FileSnapshotBuffer final : public DartSnapshotBuffer { +class MappingBuffer final : public DartSnapshotBuffer { public: - FileSnapshotBuffer( - const fml::UniqueFD& fd, - std::initializer_list protection) - : mapping_(fd, protection) { - if (mapping_.GetSize() > 0) { - symbol_ = mapping_.GetMapping(); - } + MappingBuffer(std::unique_ptr mapping) + : mapping_(std::move(mapping)) { + FML_DCHECK(mapping_); } - const uint8_t* GetSnapshotPointer() const override { return symbol_; } + const uint8_t* GetSnapshotPointer() const override { + return mapping_->GetMapping(); + } - size_t GetSnapshotSize() const override { return mapping_.GetSize(); } + size_t GetSnapshotSize() const override { return mapping_->GetSize(); } private: - fml::FileMapping mapping_; - const uint8_t* symbol_ = nullptr; + std::unique_ptr mapping_; - FML_DISALLOW_COPY_AND_ASSIGN(FileSnapshotBuffer); + FML_DISALLOW_COPY_AND_ASSIGN(MappingBuffer); }; class UnmanagedAllocation final : public DartSnapshotBuffer { @@ -80,8 +77,16 @@ std::unique_ptr DartSnapshotBuffer::CreateWithContentsOfFile( const fml::UniqueFD& fd, std::initializer_list protection) { - auto source = std::make_unique(fd, protection); - return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); + return CreateWithMapping(std::make_unique(fd, protection)); +} + +std::unique_ptr DartSnapshotBuffer::CreateWithMapping( + std::unique_ptr mapping) { + if (mapping == nullptr || mapping->GetSize() == 0 || + mapping->GetMapping() == nullptr) { + return nullptr; + } + return std::make_unique(std::move(mapping)); } std::unique_ptr diff --git a/runtime/dart_snapshot_buffer.h b/runtime/dart_snapshot_buffer.h index 75fff29d7eaea..34fee1f411cb9 100644 --- a/runtime/dart_snapshot_buffer.h +++ b/runtime/dart_snapshot_buffer.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,6 +15,8 @@ namespace blink { +// TODO(chinmaygarde): Replace this with just |fml::Mapping|. +// https://github.com/flutter/flutter/issues/26782 class DartSnapshotBuffer { public: static std::unique_ptr CreateWithSymbolInLibrary( @@ -28,6 +30,9 @@ class DartSnapshotBuffer { static std::unique_ptr CreateWithUnmanagedAllocation( const uint8_t* allocation); + static std::unique_ptr CreateWithMapping( + std::unique_ptr mapping); + virtual ~DartSnapshotBuffer(); virtual const uint8_t* GetSnapshotPointer() const = 0; diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index 35bc542432411..b907db41bd0f5 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,16 +27,11 @@ #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_class_library.h" #include "third_party/tonic/dart_class_provider.h" -#include "third_party/tonic/dart_sticky_error.h" #include "third_party/tonic/file_loader/file_loader.h" #include "third_party/tonic/logging/dart_error.h" #include "third_party/tonic/scopes/dart_api_scope.h" #include "third_party/tonic/typed_data/uint8_list.h" -#ifdef ERROR -#undef ERROR -#endif - namespace dart { namespace observatory { @@ -95,8 +90,15 @@ static const char* kDartEndlessTraceBufferArgs[]{ "--timeline_recorder=endless", }; +static const char* kDartSystraceTraceBufferArgs[]{ + "--timeline_recorder=systrace", +}; + static const char* kDartFuchsiaTraceArgs[] FML_ALLOW_UNUSED_TYPE = { "--systrace_timeline", +}; + +static const char* kDartTraceStreamsArgs[] = { "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", }; @@ -317,7 +319,7 @@ DartVM::DartVM(const Settings& settings, // it does not recognize, it exits immediately. args.push_back("--ignore-unrecognized-flags"); - for (const auto& profiler_flag : + for (auto* const profiler_flag : ProfilingFlags(settings.enable_dart_profiling)) { args.push_back(profiler_flag); } @@ -329,18 +331,18 @@ DartVM::DartVM(const Settings& settings, arraysize(kDartPrecompilationArgs)); } - // Enable checked mode if we are not running precompiled code. We run non- + // Enable Dart assertions if we are not running precompiled code. We run non- // precompiled code only in the debug product mode. - bool use_checked_mode = !settings.dart_non_checked_mode; + bool enable_asserts = !settings.disable_dart_asserts; #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_PROFILE || \ FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE - use_checked_mode = false; + enable_asserts = false; #endif #if !OS_FUCHSIA if (IsRunningPrecompiledCode()) { - use_checked_mode = false; + enable_asserts = false; } #endif // !OS_FUCHSIA @@ -351,7 +353,7 @@ DartVM::DartVM(const Settings& settings, arraysize(kDartWriteProtectCodeArgs)); #endif - if (use_checked_mode) { + if (enable_asserts) { PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); } @@ -366,12 +368,19 @@ DartVM::DartVM(const Settings& settings, arraysize(kDartEndlessTraceBufferArgs)); } + if (settings.trace_systrace) { + PushBackAll(&args, kDartSystraceTraceBufferArgs, + arraysize(kDartSystraceTraceBufferArgs)); + PushBackAll(&args, kDartTraceStreamsArgs, arraysize(kDartTraceStreamsArgs)); + } + if (settings.trace_startup) { PushBackAll(&args, kDartTraceStartupArgs, arraysize(kDartTraceStartupArgs)); } #if defined(OS_FUCHSIA) PushBackAll(&args, kDartFuchsiaTraceArgs, arraysize(kDartFuchsiaTraceArgs)); + PushBackAll(&args, kDartTraceStreamsArgs, arraysize(kDartTraceStreamsArgs)); #endif for (size_t i = 0; i < settings.dart_flags.size(); i++) @@ -429,6 +438,14 @@ DartVM::DartVM(const Settings& settings, &ServiceStreamCancelCallback); Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); + + if (settings.dart_library_sources_kernel != nullptr) { + std::unique_ptr dart_library_sources = + settings.dart_library_sources_kernel(); + // Set sources for dart:* libraries for debugging. + Dart_SetDartLibrarySourcesKernel(dart_library_sources->GetMapping(), + dart_library_sources->GetSize()); + } } DartVM::~DartVM() { diff --git a/runtime/dart_vm.h b/runtime/dart_vm.h index b853f026c328f..9a1bde888dd86 100644 --- a/runtime/dart_vm.h +++ b/runtime/dart_vm.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/dart_vm_unittests.cc b/runtime/dart_vm_unittests.cc index 94d5dadaaec1f..21e3d3d3fa58f 100644 --- a/runtime/dart_vm_unittests.cc +++ b/runtime/dart_vm_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/embedder_resources.cc b/runtime/embedder_resources.cc index 102bdae7b3a00..2eb73880d3d08 100644 --- a/runtime/embedder_resources.cc +++ b/runtime/embedder_resources.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/embedder_resources.h b/runtime/embedder_resources.h index eb3a0b833fdbf..1ae866a54a75f 100644 --- a/runtime/embedder_resources.h +++ b/runtime/embedder_resources.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/fixtures/simple_main.dart b/runtime/fixtures/simple_main.dart index 552dfbe344902..5d6a46eb7f1cf 100644 --- a/runtime/fixtures/simple_main.dart +++ b/runtime/fixtures/simple_main.dart @@ -1,7 +1,14 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -void simple_main() { - print("Hello"); +void main() { +} + +void sayHi() { + print("Hi"); +} + +void throwExceptionNow() { + throw("Hello"); } diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 7e0bd024c66cc..83a848e87955c 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,10 +12,6 @@ #include "flutter/runtime/runtime_delegate.h" #include "third_party/tonic/dart_message_handler.h" -#ifdef ERROR -#undef ERROR -#endif - namespace blink { RuntimeController::RuntimeController( @@ -24,19 +20,21 @@ RuntimeController::RuntimeController( fml::RefPtr p_isolate_snapshot, fml::RefPtr p_shared_snapshot, TaskRunners p_task_runners, - fml::WeakPtr p_resource_context, - fml::RefPtr p_unref_queue, + fml::WeakPtr p_snapshot_delegate, + fml::WeakPtr p_io_manager, std::string p_advisory_script_uri, - std::string p_advisory_script_entrypoint) + std::string p_advisory_script_entrypoint, + std::function p_idle_notification_callback) : RuntimeController(p_client, p_vm, std::move(p_isolate_snapshot), std::move(p_shared_snapshot), std::move(p_task_runners), - std::move(p_resource_context), - std::move(p_unref_queue), + std::move(p_snapshot_delegate), + std::move(p_io_manager), std::move(p_advisory_script_uri), std::move(p_advisory_script_entrypoint), + p_idle_notification_callback, WindowData{/* default window data */}) {} RuntimeController::RuntimeController( @@ -45,20 +43,22 @@ RuntimeController::RuntimeController( fml::RefPtr p_isolate_snapshot, fml::RefPtr p_shared_snapshot, TaskRunners p_task_runners, - fml::WeakPtr p_resource_context, - fml::RefPtr p_unref_queue, + fml::WeakPtr p_snapshot_delegate, + fml::WeakPtr p_io_manager, std::string p_advisory_script_uri, std::string p_advisory_script_entrypoint, + std::function idle_notification_callback, WindowData p_window_data) : client_(p_client), vm_(p_vm), isolate_snapshot_(std::move(p_isolate_snapshot)), shared_snapshot_(std::move(p_shared_snapshot)), task_runners_(p_task_runners), - resource_context_(p_resource_context), - unref_queue_(p_unref_queue), + snapshot_delegate_(p_snapshot_delegate), + io_manager_(p_io_manager), advisory_script_uri_(p_advisory_script_uri), advisory_script_entrypoint_(p_advisory_script_entrypoint), + idle_notification_callback_(idle_notification_callback), window_data_(std::move(p_window_data)), root_isolate_( DartIsolate::CreateRootIsolate(vm_, @@ -66,15 +66,15 @@ RuntimeController::RuntimeController( shared_snapshot_, task_runners_, std::make_unique(this), - resource_context_, - unref_queue_, + snapshot_delegate_, + io_manager_, p_advisory_script_uri, p_advisory_script_entrypoint)) { std::shared_ptr root_isolate = root_isolate_.lock(); root_isolate->SetReturnCodeCallback([this](uint32_t code) { root_isolate_return_code_ = {true, code}; }); - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { tonic::DartState::Scope scope(root_isolate); window->DidCreateIsolate(); if (!FlushRuntimeStateToIsolate()) { @@ -114,44 +114,40 @@ std::unique_ptr RuntimeController::Clone() const { isolate_snapshot_, // shared_snapshot_, // task_runners_, // - resource_context_, // - unref_queue_, // + snapshot_delegate_, // + io_manager_, // advisory_script_uri_, // advisory_script_entrypoint_, // + idle_notification_callback_, // window_data_ // )); } bool RuntimeController::FlushRuntimeStateToIsolate() { return SetViewportMetrics(window_data_.viewport_metrics) && - SetLocale(window_data_.language_code, window_data_.country_code, - window_data_.script_code, window_data_.variant_code) && + SetLocales(window_data_.locale_data) && SetSemanticsEnabled(window_data_.semantics_enabled) && - SetAccessibilityFeatures(window_data_.accessibility_feature_flags_); + SetAccessibilityFeatures(window_data_.accessibility_feature_flags_) && + SetUserSettingsData(window_data_.user_settings_data) && + SetLifecycleState(window_data_.lifecycle_state); } bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { window_data_.viewport_metrics = metrics; - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->UpdateWindowMetrics(metrics); return true; } return false; } -bool RuntimeController::SetLocale(const std::string& language_code, - const std::string& country_code, - const std::string& script_code, - const std::string& variant_code) { - window_data_.language_code = language_code; - window_data_.country_code = country_code; - window_data_.script_code = script_code; - window_data_.variant_code = variant_code; - - if (auto window = GetWindowIfAvailable()) { - window->UpdateLocale(window_data_.language_code, window_data_.country_code, - window_data_.script_code, window_data_.variant_code); +bool RuntimeController::SetLocales( + const std::vector& locale_data) { + window_data_.locale_data = locale_data; + + if (auto* window = GetWindowIfAvailable()) { + window->UpdateLocales(locale_data); return true; } @@ -161,7 +157,7 @@ bool RuntimeController::SetLocale(const std::string& language_code, bool RuntimeController::SetUserSettingsData(const std::string& data) { window_data_.user_settings_data = data; - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->UpdateUserSettingsData(window_data_.user_settings_data); return true; } @@ -169,10 +165,21 @@ bool RuntimeController::SetUserSettingsData(const std::string& data) { return false; } +bool RuntimeController::SetLifecycleState(const std::string& data) { + window_data_.lifecycle_state = data; + + if (auto* window = GetWindowIfAvailable()) { + window->UpdateLifecycleState(window_data_.lifecycle_state); + return true; + } + + return false; +} + bool RuntimeController::SetSemanticsEnabled(bool enabled) { window_data_.semantics_enabled = enabled; - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->UpdateSemanticsEnabled(window_data_.semantics_enabled); return true; } @@ -182,7 +189,7 @@ bool RuntimeController::SetSemanticsEnabled(bool enabled) { bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { window_data_.accessibility_feature_flags_ = flags; - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->UpdateAccessibilityFeatures( window_data_.accessibility_feature_flags_); return true; @@ -192,7 +199,7 @@ bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { } bool RuntimeController::BeginFrame(fml::TimePoint frame_time) { - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->BeginFrame(frame_time); return true; } @@ -206,13 +213,20 @@ bool RuntimeController::NotifyIdle(int64_t deadline) { } tonic::DartState::Scope scope(root_isolate); + Dart_NotifyIdle(deadline); + + // Idle notifications being in isolate scope are part of the contract. + if (idle_notification_callback_) { + TRACE_EVENT0("flutter", "EmbedderIdleNotification"); + idle_notification_callback_(deadline); + } return true; } bool RuntimeController::DispatchPlatformMessage( fml::RefPtr message) { - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", "mode", "basic"); window->DispatchPlatformMessage(std::move(message)); @@ -223,7 +237,7 @@ bool RuntimeController::DispatchPlatformMessage( bool RuntimeController::DispatchPointerDataPacket( const PointerDataPacket& packet) { - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", "mode", "basic"); window->DispatchPointerDataPacket(packet); @@ -237,7 +251,7 @@ bool RuntimeController::DispatchSemanticsAction(int32_t id, std::vector args) { TRACE_EVENT1("flutter", "RuntimeController::DispatchSemanticsAction", "mode", "basic"); - if (auto window = GetWindowIfAvailable()) { + if (auto* window = GetWindowIfAvailable()) { window->DispatchSemanticsAction(id, action, std::move(args)); return true; } @@ -276,6 +290,11 @@ FontCollection& RuntimeController::GetFontCollection() { return client_.GetFontCollection(); } +void RuntimeController::UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) { + client_.UpdateIsolateDescription(isolate_name, isolate_port); +} + Dart_Port RuntimeController::GetMainPort() { std::shared_ptr root_isolate = root_isolate_.lock(); return root_isolate ? root_isolate->main_port() : ILLEGAL_PORT; @@ -308,4 +327,21 @@ std::pair RuntimeController::GetRootIsolateReturnCode() { return root_isolate_return_code_; } +RuntimeController::Locale::Locale(std::string language_code_, + std::string country_code_, + std::string script_code_, + std::string variant_code_) + : language_code(language_code_), + country_code(country_code_), + script_code(script_code_), + variant_code(variant_code_) {} + +RuntimeController::Locale::~Locale() = default; + +RuntimeController::WindowData::WindowData() = default; + +RuntimeController::WindowData::WindowData(const WindowData& other) = default; + +RuntimeController::WindowData::~WindowData() = default; + } // namespace blink diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 67c7f3e44cb83..03f1389d8c613 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,15 +6,19 @@ #define FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_ #include +#include #include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/fml/macros.h" +#include "flutter/lib/ui/io_manager.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/window.h" #include "flutter/runtime/dart_vm.h" +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" namespace blink { class Scene; @@ -29,24 +33,24 @@ class RuntimeController final : public WindowClient { fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, TaskRunners task_runners, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, - std::string advisory_script_entrypoint); + std::string advisory_script_entrypoint, + std::function idle_notification_callback); - ~RuntimeController(); + ~RuntimeController() override; std::unique_ptr Clone() const; bool SetViewportMetrics(const ViewportMetrics& metrics); - bool SetLocale(const std::string& language_code, - const std::string& country_code, - const std::string& script_code, - const std::string& variant_code); + bool SetLocales(const std::vector& locale_data); bool SetUserSettingsData(const std::string& data); + bool SetLifecycleState(const std::string& data); + bool SetSemanticsEnabled(bool enabled); bool SetAccessibilityFeatures(int32_t flags); @@ -78,13 +82,38 @@ class RuntimeController final : public WindowClient { std::pair GetRootIsolateReturnCode(); private: + struct Locale { + Locale(std::string language_code_, + std::string country_code_, + std::string script_code_, + std::string variant_code_); + + ~Locale(); + + std::string language_code; + std::string country_code; + std::string script_code; + std::string variant_code; + }; + + // Stores data about the window to be used at startup + // as well as on hot restarts. Data kept here will persist + // after hot restart. struct WindowData { + WindowData(); + + WindowData(const WindowData& other); + + ~WindowData(); + ViewportMetrics viewport_metrics; std::string language_code; std::string country_code; std::string script_code; std::string variant_code; + std::vector locale_data; std::string user_settings_data = "{}"; + std::string lifecycle_state; bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; @@ -95,10 +124,11 @@ class RuntimeController final : public WindowClient { fml::RefPtr isolate_snapshot_; fml::RefPtr shared_snapshot_; TaskRunners task_runners_; - fml::WeakPtr resource_context_; - fml::RefPtr unref_queue_; + fml::WeakPtr snapshot_delegate_; + fml::WeakPtr io_manager_; std::string advisory_script_uri_; std::string advisory_script_entrypoint_; + std::function idle_notification_callback_; WindowData window_data_; std::weak_ptr root_isolate_; std::pair root_isolate_return_code_ = {false, 0}; @@ -108,10 +138,11 @@ class RuntimeController final : public WindowClient { fml::RefPtr isolate_snapshot, fml::RefPtr shared_snapshot, TaskRunners task_runners, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue, + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager, std::string advisory_script_uri, std::string advisory_script_entrypoint, + std::function idle_notification_callback, WindowData data); Window* GetWindowIfAvailable(); @@ -136,6 +167,10 @@ class RuntimeController final : public WindowClient { // |blink::WindowClient| FontCollection& GetFontCollection() override; + // |blink::WindowClient| + void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) override; + FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController); }; diff --git a/runtime/runtime_delegate.cc b/runtime/runtime_delegate.cc index 902672be06d8f..65e86bbde539e 100644 --- a/runtime/runtime_delegate.cc +++ b/runtime/runtime_delegate.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 2a9bded0a24f0..7d79dac199838 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -33,6 +33,9 @@ class RuntimeDelegate { virtual FontCollection& GetFontCollection() = 0; + virtual void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) = 0; + protected: virtual ~RuntimeDelegate(); }; diff --git a/runtime/service_protocol.cc b/runtime/service_protocol.cc index 207e683a90ebc..42e4eb7528d17 100644 --- a/runtime/service_protocol.cc +++ b/runtime/service_protocol.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -30,6 +30,8 @@ const fml::StringView ServiceProtocol::kFlushUIThreadTasksExtensionName = "_flutter.flushUIThreadTasks"; const fml::StringView ServiceProtocol::kSetAssetBundlePathExtensionName = "_flutter.setAssetBundlePath"; +const fml::StringView ServiceProtocol::kGetDisplayRefreshRateExtensionName = + "_flutter.getDisplayRefreshRate"; static constexpr fml::StringView kViewIdPrefx = "_flutterView/"; static constexpr fml::StringView kListViewsExtensionName = "_flutter.listViews"; @@ -45,22 +47,33 @@ ServiceProtocol::ServiceProtocol() kRunInViewExtensionName, kFlushUIThreadTasksExtensionName, kSetAssetBundlePathExtensionName, - }) {} + kGetDisplayRefreshRateExtensionName, + }), + handlers_mutex_(fml::SharedMutex::Create()) {} ServiceProtocol::~ServiceProtocol() { ToggleHooks(false); } -void ServiceProtocol::AddHandler(Handler* handler) { - std::lock_guard lock(handlers_mutex_); - handlers_.emplace(handler); +void ServiceProtocol::AddHandler(Handler* handler, + Handler::Description description) { + fml::UniqueLock lock(*handlers_mutex_); + handlers_.emplace(handler, description); } void ServiceProtocol::RemoveHandler(Handler* handler) { - std::lock_guard lock(handlers_mutex_); + fml::UniqueLock lock(*handlers_mutex_); handlers_.erase(handler); } +void ServiceProtocol::SetHandlerDescription(Handler* handler, + Handler::Description description) { + fml::SharedLock lock(*handlers_mutex_); + auto it = handlers_.find(handler); + if (it != handlers_.end()) + it->second.Store(description); +} + void ServiceProtocol::ToggleHooks(bool set) { for (const auto& endpoint : endpoints_) { Dart_RegisterIsolateServiceRequestCallback( @@ -166,7 +179,7 @@ bool ServiceProtocol::HandleMessage(fml::StringView method, return HandleListViewsMethod(response); } - std::lock_guard lock(handlers_mutex_); + fml::SharedLock lock(*handlers_mutex_); if (handlers_.size() == 0) { WriteServerErrorResponse(response, @@ -177,7 +190,7 @@ bool ServiceProtocol::HandleMessage(fml::StringView method, // Find the handler by its "viewId" in the params. auto view_id_param_found = params.find(fml::StringView{"viewId"}); if (view_id_param_found != params.end()) { - auto handler = reinterpret_cast(std::stoull( + auto* handler = reinterpret_cast(std::stoull( view_id_param_found->second.data() + kViewIdPrefx.size(), nullptr, 16)); auto handler_found = handlers_.find(handler); if (handler_found != handlers_.end()) { @@ -191,7 +204,8 @@ bool ServiceProtocol::HandleMessage(fml::StringView method, if (method == kScreenshotExtensionName || method == kScreenshotSkpExtensionName || method == kFlushUIThreadTasksExtensionName) { - return HandleMessageOnHandler(*handlers_.begin(), method, params, response); + return HandleMessageOnHandler(handlers_.begin()->first, method, params, + response); } WriteServerErrorResponse( @@ -236,26 +250,11 @@ void ServiceProtocol::Handler::Description::Write( bool ServiceProtocol::HandleListViewsMethod( rapidjson::Document& response) const { - // Collect handler descriptions on their respective task runners. - std::lock_guard lock(handlers_mutex_); + fml::SharedLock lock(*handlers_mutex_); std::vector> descriptions; for (const auto& handler : handlers_) { - fml::AutoResetWaitableEvent latch; - Handler::Description description; - - fml::TaskRunner::RunNowOrPostTask( - handler->GetServiceProtocolHandlerTaskRunner( - kListViewsExtensionName), // task runner - [&latch, // - &description, // - &handler // - ]() { - description = handler->GetServiceProtocolDescription(); - latch.Signal(); - }); - latch.Wait(); - descriptions.emplace_back(std::make_pair( - reinterpret_cast(handler), std::move(description))); + descriptions.emplace_back(reinterpret_cast(handler.first), + handler.second.Load()); } auto& allocator = response.GetAllocator(); diff --git a/runtime/service_protocol.h b/runtime/service_protocol.h index 46a680733d860..b514beaf55fcf 100644 --- a/runtime/service_protocol.h +++ b/runtime/service_protocol.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,13 +6,14 @@ #define FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ #include -#include #include #include #include "flutter/fml/compiler_specific.h" #include "flutter/fml/macros.h" #include "flutter/fml/string_view.h" +#include "flutter/fml/synchronization/atomic_object.h" +#include "flutter/fml/synchronization/shared_mutex.h" #include "flutter/fml/synchronization/thread_annotations.h" #include "flutter/fml/task_runner.h" #include "rapidjson/document.h" @@ -26,6 +27,7 @@ class ServiceProtocol { static const fml::StringView kRunInViewExtensionName; static const fml::StringView kFlushUIThreadTasksExtensionName; static const fml::StringView kSetAssetBundlePathExtensionName; + static const fml::StringView kGetDisplayRefreshRateExtensionName; class Handler { public: @@ -63,14 +65,17 @@ class ServiceProtocol { void ToggleHooks(bool set); - void AddHandler(Handler* handler); + void AddHandler(Handler* handler, Handler::Description description); void RemoveHandler(Handler* handler); + void SetHandlerDescription(Handler* handler, + Handler::Description description); + private: const std::set endpoints_; - mutable std::mutex handlers_mutex_; - std::set handlers_; + std::unique_ptr handlers_mutex_; + std::map> handlers_; FML_WARN_UNUSED_RESULT static bool HandleMessage(const char* method, diff --git a/runtime/start_up.cc b/runtime/start_up.cc index 22a4b966d34e2..a58a24afb03fa 100644 --- a/runtime/start_up.cc +++ b/runtime/start_up.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/start_up.h b/runtime/start_up.h index 49e0726239a65..603069e16a255 100644 --- a/runtime/start_up.h +++ b/runtime/start_up.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/test_font_data.cc b/runtime/test_font_data.cc index 5a7d41acc781f..66d99e483d01d 100644 --- a/runtime/test_font_data.cc +++ b/runtime/test_font_data.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/runtime/test_font_data.h b/runtime/test_font_data.h index 0cdc9a87ddec0..2f4c7bff12a5c 100644 --- a/runtime/test_font_data.h +++ b/runtime/test_font_data.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/BUILD.gn b/shell/BUILD.gn index 0672fae2edb95..e4bfd9e8fda5d 100644 --- a/shell/BUILD.gn +++ b/shell/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/shell/common/BUILD.gn b/shell/common/BUILD.gn index c2821e2a4bdc3..aa22968662c3b 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -1,8 +1,9 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_root/testing/testing.gni") +import("$flutter_root/shell/gpu/gpu.gni") # Template to generate a dart embedder resource.cc file. # Required invoker inputs: @@ -101,14 +102,13 @@ source_set("common") { "$flutter_root/synchronization", "//third_party/dart/runtime:dart_api", "//third_party/skia", - "//third_party/skia:gpu", ] public_deps = [ - "$flutter_root/shell/version", + "$flutter_root/common/version", "$flutter_root/third_party/txt", - "//third_party/tonic", "//third_party/rapidjson", + "//third_party/tonic", ] public_configs = [ "$flutter_root:config" ] @@ -139,12 +139,20 @@ template("shell_host_executable") { } } +shell_gpu_configuration("shell_unittests_gpu_configuration") { + enable_software = true + enable_vulkan = false + enable_gl = false +} + shell_host_executable("shell_unittests") { sources = [ "shell_unittests.cc", ] deps = [ + ":shell_unittests_gpu_configuration", + "$flutter_root/shell/", "$flutter_root/testing", ] } diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 727d521b7184f..622b86c238c92 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -39,6 +39,10 @@ Animator::Animator(Delegate& delegate, Animator::~Animator() = default; +float Animator::GetDisplayRefreshRate() const { + return waiter_->GetDisplayRefreshRate(); +} + void Animator::Stop() { paused_ = true; } @@ -58,6 +62,17 @@ void Animator::SetDimensionChangePending() { dimension_change_pending_ = true; } +void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) { + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + [self = weak_factory_.GetWeakPtr(), trace_flow_id] { + if (!self) { + return; + } + self->trace_flow_ids_.push_back(trace_flow_id); + }); +} + // This Parity is used by the timeline component to correctly align // GPU Workloads events with their respective Framework Workload. const char* Animator::FrameParity() { @@ -74,6 +89,13 @@ void Animator::BeginFrame(fml::TimePoint frame_start_time, fml::TimePoint frame_target_time) { TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++); + TRACE_EVENT0("flutter", "Animator::BeginFrame"); + while (!trace_flow_ids_.empty()) { + uint64_t trace_flow_id = trace_flow_ids_.front(); + TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id); + trace_flow_ids_.pop_front(); + } + frame_scheduled_ = false; notify_idle_task_id_++; regenerate_layer_tree_ = false; @@ -120,10 +142,13 @@ void Animator::BeginFrame(fml::TimePoint frame_start_time, if (!self.get()) { return; } - // If our (this task's) task id is the same as the current one, then - // no further frames were produced, and it is safe (w.r.t. jank) to - // notify the engine we are idle. - if (notify_idle_task_id == self->notify_idle_task_id_) { + // If our (this task's) task id is the same as the current one + // (meaning there were no follow up frames to the |BeginFrame| call + // that posted this task) and no frame is currently scheduled, then + // assume that we are idle, and notify the engine of this. + if (notify_idle_task_id == self->notify_idle_task_id_ && + !self->frame_scheduled_) { + TRACE_EVENT0("flutter", "BeginFrame idle callback"); self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() + 100000); } diff --git a/shell/common/animator.h b/shell/common/animator.h index 470f04b542881..15f8b8c745e67 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -1,10 +1,12 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_COMMON_ANIMATOR_H_ #define FLUTTER_SHELL_COMMON_ANIMATOR_H_ +#include + #include "flutter/common/task_runners.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/memory/weak_ptr.h" @@ -36,6 +38,8 @@ class Animator final { ~Animator(); + float GetDisplayRefreshRate() const; + void RequestFrame(bool regenerate_layer_tree = true); void Render(std::unique_ptr layer_tree); @@ -46,6 +50,10 @@ class Animator final { void SetDimensionChangePending(); + // Enqueue |trace_flow_id| into |trace_flow_ids_|. The corresponding flow + // will be ended during the next |BeginFrame|. + void EnqueueTraceFlowId(uint64_t trace_flow_id); + private: using LayerTreePipeline = flutter::Pipeline; @@ -75,6 +83,7 @@ class Animator final { int notify_idle_task_id_; bool dimension_change_pending_; SkISize last_layer_tree_size_; + std::deque trace_flow_ids_; fml::WeakPtrFactory weak_factory_; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 5e1f53a55912d..5b2d5a5c3dd37 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,8 +7,10 @@ #include #include #include +#include #include "flutter/common/settings.h" +#include "flutter/common/version/version.h" #include "flutter/fml/eintr_wrapper.h" #include "flutter/fml/file.h" #include "flutter/fml/make_copyable.h" @@ -25,10 +27,6 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPictureRecorder.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { static constexpr char kAssetChannel[] = "flutter/assets"; @@ -44,8 +42,8 @@ Engine::Engine(Delegate& delegate, blink::TaskRunners task_runners, blink::Settings settings, std::unique_ptr animator, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue) + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager) : delegate_(delegate), settings_(std::move(settings)), animator_(std::move(animator)), @@ -56,26 +54,31 @@ Engine::Engine(Delegate& delegate, // object as its delegate. The delegate may be called in the constructor and // we want to be fully initilazed by that point. runtime_controller_ = std::make_unique( - *this, // runtime delegate - &vm, // VM - std::move(isolate_snapshot), // isolate snapshot - std::move(shared_snapshot), // shared snapshot - std::move(task_runners), // task runners - std::move(resource_context), // resource context - std::move(unref_queue), // skia unref queue - settings_.advisory_script_uri, // advisory script uri - settings_.advisory_script_entrypoint // advisory script entrypoint + *this, // runtime delegate + &vm, // VM + std::move(isolate_snapshot), // isolate snapshot + std::move(shared_snapshot), // shared snapshot + std::move(task_runners), // task runners + std::move(snapshot_delegate), // snapshot delegate + std::move(io_manager), // io manager + settings_.advisory_script_uri, // advisory script uri + settings_.advisory_script_entrypoint, // advisory script entrypoint + settings_.idle_notification_callback // idle notification callback ); } Engine::~Engine() = default; +float Engine::GetDisplayRefreshRate() const { + return animator_->GetDisplayRefreshRate(); +} + fml::WeakPtr Engine::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } bool Engine::UpdateAssetManager( - fml::RefPtr new_asset_manager) { + std::shared_ptr new_asset_manager) { if (asset_manager_ == new_asset_manager) { return false; } @@ -87,10 +90,10 @@ bool Engine::UpdateAssetManager( } // Using libTXT as the text engine. + font_collection_.RegisterFonts(asset_manager_); + if (settings_.use_test_fonts) { font_collection_.RegisterTestFonts(); - } else { - font_collection_.RegisterFonts(asset_manager_); } return true; @@ -288,6 +291,8 @@ bool Engine::HandleLifecyclePlatformMessage(blink::PlatformMessage* message) { if (state == "AppLifecycleState.resumed" && have_surface_) { ScheduleFrame(); } + runtime_controller_->SetLifecycleState(state); + // Always forward these messages to the framework by returning false. return false; } @@ -325,17 +330,22 @@ bool Engine::HandleLocalizationPlatformMessage( if (args == root.MemberEnd() || !args->value.IsArray()) return false; - const auto& language = args->value[0]; - const auto& country = args->value[1]; - const auto& script = args->value[2]; - const auto& variant = args->value[3]; - - if (!language.IsString() || !country.IsString()) + const size_t strings_per_locale = 4; + if (args->value.Size() % strings_per_locale != 0) return false; + std::vector locale_data; + for (size_t locale_index = 0; locale_index < args->value.Size(); + locale_index += strings_per_locale) { + if (!args->value[locale_index].IsString() || + !args->value[locale_index + 1].IsString()) + return false; + locale_data.push_back(args->value[locale_index].GetString()); + locale_data.push_back(args->value[locale_index + 1].GetString()); + locale_data.push_back(args->value[locale_index + 2].GetString()); + locale_data.push_back(args->value[locale_index + 3].GetString()); + } - return runtime_controller_->SetLocale(language.GetString(), - country.GetString(), script.GetString(), - variant.GetString()); + return runtime_controller_->SetLocales(locale_data); } void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) { @@ -347,7 +357,11 @@ void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) { } } -void Engine::DispatchPointerDataPacket(const blink::PointerDataPacket& packet) { +void Engine::DispatchPointerDataPacket(const blink::PointerDataPacket& packet, + uint64_t trace_flow_id) { + TRACE_EVENT0("flutter", "Engine::DispatchPointerDataPacket"); + TRACE_FLOW_STEP("flutter", "PointerEvent", trace_flow_id); + animator_->EnqueueTraceFlowId(trace_flow_id); runtime_controller_->DispatchPointerDataPacket(packet); } @@ -412,6 +426,11 @@ void Engine::HandlePlatformMessage( } } +void Engine::UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) { + delegate_.UpdateIsolateDescription(isolate_name, isolate_port); +} + blink::FontCollection& Engine::GetFontCollection() { return font_collection_; } diff --git a/shell/common/engine.h b/shell/common/engine.h index 06436ce3683d0..b390b36a2b197 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/viewport_metrics.h" @@ -21,6 +22,7 @@ #include "flutter/runtime/runtime_controller.h" #include "flutter/runtime/runtime_delegate.h" #include "flutter/shell/common/animator.h" +#include "flutter/shell/common/io_manager.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" #include "third_party/skia/include/core/SkPicture.h" @@ -47,6 +49,9 @@ class Engine final : public blink::RuntimeDelegate { fml::RefPtr message) = 0; virtual void OnPreEngineRestart() = 0; + + virtual void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) = 0; }; Engine(Delegate& delegate, @@ -56,11 +61,13 @@ class Engine final : public blink::RuntimeDelegate { blink::TaskRunners task_runners, blink::Settings settings, std::unique_ptr animator, - fml::WeakPtr resource_context, - fml::RefPtr unref_queue); + fml::WeakPtr snapshot_delegate, + fml::WeakPtr io_manager); ~Engine() override; + float GetDisplayRefreshRate() const; + fml::WeakPtr GetWeakPtr() const; FML_WARN_UNUSED_RESULT @@ -73,7 +80,7 @@ class Engine final : public blink::RuntimeDelegate { FML_WARN_UNUSED_RESULT bool Restart(RunConfiguration configuration); - bool UpdateAssetManager(fml::RefPtr asset_manager); + bool UpdateAssetManager(std::shared_ptr asset_manager); void BeginFrame(fml::TimePoint frame_time); @@ -97,7 +104,8 @@ class Engine final : public blink::RuntimeDelegate { void DispatchPlatformMessage(fml::RefPtr message); - void DispatchPointerDataPacket(const blink::PointerDataPacket& packet); + void DispatchPointerDataPacket(const blink::PointerDataPacket& packet, + uint64_t trace_flow_id); void DispatchSemanticsAction(int id, blink::SemanticsAction action, @@ -119,7 +127,7 @@ class Engine final : public blink::RuntimeDelegate { std::unique_ptr runtime_controller_; std::string initial_route_; blink::ViewportMetrics viewport_metrics_; - fml::RefPtr asset_manager_; + std::shared_ptr asset_manager_; bool activity_running_; bool have_surface_; blink::FontCollection font_collection_; @@ -140,6 +148,10 @@ class Engine final : public blink::RuntimeDelegate { void HandlePlatformMessage( fml::RefPtr message) override; + // |blink::RuntimeDelegate| + void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) override; + void StopAnimator(); void StartAnimatorIfPossible(); diff --git a/shell/common/io_manager.cc b/shell/common/io_manager.cc index c92df1e910c99..a0172e1084253 100644 --- a/shell/common/io_manager.cc +++ b/shell/common/io_manager.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,8 @@ namespace shell { sk_sp IOManager::CreateCompatibleResourceLoadingContext( - GrBackend backend) { + GrBackend backend, + sk_sp gl_interface) { if (backend != GrBackend::kOpenGL_GrBackend) { return nullptr; } @@ -24,13 +25,14 @@ sk_sp IOManager::CreateCompatibleResourceLoadingContext( // thread. The necessary work isn't being flushed or synchronized with the // other threads correctly, so the textures end up blank. For now, suppress // that feature, which will cause texture uploads to do CPU YUV conversion. + // A similar work-around is also used in shell/gpu/gpu_surface_gl.cc. options.fDisableGpuYUVConversion = true; // To get video playback on the widest range of devices, we limit Skia to // ES2 shading language when the ES3 external image extension is missing. options.fPreferExternalImagesOverES3 = true; - if (auto context = GrContext::MakeGL(GrGLMakeNativeInterface(), options)) { + if (auto context = GrContext::MakeGL(gl_interface, options)) { // Do not cache textures created by the image decoder. These textures // should be deleted when they are no longer referenced by an SkImage. context->setResourceCacheLimits(0, 0); @@ -52,9 +54,11 @@ IOManager::IOManager(sk_sp resource_context, fml::TimeDelta::FromMilliseconds(250))), weak_factory_(this) { if (!resource_context_) { +#ifndef OS_FUCHSIA FML_DLOG(WARNING) << "The IO manager was initialized without a resource " "context. Async texture uploads will be disabled. " "Expect performance degradation."; +#endif // OS_FUCHSIA } } @@ -70,8 +74,29 @@ fml::WeakPtr IOManager::GetResourceContext() const { : fml::WeakPtr(); } +void IOManager::NotifyResourceContextAvailable( + sk_sp resource_context) { + // The resource context needs to survive as long as we have Dart objects + // referencing. We shouldn't ever need to replace it if we have one - unless + // we've somehow shut down the Dart VM and started a new one fresh. + if (!resource_context_) { + UpdateResourceContext(std::move(resource_context)); + } +} + +void IOManager::UpdateResourceContext(sk_sp resource_context) { + resource_context_ = std::move(resource_context); + resource_context_weak_factory_ = + resource_context_ ? std::make_unique>( + resource_context_.get()) + : nullptr; +} + fml::RefPtr IOManager::GetSkiaUnrefQueue() const { return unref_queue_; } +fml::WeakPtr IOManager::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} } // namespace shell diff --git a/shell/common/io_manager.h b/shell/common/io_manager.h index 0b459e20571d5..a69c42abbc311 100644 --- a/shell/common/io_manager.h +++ b/shell/common/io_manager.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,26 +10,41 @@ #include "flutter/flow/skia_gpu_object.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/io_manager.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { -class IOManager { +class IOManager : public blink::IOManager { public: // Convenience methods for platforms to create a GrContext used to supply to // the IOManager. The platforms may create the context themselves if they so // desire. static sk_sp CreateCompatibleResourceLoadingContext( - GrBackend backend); + GrBackend backend, + sk_sp gl_interface); IOManager(sk_sp resource_context, fml::RefPtr unref_queue_task_runner); - ~IOManager(); + virtual ~IOManager(); - fml::WeakPtr GetResourceContext() const; + fml::WeakPtr GetResourceContext() const override; - fml::RefPtr GetSkiaUnrefQueue() const; + // This method should be called when a resource_context first becomes + // available. It is safe to call multiple times, and will only update + // the held resource context if it has not already been set. + void NotifyResourceContextAvailable(sk_sp resource_context); + + // This method should be called if you want to force the IOManager to + // update its resource context reference. It should not be called + // if there are any Dart objects that have a reference to the old + // resource context, but may be called if the Dart VM is restarted. + void UpdateResourceContext(sk_sp resource_context); + + fml::RefPtr GetSkiaUnrefQueue() const override; + + fml::WeakPtr GetWeakPtr(); private: // Resource context management. diff --git a/shell/common/isolate_configuration.cc b/shell/common/isolate_configuration.cc index 57c38bef9f8d8..173973b64e5b1 100644 --- a/shell/common/isolate_configuration.cc +++ b/shell/common/isolate_configuration.cc @@ -1,15 +1,12 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/common/isolate_configuration.h" +#include "flutter/fml/make_copyable.h" #include "flutter/runtime/dart_vm.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { IsolateConfiguration::IsolateConfiguration() = default; @@ -61,7 +58,7 @@ class KernelIsolateConfiguration : public IsolateConfiguration { class KernelListIsolateConfiguration final : public IsolateConfiguration { public: KernelListIsolateConfiguration( - std::vector> kernel_pieces) + std::vector>> kernel_pieces) : kernel_pieces_(std::move(kernel_pieces)) {} // |shell::IsolateConfiguration| @@ -72,7 +69,8 @@ class KernelListIsolateConfiguration final : public IsolateConfiguration { for (size_t i = 0; i < kernel_pieces_.size(); i++) { bool last_piece = i + 1 == kernel_pieces_.size(); - if (!isolate.PrepareForRunningFromKernel(std::move(kernel_pieces_[i]), + + if (!isolate.PrepareForRunningFromKernel(kernel_pieces_[i].get(), last_piece)) { return false; } @@ -82,21 +80,88 @@ class KernelListIsolateConfiguration final : public IsolateConfiguration { } private: - std::vector> kernel_pieces_; + std::vector>> kernel_pieces_; FML_DISALLOW_COPY_AND_ASSIGN(KernelListIsolateConfiguration); }; +static std::vector ParseKernelListPaths( + std::unique_ptr kernel_list) { + FML_DCHECK(kernel_list); + + std::vector kernel_pieces_paths; + + const char* kernel_list_str = + reinterpret_cast(kernel_list->GetMapping()); + size_t kernel_list_size = kernel_list->GetSize(); + + size_t piece_path_start = 0; + while (piece_path_start < kernel_list_size) { + size_t piece_path_end = piece_path_start; + while ((piece_path_end < kernel_list_size) && + (kernel_list_str[piece_path_end] != '\n')) { + piece_path_end++; + } + std::string piece_path(&kernel_list_str[piece_path_start], + piece_path_end - piece_path_start); + kernel_pieces_paths.emplace_back(std::move(piece_path)); + + piece_path_start = piece_path_end + 1; + } + + return kernel_pieces_paths; +} + +static std::vector>> +PrepareKernelMappings(std::vector kernel_pieces_paths, + std::shared_ptr asset_manager, + fml::RefPtr io_worker) { + FML_DCHECK(asset_manager); + std::vector>> fetch_futures; + + for (const auto& kernel_pieces_path : kernel_pieces_paths) { + std::promise> fetch_promise; + fetch_futures.push_back(fetch_promise.get_future()); + auto fetch_task = + fml::MakeCopyable([asset_manager, kernel_pieces_path, + fetch_promise = std::move(fetch_promise)]() mutable { + fetch_promise.set_value( + asset_manager->GetAsMapping(kernel_pieces_path)); + }); + // Fulfill the promise on the worker if one is available or the current + // thread if one is not. + if (io_worker) { + io_worker->PostTask(fetch_task); + } else { + fetch_task(); + } + } + + return fetch_futures; +} + std::unique_ptr IsolateConfiguration::InferFromSettings( const blink::Settings& settings, - fml::RefPtr asset_manager) { + std::shared_ptr asset_manager, + fml::RefPtr io_worker) { // Running in AOT mode. if (blink::DartVM::IsRunningPrecompiledCode()) { return CreateForAppSnapshot(); } + if (!asset_manager) { + return nullptr; + } + + if (settings.application_kernel_asset.empty() && + settings.application_kernel_list_asset.empty()) { + FML_DLOG(ERROR) << "application_kernel_asset or " + "application_kernel_list_asset must be set"; + return nullptr; + } + // Running from kernel snapshot. - if (asset_manager) { + { std::unique_ptr kernel = asset_manager->GetAsMapping(settings.application_kernel_asset); if (kernel) { @@ -105,40 +170,17 @@ std::unique_ptr IsolateConfiguration::InferFromSettings( } // Running from kernel divided into several pieces (for sharing). - // TODO(fuchsia): Use async blobfs API once it becomes available. - if (asset_manager) { + { std::unique_ptr kernel_list = asset_manager->GetAsMapping(settings.application_kernel_list_asset); - if (kernel_list) { - const char* kernel_list_str = - reinterpret_cast(kernel_list->GetMapping()); - size_t kernel_list_size = kernel_list->GetSize(); - - std::vector> kernel_pieces; - - size_t piece_path_start = 0; - while (piece_path_start < kernel_list_size) { - size_t piece_path_end = piece_path_start; - while ((piece_path_end < kernel_list_size) && - (kernel_list_str[piece_path_end] != '\n')) { - piece_path_end++; - } - - std::string piece_path(&kernel_list_str[piece_path_start], - piece_path_end - piece_path_start); - std::unique_ptr piece = - asset_manager->GetAsMapping(piece_path); - if (piece == nullptr) { - FML_LOG(ERROR) << "Failed to load: " << piece_path; - return nullptr; - } - - kernel_pieces.emplace_back(std::move(piece)); - - piece_path_start = piece_path_end + 1; - } - return CreateForKernelList(std::move(kernel_pieces)); + if (!kernel_list) { + FML_LOG(ERROR) << "Failed to load: " << settings.application_kernel_asset; + return nullptr; } + auto kernel_pieces_paths = ParseKernelListPaths(std::move(kernel_list)); + auto kernel_mappings = PrepareKernelMappings(std::move(kernel_pieces_paths), + asset_manager, io_worker); + return CreateForKernelList(std::move(kernel_mappings)); } return nullptr; @@ -156,6 +198,17 @@ std::unique_ptr IsolateConfiguration::CreateForKernel( std::unique_ptr IsolateConfiguration::CreateForKernelList( std::vector> kernel_pieces) { + std::vector>> pieces; + for (auto& piece : kernel_pieces) { + std::promise> promise; + pieces.push_back(promise.get_future()); + promise.set_value(std::move(piece)); + } + return CreateForKernelList(std::move(pieces)); +} + +std::unique_ptr IsolateConfiguration::CreateForKernelList( + std::vector>> kernel_pieces) { return std::make_unique( std::move(kernel_pieces)); } diff --git a/shell/common/isolate_configuration.h b/shell/common/isolate_configuration.h index 7dd54ccf3b3b7..e9f8d66b2e433 100644 --- a/shell/common/isolate_configuration.h +++ b/shell/common/isolate_configuration.h @@ -1,10 +1,11 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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_COMMON_ISOLATE_CONFIGURATION_H_ #define FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ +#include #include #include @@ -22,13 +23,20 @@ class IsolateConfiguration { public: static std::unique_ptr InferFromSettings( const blink::Settings& settings, - fml::RefPtr asset_manager); + std::shared_ptr asset_manager, + fml::RefPtr io_worker); static std::unique_ptr CreateForAppSnapshot(); static std::unique_ptr CreateForKernel( std::unique_ptr kernel); + static std::unique_ptr CreateForKernelList( + std::vector>> kernel_pieces); + + // TODO(chinmaygarde): Remove this variant in favor of the one using futures + // for parallelizing asset loads. This one is in place for API compatibility + // till Android is updated. static std::unique_ptr CreateForKernelList( std::vector> kernel_pieces); diff --git a/shell/common/persistent_cache.cc b/shell/common/persistent_cache.cc index f7327a40b1686..64d29f15c2b58 100644 --- a/shell/common/persistent_cache.cc +++ b/shell/common/persistent_cache.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,16 +7,18 @@ #include #include +#include "flutter/common/version/version.h" #include "flutter/fml/base32.h" #include "flutter/fml/file.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/version/version.h" namespace shell { +std::string PersistentCache::cache_base_path_; + static std::string SkKeyToFilePath(const SkData& data) { if (data.data() == nullptr || data.size() == 0) { return ""; @@ -33,23 +35,37 @@ static std::string SkKeyToFilePath(const SkData& data) { return encode_result.second; } +bool PersistentCache::gIsReadOnly = false; + PersistentCache* PersistentCache::GetCacheForProcess() { static std::unique_ptr gPersistentCache; static std::once_flag once = {}; - std::call_once(once, []() { gPersistentCache.reset(new PersistentCache()); }); + std::call_once( + once, []() { gPersistentCache.reset(new PersistentCache(gIsReadOnly)); }); return gPersistentCache.get(); } -PersistentCache::PersistentCache() - : cache_directory_(std::make_shared( - CreateDirectory(fml::paths::GetCachesDirectory(), - { - "flutter_engine", // - GetFlutterEngineVersion(), // - "skia", // - GetSkiaVersion() // - }, - fml::FilePermission::kReadWrite))) { +void PersistentCache::SetCacheDirectoryPath(std::string path) { + cache_base_path_ = path; +} + +PersistentCache::PersistentCache(bool read_only) : is_read_only_(read_only) { + fml::UniqueFD cache_base_dir; + if (cache_base_path_.length()) { + cache_base_dir = fml::OpenDirectory(cache_base_path_.c_str(), false, + fml::FilePermission::kRead); + } else { + cache_base_dir = fml::paths::GetCachesDirectory(); + } + + if (cache_base_dir.is_valid()) { + cache_directory_ = std::make_shared( + CreateDirectory(cache_base_dir, + {"flutter_engine", blink::GetFlutterEngineVersion(), + "skia", blink::GetSkiaVersion()}, + read_only ? fml::FilePermission::kRead + : fml::FilePermission::kReadWrite)); + } if (!IsValid()) { FML_LOG(WARNING) << "Could not acquire the persistent cache directory. " "Caching of GPU resources on disk is disabled."; @@ -118,6 +134,12 @@ static void PersistentCacheStore(fml::RefPtr worker, // |GrContextOptions::PersistentCache| void PersistentCache::store(const SkData& key, const SkData& data) { + stored_new_shaders_ = true; + + if (is_read_only_) { + return; + } + if (!IsValid()) { return; } @@ -139,6 +161,24 @@ void PersistentCache::store(const SkData& key, const SkData& data) { std::move(file_name), std::move(mapping)); } +void PersistentCache::DumpSkp(const SkData& data) { + if (is_read_only_ || !IsValid()) { + FML_LOG(ERROR) << "Could not dump SKP from read-only or invalid persistent " + "cache."; + return; + } + + std::stringstream name_stream; + auto ticks = fml::TimePoint::Now().ToEpochDelta().ToNanoseconds(); + name_stream << "shader_dump_" << std::to_string(ticks) << ".skp"; + std::string file_name = name_stream.str(); + FML_LOG(INFO) << "Dumping " << file_name; + auto mapping = std::make_unique( + std::vector{data.bytes(), data.bytes() + data.size()}); + PersistentCacheStore(GetWorkerTaskRunner(), cache_directory_, + std::move(file_name), std::move(mapping)); +} + void PersistentCache::AddWorkerTaskRunner( fml::RefPtr task_runner) { std::lock_guard lock(worker_task_runners_mutex_); diff --git a/shell/common/persistent_cache.h b/shell/common/persistent_cache.h index 2bc1280d63c3e..7b3310e849bae 100644 --- a/shell/common/persistent_cache.h +++ b/shell/common/persistent_cache.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,23 +19,47 @@ namespace shell { class PersistentCache : public GrContextOptions::PersistentCache { public: + // Mutable static switch that can be set before GetCacheForProcess. If true, + // we'll only read existing caches but not generate new ones. Some clients + // (e.g., embedded devices) prefer generating persistent cache files for the + // specific device beforehand, and ship them as readonly files in OTA + // packages. + static bool gIsReadOnly; + static PersistentCache* GetCacheForProcess(); + static void SetCacheDirectoryPath(std::string path); + ~PersistentCache() override; void AddWorkerTaskRunner(fml::RefPtr task_runner); void RemoveWorkerTaskRunner(fml::RefPtr task_runner); + // Whether Skia tries to store any shader into this persistent cache after + // |ResetStoredNewShaders| is called. This flag is usually reset before each + // frame so we can know if Skia tries to compile new shaders in that frame. + bool StoredNewShaders() const { return stored_new_shaders_; } + void ResetStoredNewShaders() { stored_new_shaders_ = false; } + void DumpSkp(const SkData& data); + bool IsDumpingSkp() const { return is_dumping_skp_; } + void SetIsDumpingSkp(bool value) { is_dumping_skp_ = value; } + private: + static std::string cache_base_path_; + + const bool is_read_only_; std::shared_ptr cache_directory_; mutable std::mutex worker_task_runners_mutex_; std::multiset> worker_task_runners_ FML_GUARDED_BY(worker_task_runners_mutex_); + bool stored_new_shaders_ = false; + bool is_dumping_skp_ = false; + bool IsValid() const; - PersistentCache(); + PersistentCache(bool read_only = false); // |GrContextOptions::PersistentCache| sk_sp load(const SkData& key) override; diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 76c01dca9cef0..cd909814c66b8 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -60,7 +60,20 @@ void PlatformView::SetViewportMetrics(const blink::ViewportMetrics& metrics) { } void PlatformView::NotifyCreated() { - delegate_.OnPlatformViewCreated(CreateRenderingSurface()); + std::unique_ptr surface; + + // Threading: We want to use the platform view on the non-platform thread. + // Using the weak pointer is illegal. But, we are going to introduce a latch + // so that the platform view is not collected till the surface is obtained. + auto* platform_view = this; + fml::ManualResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), [platform_view, &surface, &latch]() { + surface = platform_view->CreateRenderingSurface(); + latch.Signal(); + }); + latch.Wait(); + delegate_.OnPlatformViewCreated(std::move(surface)); } void PlatformView::NotifyDestroyed() { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 153cae71d53a0..2ed5091b46895 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -123,6 +123,8 @@ class PlatformView { SkISize size_; fml::WeakPtrFactory weak_factory_; + // Unlike all other methods on the platform view, this is called on the GPU + // task runner. virtual std::unique_ptr CreateRenderingSurface(); private: diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 6f08b2afd0b7e..b86e7cf19c469 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -1,9 +1,11 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/persistent_cache.h" + #include #include "third_party/skia/include/core/SkEncodedImageFormat.h" @@ -14,12 +16,12 @@ #include "third_party/skia/include/core/SkSurfaceCharacterization.h" #include "third_party/skia/include/utils/SkBase64.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { +// The rasterizer will tell Skia to purge cached resources that have not been +// used within this interval. +static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000); + Rasterizer::Rasterizer(blink::TaskRunners task_runners) : Rasterizer(std::move(task_runners), std::make_unique()) {} @@ -39,6 +41,10 @@ fml::WeakPtr Rasterizer::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } +fml::WeakPtr Rasterizer::GetSnapshotDelegate() const { + return weak_factory_.GetWeakPtr(); +} + void Rasterizer::Setup(std::unique_ptr surface) { surface_ = std::move(surface); compositor_context_->OnGrContextCreated(); @@ -89,14 +95,76 @@ void Rasterizer::Draw( } } +sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, + SkISize picture_size) { + TRACE_EVENT0("flutter", __FUNCTION__); + + sk_sp surface; + SkImageInfo image_info = SkImageInfo::MakeN32Premul( + picture_size.width(), picture_size.height(), SkColorSpace::MakeSRGB()); + if (surface_ == nullptr || surface_->GetContext() == nullptr) { + // Raster surface is fine if there is no on screen surface. This might + // happen in case of software rendering. + surface = SkSurface::MakeRaster(image_info); + } else { + if (!surface_->MakeRenderContextCurrent()) { + return nullptr; + } + + // When there is an on screen surface, we need a render target SkSurface + // because we want to access texture backed images. + surface = SkSurface::MakeRenderTarget(surface_->GetContext(), // context + SkBudgeted::kNo, // budgeted + image_info // image info + ); + } + + if (surface == nullptr || surface->getCanvas() == nullptr) { + return nullptr; + } + + surface->getCanvas()->drawPicture(picture.get()); + + surface->getCanvas()->flush(); + + sk_sp device_snapshot; + { + TRACE_EVENT0("flutter", "MakeDeviceSnpashot"); + device_snapshot = surface->makeImageSnapshot(); + } + + if (device_snapshot == nullptr) { + return nullptr; + } + + { + TRACE_EVENT0("flutter", "DeviceHostTransfer"); + if (auto raster_image = device_snapshot->makeRasterImage()) { + return raster_image; + } + } + + return nullptr; +} + void Rasterizer::DoDraw(std::unique_ptr layer_tree) { if (!layer_tree || !surface_) { return; } + PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); + persistent_cache->ResetStoredNewShaders(); + if (DrawToSurface(*layer_tree)) { last_layer_tree_ = std::move(layer_tree); } + + if (persistent_cache->IsDumpingSkp() && + persistent_cache->StoredNewShaders()) { + auto screenshot = + ScreenshotLastLayerTree(ScreenshotType::SkiaPicture, false); + persistent_cache->DumpSkp(*screenshot.data); + } } bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) { @@ -113,18 +181,28 @@ bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) { // for instrumentation. compositor_context_->engine_time().SetLapTime(layer_tree.construction_time()); - auto canvas = frame->SkiaCanvas(); + auto* canvas = frame->SkiaCanvas(); - auto compositor_frame = compositor_context_->AcquireFrame( - surface_->GetContext(), canvas, surface_->GetRootTransformation(), true); + auto* external_view_embedder = surface_->GetExternalViewEmbedder(); - if (canvas) { - canvas->clear(SK_ColorTRANSPARENT); + if (external_view_embedder != nullptr) { + external_view_embedder->BeginFrame(layer_tree.frame_size()); } + auto compositor_frame = compositor_context_->AcquireFrame( + surface_->GetContext(), canvas, external_view_embedder, + surface_->GetRootTransformation(), true); + if (compositor_frame && compositor_frame->Raster(layer_tree, false)) { frame->Submit(); + if (external_view_embedder != nullptr) { + external_view_embedder->SubmitFrame(surface_->GetContext()); + } FireNextFrameCallbackIfPresent(); + + if (surface_->GetContext()) + surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + return true; } @@ -146,9 +224,11 @@ static sk_sp ScreenshotLayerTreeAsPicture( SkMatrix root_surface_transformation; root_surface_transformation.reset(); - auto frame = - compositor_context.AcquireFrame(nullptr, recorder.getRecordingCanvas(), - root_surface_transformation, false); + // TODO(amirh): figure out how to take a screenshot with embedded UIView. + // https://github.com/flutter/flutter/issues/23435 + auto frame = compositor_context.AcquireFrame( + nullptr, recorder.getRecordingCanvas(), nullptr, + root_surface_transformation, false); frame->Raster(*tree, true); @@ -160,7 +240,8 @@ static sk_sp ScreenshotLayerTreeAsPicture( static sk_sp CreateSnapshotSurface(GrContext* surface_context, const SkISize& size) { - const auto image_info = SkImageInfo::MakeN32Premul(size); + const auto image_info = SkImageInfo::MakeN32Premul( + size.width(), size.height(), SkColorSpace::MakeSRGB()); if (surface_context) { // There is a rendering surface that may contain textures that are going to // be referenced in the layer tree about to be drawn. @@ -190,7 +271,7 @@ static sk_sp ScreenshotLayerTreeAsImage( } // Draw the current layer tree into the snapshot surface. - auto canvas = snapshot_surface->getCanvas(); + auto* canvas = snapshot_surface->getCanvas(); // There is no root surface transformation for the screenshot layer. Reset the // matrix to identity. @@ -198,7 +279,7 @@ static sk_sp ScreenshotLayerTreeAsImage( root_surface_transformation.reset(); auto frame = compositor_context.AcquireFrame( - surface_context, canvas, root_surface_transformation, false); + surface_context, canvas, nullptr, root_surface_transformation, false); canvas->clear(SK_ColorTRANSPARENT); frame->Raster(*tree, true); canvas->flush(); @@ -236,7 +317,7 @@ static sk_sp ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { - auto layer_tree = GetLastLayerTree(); + auto* layer_tree = GetLastLayerTree(); if (layer_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; return {}; @@ -289,4 +370,22 @@ void Rasterizer::FireNextFrameCallbackIfPresent() { callback(); } +void Rasterizer::SetResourceCacheMaxBytes(int max_bytes) { + GrContext* context = surface_->GetContext(); + if (context) { + int max_resources; + context->getResourceCacheLimits(&max_resources, nullptr); + context->setResourceCacheLimits(max_resources, max_bytes); + } +} + +Rasterizer::Screenshot::Screenshot() {} + +Rasterizer::Screenshot::Screenshot(sk_sp p_data, SkISize p_size) + : data(std::move(p_data)), frame_size(p_size) {} + +Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default; + +Rasterizer::Screenshot::~Screenshot() = default; + } // namespace shell diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 1d3c03d8953df..17e429f54b4d6 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,12 +13,13 @@ #include "flutter/fml/closure.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/shell/common/surface.h" #include "flutter/synchronization/pipeline.h" namespace shell { -class Rasterizer final { +class Rasterizer final : public blink::SnapshotDelegate { public: Rasterizer(blink::TaskRunners task_runners); @@ -33,6 +34,8 @@ class Rasterizer final { fml::WeakPtr GetWeakPtr() const; + fml::WeakPtr GetSnapshotDelegate() const; + flow::LayerTree* GetLastLayerTree(); void DrawLastLayerTree(); @@ -51,10 +54,13 @@ class Rasterizer final { sk_sp data; SkISize frame_size = SkISize::MakeEmpty(); - Screenshot() {} + Screenshot(); + + Screenshot(sk_sp p_data, SkISize p_size); + + Screenshot(const Screenshot& other); - Screenshot(sk_sp p_data, SkISize p_size) - : data(std::move(p_data)), frame_size(p_size) {} + ~Screenshot(); }; Screenshot ScreenshotLastLayerTree(ScreenshotType type, bool base64_encode); @@ -67,6 +73,8 @@ class Rasterizer final { return compositor_context_.get(); } + void SetResourceCacheMaxBytes(int max_bytes); + private: blink::TaskRunners task_runners_; std::unique_ptr surface_; @@ -75,6 +83,10 @@ class Rasterizer final { fml::closure next_frame_callback_; fml::WeakPtrFactory weak_factory_; + // |blink::SnapshotDelegate| + sk_sp MakeRasterSnapshot(sk_sp picture, + SkISize picture_size) override; + void DoDraw(std::unique_ptr layer_tree); bool DrawToSurface(flow::LayerTree& layer_tree); diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index 57784a0e08d67..122b45e6b0eff 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,8 +13,9 @@ namespace shell { RunConfiguration RunConfiguration::InferFromSettings( - const blink::Settings& settings) { - auto asset_manager = fml::MakeRefCounted(); + const blink::Settings& settings, + fml::RefPtr io_worker) { + auto asset_manager = std::make_shared(); asset_manager->PushBack(std::make_unique( fml::Duplicate(settings.assets_dir))); @@ -23,18 +24,19 @@ RunConfiguration RunConfiguration::InferFromSettings( std::make_unique(fml::OpenDirectory( settings.assets_path.c_str(), false, fml::FilePermission::kRead))); - return {IsolateConfiguration::InferFromSettings(settings, asset_manager), + return {IsolateConfiguration::InferFromSettings(settings, asset_manager, + io_worker), asset_manager}; } RunConfiguration::RunConfiguration( std::unique_ptr configuration) : RunConfiguration(std::move(configuration), - fml::MakeRefCounted()) {} + std::make_shared()) {} RunConfiguration::RunConfiguration( std::unique_ptr configuration, - fml::RefPtr asset_manager) + std::shared_ptr asset_manager) : isolate_configuration_(std::move(configuration)), asset_manager_(std::move(asset_manager)) {} @@ -66,7 +68,7 @@ void RunConfiguration::SetEntrypointAndLibrary(std::string entrypoint, entrypoint_library_ = std::move(library); } -fml::RefPtr RunConfiguration::GetAssetManager() const { +std::shared_ptr RunConfiguration::GetAssetManager() const { return asset_manager_; } diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h index bc3b78f52e157..7cdebdd9f4a6b 100644 --- a/shell/common/run_configuration.h +++ b/shell/common/run_configuration.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,12 +20,14 @@ namespace shell { class RunConfiguration { public: - static RunConfiguration InferFromSettings(const blink::Settings& settings); + static RunConfiguration InferFromSettings( + const blink::Settings& settings, + fml::RefPtr io_worker = nullptr); RunConfiguration(std::unique_ptr configuration); RunConfiguration(std::unique_ptr configuration, - fml::RefPtr asset_manager); + std::shared_ptr asset_manager); RunConfiguration(RunConfiguration&&); @@ -39,7 +41,7 @@ class RunConfiguration { void SetEntrypointAndLibrary(std::string entrypoint, std::string library); - fml::RefPtr GetAssetManager() const; + std::shared_ptr GetAssetManager() const; const std::string& GetEntrypoint() const; @@ -49,7 +51,7 @@ class RunConfiguration { private: std::unique_ptr isolate_configuration_; - fml::RefPtr asset_manager_; + std::shared_ptr asset_manager_; std::string entrypoint_ = "main"; std::string entrypoint_library_ = ""; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 8f07f500588a4..6ba709e879f19 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -31,12 +31,10 @@ #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/tonic/common/log.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { +constexpr char kSkiaChannel[] = "flutter/skia"; + std::unique_ptr Shell::CreateShellOnPlatformThread( blink::TaskRunners task_runners, blink::Settings settings, @@ -69,22 +67,16 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( // other subsystems. fml::AutoResetWaitableEvent io_latch; std::unique_ptr io_manager; - fml::WeakPtr resource_context; - fml::RefPtr unref_queue; auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); fml::TaskRunner::RunNowOrPostTask( io_task_runner, - [&io_latch, // - &io_manager, // - &resource_context, // - &unref_queue, // - &platform_view, // - io_task_runner // + [&io_latch, // + &io_manager, // + &platform_view, // + io_task_runner // ]() { io_manager = std::make_unique( platform_view->CreateResourceContext(), io_task_runner); - resource_context = io_manager->GetResourceContext(); - unref_queue = io_manager->GetSkiaUnrefQueue(); io_latch.Signal(); }); io_latch.Wait(); @@ -92,31 +84,36 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( // Create the rasterizer on the GPU thread. fml::AutoResetWaitableEvent gpu_latch; std::unique_ptr rasterizer; + fml::WeakPtr snapshot_delegate; fml::TaskRunner::RunNowOrPostTask( task_runners.GetGPUTaskRunner(), [&gpu_latch, // &rasterizer, // on_create_rasterizer, // - shell = shell.get() // + shell = shell.get(), // + &snapshot_delegate // ]() { if (auto new_rasterizer = on_create_rasterizer(*shell)) { rasterizer = std::move(new_rasterizer); + snapshot_delegate = rasterizer->GetSnapshotDelegate(); } gpu_latch.Signal(); }); + gpu_latch.Wait(); + // Create the engine on the UI thread. fml::AutoResetWaitableEvent ui_latch; std::unique_ptr engine; fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetUITaskRunner(), - fml::MakeCopyable([&ui_latch, // - &engine, // - shell = shell.get(), // - isolate_snapshot = std::move(isolate_snapshot), // - shared_snapshot = std::move(shared_snapshot), // - vsync_waiter = std::move(vsync_waiter), // - resource_context = std::move(resource_context), // - unref_queue = std::move(unref_queue) // + fml::MakeCopyable([&ui_latch, // + &engine, // + shell = shell.get(), // + isolate_snapshot = std::move(isolate_snapshot), // + shared_snapshot = std::move(shared_snapshot), // + vsync_waiter = std::move(vsync_waiter), // + snapshot_delegate = std::move(snapshot_delegate), // + io_manager = io_manager->GetWeakPtr() // ]() mutable { const auto& task_runners = shell->GetTaskRunners(); @@ -125,20 +122,19 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( auto animator = std::make_unique(*shell, task_runners, std::move(vsync_waiter)); - engine = std::make_unique(*shell, // - shell->GetDartVM(), // - std::move(isolate_snapshot), // - std::move(shared_snapshot), // - task_runners, // - shell->GetSettings(), // - std::move(animator), // - std::move(resource_context), // - std::move(unref_queue) // + engine = std::make_unique(*shell, // + shell->GetDartVM(), // + std::move(isolate_snapshot), // + std::move(shared_snapshot), // + task_runners, // + shell->GetSettings(), // + std::move(animator), // + std::move(snapshot_delegate), // + std::move(io_manager) // ); ui_latch.Signal(); })); - gpu_latch.Wait(); ui_latch.Wait(); // We are already on the platform thread. So there is no platform latch to // wait on. @@ -191,10 +187,14 @@ static void PerformInitializationTasks(const blink::Settings& settings) { FML_DLOG(INFO) << "Skia deterministic rendering is enabled."; } - if (settings.icu_data_path.size() != 0) { - fml::icu::InitializeICU(settings.icu_data_path); - } else { - FML_DLOG(WARNING) << "Skipping ICU initialization in the shell."; + if (settings.icu_initialization_required) { + if (settings.icu_data_path.size() != 0) { + fml::icu::InitializeICU(settings.icu_data_path); + } else if (settings.icu_mapper) { + fml::icu::InitializeICUFromMapping(settings.icu_mapper()); + } else { + FML_DLOG(WARNING) << "Skipping ICU initialization in the shell."; + } } }); } @@ -291,6 +291,11 @@ Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kGetDisplayRefreshRateExtensionName.ToString()] = + {task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this, + std::placeholders::_1, std::placeholders::_2)}; } Shell::~Shell() { @@ -371,12 +376,15 @@ bool Shell::Setup(std::unique_ptr platform_view, is_setup_ = true; if (auto vm = blink::DartVM::ForProcessIfInitialized()) { - vm->GetServiceProtocol().AddHandler(this); + vm->GetServiceProtocol().AddHandler(this, GetServiceProtocolDescription()); } PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( task_runners_.GetIOTaskRunner()); + PersistentCache::GetCacheForProcess()->SetIsDumpingSkp( + settings_.dump_skp_on_shader_compilation); + return true; } @@ -424,27 +432,70 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { if (rasterizer) { rasterizer->Setup(std::move(surface)); } - // Step 2: All done. Signal the latch that the platform thread is waiting + // Step 3: All done. Signal the latch that the platform thread is waiting // on. latch.Signal(); }); + // The normal flow exectued by this method is that the platform thread is + // starting the sequence and waiting on the latch. Later the UI thread posts + // gpu_task to the GPU thread which signals the latch. If the GPU the and + // platform threads are the same this results in a deadlock as the gpu_task + // will never be posted to the plaform/gpu thread that is blocked on a latch. + // To avoid the described deadlock, if the gpu and the platform threads are + // the same, should_post_gpu_task will be false, and then instead of posting a + // task to the gpu thread, the ui thread just signals the latch and the + // platform/gpu thread follows with executing gpu_task. + bool should_post_gpu_task = + task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); + auto ui_task = [engine = engine_->GetWeakPtr(), // gpu_task_runner = task_runners_.GetGPUTaskRunner(), // - gpu_task // + gpu_task, should_post_gpu_task, + &latch // ] { if (engine) { engine->OnOutputSurfaceCreated(); } - // Step 1: Next, tell the GPU thread that it should create a surface for its + // Step 2: Next, tell the GPU thread that it should create a surface for its // rasterizer. - fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + if (should_post_gpu_task) { + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + } else { + // See comment on should_post_gpu_task, in this case we just unblock + // the platform thread. + latch.Signal(); + } }; - // Step 0: Post a task onto the UI thread to tell the engine that it has an - // output surface. - fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); + // Threading: Capture platform view by raw pointer and not the weak pointer. + // We are going to use the pointer on the IO thread which is not safe with a + // weak pointer. However, we are preventing the platform view from being + // collected by using a latch. + auto* platform_view = platform_view_.get(); + + FML_DCHECK(platform_view); + + auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view, + ui_task_runner = task_runners_.GetUITaskRunner(), ui_task] { + if (io_manager && !io_manager->GetResourceContext()) { + io_manager->NotifyResourceContextAvailable( + platform_view->CreateResourceContext()); + } + // Step 1: Next, post a task on the UI thread to tell the engine that it has + // an output surface. + fml::TaskRunner::RunNowOrPostTask(ui_task_runner, ui_task); + }; + + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task); + latch.Wait(); + if (!should_post_gpu_task) { + // See comment on should_post_gpu_task, in this case the gpu_task + // wasn't executed, and we just run it here as the platform thread + // is the GPU thread. + gpu_task(); + } } // |shell::PlatformView::Delegate| @@ -478,21 +529,46 @@ void Shell::OnPlatformViewDestroyed() { fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task); }; + // The normal flow exectued by this method is that the platform thread is + // starting the sequence and waiting on the latch. Later the UI thread posts + // gpu_task to the GPU thread triggers signaling the latch(on the IO thread). + // If the GPU the and platform threads are the same this results in a deadlock + // as the gpu_task will never be posted to the plaform/gpu thread that is + // blocked on a latch. To avoid the described deadlock, if the gpu and the + // platform threads are the same, should_post_gpu_task will be false, and then + // instead of posting a task to the gpu thread, the ui thread just signals the + // latch and the platform/gpu thread follows with executing gpu_task. + bool should_post_gpu_task = + task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); + auto ui_task = [engine = engine_->GetWeakPtr(), - gpu_task_runner = task_runners_.GetGPUTaskRunner(), - gpu_task]() { + gpu_task_runner = task_runners_.GetGPUTaskRunner(), gpu_task, + should_post_gpu_task, &latch]() { if (engine) { engine->OnOutputSurfaceDestroyed(); } // Step 1: Next, tell the GPU thread that its rasterizer should suspend // access to the underlying surface. - fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + if (should_post_gpu_task) { + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + } else { + // See comment on should_post_gpu_task, in this case we just unblock + // the platform thread. + latch.Signal(); + } }; // Step 0: Post a task onto the UI thread to tell the engine that its output // surface is about to go away. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); latch.Wait(); + if (!should_post_gpu_task) { + // See comment on should_post_gpu_task, in this case the gpu_task + // wasn't executed, and we just run it here as the platform thread + // is the GPU thread. + gpu_task(); + latch.Wait(); + } } // |shell::PlatformView::Delegate| @@ -526,14 +602,18 @@ void Shell::OnPlatformViewDispatchPlatformMessage( // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDispatchPointerDataPacket( std::unique_ptr packet) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchPointerDataPacket"); + TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = engine_->GetWeakPtr(), packet = std::move(packet)] { + [engine = engine_->GetWeakPtr(), packet = std::move(packet), + flow_id = next_pointer_flow_id_] { if (engine) { - engine->DispatchPointerDataPacket(*packet); + engine->DispatchPointerDataPacket(*packet, flow_id); } })); + next_pointer_flow_id_++; } // |shell::PlatformView::Delegate| @@ -586,7 +666,7 @@ void Shell::OnPlatformViewRegisterTexture( task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture] { if (rasterizer) { - if (auto registry = rasterizer->GetTextureRegistry()) { + if (auto* registry = rasterizer->GetTextureRegistry()) { registry->RegisterTexture(texture); } } @@ -601,7 +681,7 @@ void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) { task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { if (rasterizer) { - if (auto registry = rasterizer->GetTextureRegistry()) { + if (auto* registry = rasterizer->GetTextureRegistry()) { registry->UnregisterTexture(texture_id); } } @@ -616,7 +696,7 @@ void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) { // Tell the rasterizer that one of its textures has a new frame available. task_runners_.GetGPUTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { - auto registry = rasterizer->GetTextureRegistry(); + auto* registry = rasterizer->GetTextureRegistry(); if (!registry) { return; @@ -720,6 +800,11 @@ void Shell::OnEngineHandlePlatformMessage( FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + if (message->channel() == kSkiaChannel) { + HandleEngineSkiaMessage(std::move(message)); + return; + } + task_runners_.GetPlatformTaskRunner()->PostTask( [view = platform_view_->GetWeakPtr(), message = std::move(message)]() { if (view) { @@ -728,6 +813,31 @@ void Shell::OnEngineHandlePlatformMessage( }); } +void Shell::HandleEngineSkiaMessage( + fml::RefPtr message) { + const auto& data = message->data(); + + rapidjson::Document document; + document.Parse(reinterpret_cast(data.data()), data.size()); + if (document.HasParseError() || !document.IsObject()) + return; + auto root = document.GetObject(); + auto method = root.FindMember("method"); + if (method->value != "Skia.setResourceCacheMaxBytes") + return; + auto args = root.FindMember("args"); + if (args == root.MemberEnd() || !args->value.IsInt()) + return; + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), + max_bytes = args->value.GetInt()] { + if (rasterizer) { + rasterizer->SetResourceCacheMaxBytes(max_bytes); + } + }); +} + // |shell::Engine::Delegate| void Shell::OnPreEngineRestart() { FML_DCHECK(is_setup_); @@ -747,6 +857,15 @@ void Shell::OnPreEngineRestart() { latch.Wait(); } +// |shell::Engine::Delegate| +void Shell::UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) { + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { + Handler::Description description(isolate_port, isolate_name); + vm->GetServiceProtocol().SetHandlerDescription(this, description); + } +} + // |blink::ServiceProtocol::Handler| fml::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( fml::StringView method) const { @@ -928,6 +1047,16 @@ bool Shell::OnServiceProtocolFlushUIThreadTasks( return true; } +bool Shell::OnServiceProtocolGetDisplayRefreshRate( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + response.SetObject(); + response.AddMember("fps", engine_->GetDisplayRefreshRate(), + response.GetAllocator()); + return true; +} + // Service protocol handler bool Shell::OnServiceProtocolSetAssetBundlePath( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, @@ -943,7 +1072,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( auto& allocator = response.GetAllocator(); response.SetObject(); - auto asset_manager = fml::MakeRefCounted(); + auto asset_manager = std::make_shared(); asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").ToString().c_str(), false, diff --git a/shell/common/shell.h b/shell/common/shell.h index 62723fde78521..17b0f170561d5 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -99,6 +99,8 @@ class Shell final : public PlatformView::Delegate, service_protocol_handlers_; bool is_setup_ = false; + uint64_t next_pointer_flow_id_ = 0; + Shell(blink::TaskRunners task_runners, blink::Settings settings); static std::unique_ptr CreateShellOnPlatformThread( @@ -179,9 +181,15 @@ class Shell final : public PlatformView::Delegate, void OnEngineHandlePlatformMessage( fml::RefPtr message) override; + void HandleEngineSkiaMessage(fml::RefPtr message); + // |shell::Engine::Delegate| void OnPreEngineRestart() override; + // |shell::Engine::Delegate| + void UpdateIsolateDescription(const std::string isolate_name, + int64_t isolate_port) override; + // |blink::ServiceProtocol::Handler| fml::RefPtr GetServiceProtocolHandlerTaskRunner( fml::StringView method) const override; @@ -221,6 +229,11 @@ class Shell final : public PlatformView::Delegate, const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response); + // Service protocol handler + bool OnServiceProtocolGetDisplayRefreshRate( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + FML_DISALLOW_COPY_AND_ASSIGN(Shell); }; diff --git a/shell/common/shell_benchmarks.cc b/shell/common/shell_benchmarks.cc index 9d26a068bd327..df15b8f1b40ff 100644 --- a/shell/common/shell_benchmarks.cc +++ b/shell/common/shell_benchmarks.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 6c1cc66c9f6ff..85c6b7962ae55 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" +#include "flutter/shell/gpu/gpu_surface_software.h" #include "gtest/gtest.h" #define CURRENT_TEST_NAME \ @@ -23,6 +24,66 @@ namespace shell { +class TestPlatformView : public PlatformView, + public GPUSurfaceSoftwareDelegate { + public: + TestPlatformView(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners) + : PlatformView(delegate, std::move(task_runners)) {} + + private: + // |PlatformView| + std::unique_ptr CreateRenderingSurface() override { + return std::make_unique(this); + } + + // |GPUSurfaceSoftwareDelegate| + virtual sk_sp AcquireBackingStore(const SkISize& size) override { + SkImageInfo image_info = SkImageInfo::MakeN32Premul( + size.width(), size.height(), SkColorSpace::MakeSRGB()); + return SkSurface::MakeRaster(image_info); + } + + // |GPUSurfaceSoftwareDelegate| + virtual bool PresentBackingStore(sk_sp backing_store) override { + return true; + } + + FML_DISALLOW_COPY_AND_ASSIGN(TestPlatformView); +}; + +static bool ValidateShell(Shell* shell) { + if (!shell) { + return false; + } + + if (!shell->IsSetup()) { + return false; + } + + { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { + shell->GetPlatformView()->NotifyCreated(); + latch.Signal(); + }); + latch.Wait(); + } + + { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() { + shell->GetPlatformView()->NotifyDestroyed(); + latch.Signal(); + }); + latch.Wait(); + } + + return true; +} + TEST(ShellTest, InitializeWithInvalidThreads) { blink::Settings settings = {}; settings.task_observer_add = [](intptr_t, fml::closure) {}; @@ -31,7 +92,8 @@ TEST(ShellTest, InitializeWithInvalidThreads) { auto shell = Shell::Create( std::move(task_runners), settings, [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); + return std::make_unique(shell, + shell.GetTaskRunners()); }, [](Shell& shell) { return std::make_unique(shell.GetTaskRunners()); @@ -54,12 +116,13 @@ TEST(ShellTest, InitializeWithDifferentThreads) { auto shell = Shell::Create( std::move(task_runners), settings, [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); + return std::make_unique(shell, + shell.GetTaskRunners()); }, [](Shell& shell) { return std::make_unique(shell.GetTaskRunners()); }); - ASSERT_TRUE(shell); + ASSERT_TRUE(ValidateShell(shell.get())); } TEST(ShellTest, InitializeWithSingleThread) { @@ -74,12 +137,13 @@ TEST(ShellTest, InitializeWithSingleThread) { auto shell = Shell::Create( std::move(task_runners), settings, [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); + return std::make_unique(shell, + shell.GetTaskRunners()); }, [](Shell& shell) { return std::make_unique(shell.GetTaskRunners()); }); - ASSERT_TRUE(shell); + ASSERT_TRUE(ValidateShell(shell.get())); } TEST(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { @@ -93,12 +157,13 @@ TEST(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { auto shell = Shell::Create( std::move(task_runners), settings, [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); + return std::make_unique(shell, + shell.GetTaskRunners()); }, [](Shell& shell) { return std::make_unique(shell.GetTaskRunners()); }); - ASSERT_TRUE(shell); + ASSERT_TRUE(ValidateShell(shell.get())); } TEST(ShellTest, InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { @@ -117,12 +182,41 @@ TEST(ShellTest, InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { auto shell = Shell::Create( std::move(task_runners), settings, [](Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); + return std::make_unique(shell, + shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(ValidateShell(shell.get())); +} + +// Reported in Bug: Engine deadlocks when gpu and platforms threads are the same +// #21398 (https://github.com/flutter/flutter/issues/21398) +TEST(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fml::closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + ThreadHost thread_host( + "io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI); + blink::TaskRunners task_runners( + "test", + thread_host.platform_thread->GetTaskRunner(), // platform + thread_host.platform_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, + shell.GetTaskRunners()); }, [](Shell& shell) { return std::make_unique(shell.GetTaskRunners()); }); - ASSERT_TRUE(shell); + ASSERT_TRUE(ValidateShell(shell.get())); } } // namespace shell diff --git a/shell/common/skia_event_tracer_impl.cc b/shell/common/skia_event_tracer_impl.cc index 2f1c386f08a0b..7b073a729638e 100644 --- a/shell/common/skia_event_tracer_impl.cc +++ b/shell/common/skia_event_tracer_impl.cc @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/common/skia_event_tracer_impl.h b/shell/common/skia_event_tracer_impl.h index 55305b9cbdd4b..12e2fc41dee85 100644 --- a/shell/common/skia_event_tracer_impl.h +++ b/shell/common/skia_event_tracer_impl.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/common/surface.cc b/shell/common/surface.cc index f0b3ba19d93ab..b1134acc1b03b 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -1,11 +1,10 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/common/surface.h" #include "flutter/fml/logging.h" -#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h" #include "third_party/skia/include/core/SkSurface.h" namespace shell { @@ -14,10 +13,6 @@ SurfaceFrame::SurfaceFrame(sk_sp surface, SubmitCallback submit_callback) : submitted_(false), surface_(surface), submit_callback_(submit_callback) { FML_DCHECK(submit_callback_); - if (surface_) { - xform_canvas_ = SkCreateColorSpaceXformCanvas(surface_->getCanvas(), - SkColorSpace::MakeSRGB()); - } } SurfaceFrame::~SurfaceFrame() { @@ -38,9 +33,6 @@ bool SurfaceFrame::Submit() { } SkCanvas* SurfaceFrame::SkiaCanvas() { - if (xform_canvas_) { - return xform_canvas_.get(); - } return surface_ != nullptr ? surface_->getCanvas() : nullptr; } @@ -64,4 +56,12 @@ Surface::Surface() = default; Surface::~Surface() = default; +flow::ExternalViewEmbedder* Surface::GetExternalViewEmbedder() { + return nullptr; +} + +bool Surface::MakeRenderContextCurrent() { + return true; +} + } // namespace shell diff --git a/shell/common/surface.h b/shell/common/surface.h index beef9765da490..dc37c803d9ce9 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,13 +8,14 @@ #include #include "flutter/flow/compositor_context.h" +#include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "third_party/skia/include/core/SkCanvas.h" namespace shell { /// Represents a Frame that has been fully configured for the underlying client -/// rendering API. A frame may only be sumitted once. +/// rendering API. A frame may only be submitted once. class SurfaceFrame { public: using SubmitCallback = @@ -33,7 +34,6 @@ class SurfaceFrame { private: bool submitted_; sk_sp surface_; - std::unique_ptr xform_canvas_; SubmitCallback submit_callback_; bool PerformSubmit(); @@ -55,6 +55,10 @@ class Surface { virtual GrContext* GetContext() = 0; + virtual flow::ExternalViewEmbedder* GetExternalViewEmbedder(); + + virtual bool MakeRenderContextCurrent(); + private: FML_DISALLOW_COPY_AND_ASSIGN(Surface); }; diff --git a/shell/common/switches.cc b/shell/common/switches.cc index 98f0bb353015f..215e043f78505 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,9 +9,10 @@ #include #include +#include "flutter/common/version/version.h" +#include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/fml/string_view.h" -#include "flutter/shell/version/version.h" // Include once for the default enum definition. #include "flutter/shell/common/switches.h" @@ -45,11 +46,12 @@ void PrintUsage(const std::string& executable_name) { std::cerr << "Versions: " << std::endl << std::endl; - std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion() + std::cerr << "Flutter Engine Version: " << blink::GetFlutterEngineVersion() << std::endl; - std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl; + std::cerr << "Skia Version: " << blink::GetSkiaVersion() << std::endl; - std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl; + std::cerr << "Dart Version: " << blink::GetDartVersion() << std::endl + << std::endl; std::cerr << "Available Flags:" << std::endl; @@ -121,6 +123,33 @@ static bool GetSwitchValue(const fml::CommandLine& command_line, return false; } +std::unique_ptr GetSymbolMapping(std::string symbol_prefix, + std::string native_lib_path) { + const uint8_t* mapping; + intptr_t size; + + auto lookup_symbol = [&mapping, &size, symbol_prefix]( + const fml::RefPtr& library) { + mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str()); + size = reinterpret_cast( + library->ResolveSymbol((symbol_prefix + "_size").c_str())); + }; + + fml::RefPtr library = + fml::NativeLibrary::CreateForCurrentProcess(); + lookup_symbol(library); + + if (!(mapping && size)) { + // Symbol lookup for the current process fails on some devices. As a + // fallback, try doing the lookup based on the path to the Flutter library. + library = fml::NativeLibrary::Create(native_lib_path.c_str()); + lookup_symbol(library); + } + + FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix; + return std::make_unique(mapping, size); +} + blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { blink::Settings settings = {}; @@ -139,8 +168,8 @@ blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { } // Checked mode overrides. - settings.dart_non_checked_mode = - command_line.HasOption(FlagForSwitch(Switch::DartNonCheckedMode)); + settings.disable_dart_asserts = + command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts)); settings.ipv6 = command_line.HasOption(FlagForSwitch(Switch::IPv6)); @@ -170,12 +199,6 @@ blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir), &settings.assets_path); - command_line.GetOptionValue(FlagForSwitch(Switch::MainDartFile), - &settings.main_dart_file_path); - - command_line.GetOptionValue(FlagForSwitch(Switch::Packages), - &settings.packages_file_path); - std::string aot_shared_library_path; command_line.GetOptionValue(FlagForSwitch(Switch::AotSharedLibraryPath), &aot_shared_library_path); @@ -217,8 +240,20 @@ blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), &settings.temp_directory_path); - command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath), - &settings.icu_data_path); + if (settings.icu_initialization_required) { + command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath), + &settings.icu_data_path); + if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) { + std::string icu_symbol_prefix, native_lib_path; + command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix), + &icu_symbol_prefix); + command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath), + &native_lib_path); + settings.icu_mapper = [icu_symbol_prefix, native_lib_path] { + return GetSymbolMapping(icu_symbol_prefix, native_lib_path); + }; + } + } settings.use_test_fonts = command_line.HasOption(FlagForSwitch(Switch::UseTestFonts)); @@ -237,8 +272,13 @@ blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE settings.trace_skia = command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); + settings.trace_systrace = + command_line.HasOption(FlagForSwitch(Switch::TraceSystrace)); #endif + settings.dump_skp_on_shader_compilation = + command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation)); + return settings; } diff --git a/shell/common/switches.h b/shell/common/switches.h index bf23d06175cd1..951bf7ad55b24 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -48,6 +48,13 @@ DEF_SWITCH(AotIsolateSnapshotInstructions, "read and executable. AotSnapshotPath must be present.") DEF_SWITCH(CacheDirPath, "cache-dir-path", "Path to the cache directory.") DEF_SWITCH(ICUDataFilePath, "icu-data-file-path", "Path to the ICU data file.") +DEF_SWITCH(ICUSymbolPrefix, + "icu-symbol-prefix", + "Prefix for the symbols representing ICU data linked into the " + "Flutter library.") +DEF_SWITCH(ICUNativeLibPath, + "icu-native-lib-path", + "Path to the library file that exports the ICU data.") DEF_SWITCH(DartFlags, "dart-flags", "Flags passed directly to the Dart VM without being interpreted " @@ -92,8 +99,6 @@ DEF_SWITCH(FlutterAssetsDir, "Path to the Flutter assets directory.") DEF_SWITCH(Help, "help", "Display this help text.") DEF_SWITCH(LogTag, "log-tag", "Tag associated with log messages.") -DEF_SWITCH(MainDartFile, "dart-main", "The path to the main Dart file.") -DEF_SWITCH(Packages, "packages", "Specify the path to the packages.") DEF_SWITCH(StartPaused, "start-paused", "Start the application paused in the Dart debugger.") @@ -104,8 +109,19 @@ DEF_SWITCH(TraceStartup, DEF_SWITCH(TraceSkia, "trace-skia", "Trace Skia calls. This is useful when debugging the GPU threed." - "By default, Skia tracing is not enable to reduce the number of " + "By default, Skia tracing is not enabled to reduce the number of " "traced events") +DEF_SWITCH(DumpSkpOnShaderCompilation, + "dump-skp-on-shader-compilation", + "Automatically dump the skp that triggers new shader compilations. " + "This is useful for writing custom ShaderWarmUp to reduce jank. " + "By default, this is not enabled to reduce the overhead. ") +DEF_SWITCH( + TraceSystrace, + "trace-systrace", + "Trace to the system tracer (instead of the timeline) on platforms where " + "such a tracer is available. Currently only supported on Android and " + "Fuchsia.") DEF_SWITCH(UseTestFonts, "use-test-fonts", "Running tests that layout and measure text will not yield " @@ -122,13 +138,13 @@ DEF_SWITCH(RunForever, "run-forever", "In non-interactive mode, keep the shell running after the Dart " "script has completed.") -DEF_SWITCH(DartNonCheckedMode, - "dart-non-checked-mode", - "Dart code runs in checked mode when the runtime mode is debug. In " - "profile and release product modes, the application code is " - "precompiled and checked mode is unsupported. However, this flag " - "may be specified if the user wishes to run in the debug product " - "mode (i.e. with JIT or DBC) with checked mode off.") +DEF_SWITCH(DisableDartAsserts, + "disable-dart-asserts", + "Dart code runs with assertions enabled when the runtime mode is " + "debug. In profile and release product modes, assertions are " + "disabled. This flag may be specified if the user wishes to run " + "with assertions disabled in the debug product mode (i.e. with JIT " + "or DBC).") DEF_SWITCHES_END void PrintUsage(const std::string& executable_name); diff --git a/shell/common/thread_host.cc b/shell/common/thread_host.cc index f35594829d5d9..d8a2a73006eb4 100644 --- a/shell/common/thread_host.cc +++ b/shell/common/thread_host.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,8 @@ namespace shell { ThreadHost::ThreadHost() = default; +ThreadHost::ThreadHost(ThreadHost&&) = default; + ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) { if (mask & ThreadHost::Type::Platform) { platform_thread = std::make_unique(name_prefix + ".platform"); diff --git a/shell/common/thread_host.h b/shell/common/thread_host.h index 4fbe0f130910d..9e09603095644 100644 --- a/shell/common/thread_host.h +++ b/shell/common/thread_host.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,7 +27,7 @@ struct ThreadHost { ThreadHost(); - ThreadHost(ThreadHost&&) = default; + ThreadHost(ThreadHost&&); ThreadHost& operator=(ThreadHost&&) = default; diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 1629db908a685..87ead7cc205cf 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,14 +9,42 @@ namespace shell { +#if defined(OS_FUCHSIA) +// In general, traces on Fuchsia are recorded across the whole system. +// Because of this, emitting a "VSYNC" event per flutter process is +// undesirable, as the events will collide with each other. We +// instead let another area of the system emit them. +static constexpr const char* kVsyncTraceName = "vsync callback"; +#else // defined(OS_FUCHSIA) +// Note: The tag name must be "VSYNC" (it is special) so that the +// "Highlight Vsync" checkbox in the timeline can be enabled. +static constexpr const char* kVsyncTraceName = "VSYNC"; +#endif // defined(OS_FUCHSIA) + +static constexpr const char* kVsyncFlowName = "VsyncFlow"; + VsyncWaiter::VsyncWaiter(blink::TaskRunners task_runners) : task_runners_(std::move(task_runners)) {} VsyncWaiter::~VsyncWaiter() = default; +// Public method invoked by the animator. void VsyncWaiter::AsyncWaitForVsync(Callback callback) { + if (!callback) { + return; + } + + TRACE_EVENT0("flutter", "AsyncWaitForVsync"); + { std::lock_guard lock(callback_mutex_); + if (callback_) { + // The animator may request a frame more than once within a frame + // interval. Multiple calls to request frame must result in a single + // callback per frame interval. + TRACE_EVENT_INSTANT0("flutter", "MultipleCallsToVsyncInFrameInterval"); + return; + } callback_ = std::move(callback); } AwaitVSync(); @@ -32,24 +60,38 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time, } if (!callback) { + // This means that the vsync waiter implementation fired a callback for a + // request we did not make. This is a paranoid check but we still want to + // make sure we catch misbehaving vsync implementations. + TRACE_EVENT_INSTANT0("flutter", "MismatchedFrameCallback"); return; } - task_runners_.GetUITaskRunner()->PostTask( - [callback, frame_start_time, frame_target_time]() { -#if defined(OS_FUCHSIA) - // In general, traces on Fuchsia are recorded across the whole system. - // Because of this, emitting a "VSYNC" event per flutter process is - // undesirable, as the events will collide with each other. We - // instead let another area of the system emit them. - TRACE_EVENT0("flutter", "vsync callback"); -#else - // Note: The tag name must be "VSYNC" (it is special) so that the - // "Highlight Vsync" checkbox in the timeline can be enabled. - TRACE_EVENT0("flutter", "VSYNC"); -#endif + auto flow_identifier = fml::tracing::TraceNonce(); + + // The base trace ensures that flows have a root to begin from if one does not + // exist. The trace viewer will ignore traces that have no base event trace. + // While all our message loops insert a base trace trace + // (MessageLoop::RunExpiredTasks), embedders may not. + TRACE_EVENT0("flutter", "VsyncFireCallback"); + + TRACE_FLOW_BEGIN("flutter", kVsyncFlowName, flow_identifier); + + task_runners_.GetUITaskRunner()->PostTaskForTime( + [callback, flow_identifier, frame_start_time, frame_target_time]() { + FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime", + frame_start_time, "TargetTime", frame_target_time); + fml::tracing::TraceEventAsyncComplete( + "flutter", "VsyncSchedulingOverhead", fml::TimePoint::Now(), + frame_start_time); callback(frame_start_time, frame_target_time); - }); + TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier); + }, + frame_start_time); +} + +float VsyncWaiter::GetDisplayRefreshRate() const { + return kUnknownRefreshRateFPS; } } // namespace shell diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index a787e3554f993..afb7d3cd36b72 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,7 @@ #include #include "flutter/common/task_runners.h" +#include "flutter/fml/synchronization/thread_annotations.h" #include "flutter/fml/time/time_point.h" namespace shell { @@ -23,18 +24,35 @@ class VsyncWaiter : public std::enable_shared_from_this { void AsyncWaitForVsync(Callback callback); - void FireCallback(fml::TimePoint frame_start_time, - fml::TimePoint frame_target_time); + static constexpr float kUnknownRefreshRateFPS = 0.0; + + // Get the display's maximum refresh rate in the unit of frame per second. + // Return kUnknownRefreshRateFPS if the refresh rate is unkonwn. + virtual float GetDisplayRefreshRate() const; protected: + // On some backends, the |FireCallback| needs to be made from a static C + // method. + friend class VsyncWaiterAndroid; + friend class VsyncWaiterEmbedder; + const blink::TaskRunners task_runners_; - std::mutex callback_mutex_; - Callback callback_; VsyncWaiter(blink::TaskRunners task_runners); + // Implementations are meant to override this method and arm their vsync + // latches when in response to this invocation. On vsync, they are meant to + // invoke the |FireCallback| method once (and only once) with the appropriate + // arguments. virtual void AwaitVSync() = 0; + void FireCallback(fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time); + + private: + std::mutex callback_mutex_; + Callback callback_ FML_GUARDED_BY(callback_mutex_); + FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiter); }; diff --git a/shell/common/vsync_waiter_fallback.cc b/shell/common/vsync_waiter_fallback.cc index f8e091495e59d..602770ba677ff 100644 --- a/shell/common/vsync_waiter_fallback.cc +++ b/shell/common/vsync_waiter_fallback.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,9 +9,9 @@ namespace shell { namespace { -fml::TimePoint SnapToNextTick(fml::TimePoint value, - fml::TimePoint tick_phase, - fml::TimeDelta tick_interval) { +static fml::TimePoint SnapToNextTick(fml::TimePoint value, + fml::TimePoint tick_phase, + fml::TimeDelta tick_interval) { fml::TimeDelta offset = (tick_phase - value) % tick_interval; if (offset != fml::TimeDelta::Zero()) offset = offset + tick_interval; @@ -21,27 +21,19 @@ fml::TimePoint SnapToNextTick(fml::TimePoint value, } // namespace VsyncWaiterFallback::VsyncWaiterFallback(blink::TaskRunners task_runners) - : VsyncWaiter(std::move(task_runners)), - phase_(fml::TimePoint::Now()), - weak_factory_(this) {} + : VsyncWaiter(std::move(task_runners)), phase_(fml::TimePoint::Now()) {} VsyncWaiterFallback::~VsyncWaiterFallback() = default; -constexpr fml::TimeDelta interval = fml::TimeDelta::FromSecondsF(1.0 / 60.0); - // |shell::VsyncWaiter| void VsyncWaiterFallback::AwaitVSync() { - fml::TimePoint now = fml::TimePoint::Now(); - fml::TimePoint next = SnapToNextTick(now, phase_, interval); - - task_runners_.GetUITaskRunner()->PostDelayedTask( - [self = weak_factory_.GetWeakPtr()] { - if (self) { - const auto frame_time = fml::TimePoint::Now(); - self->FireCallback(frame_time, frame_time + interval); - } - }, - next - now); + constexpr fml::TimeDelta kSingleFrameInterval = + fml::TimeDelta::FromSecondsF(1.0 / 60.0); + + auto next = + SnapToNextTick(fml::TimePoint::Now(), phase_, kSingleFrameInterval); + + FireCallback(next, next + kSingleFrameInterval); } } // namespace shell diff --git a/shell/common/vsync_waiter_fallback.h b/shell/common/vsync_waiter_fallback.h index 1289dd91bd532..b3ab8db9f43f2 100644 --- a/shell/common/vsync_waiter_fallback.h +++ b/shell/common/vsync_waiter_fallback.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,7 +20,6 @@ class VsyncWaiterFallback final : public VsyncWaiter { private: fml::TimePoint phase_; - fml::WeakPtrFactory weak_factory_; // |shell::VsyncWaiter| void AwaitVSync() override; diff --git a/shell/config.gni b/shell/config.gni index 0c31d3acd316f..3088b2cc6544a 100644 --- a/shell/config.gni +++ b/shell/config.gni @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/shell/gpu/BUILD.gn b/shell/gpu/BUILD.gn new file mode 100644 index 0000000000000..02130c85e186c --- /dev/null +++ b/shell/gpu/BUILD.gn @@ -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. + +import("$flutter_root/shell/config.gni") + +gpu_dir = "$flutter_root/shell/gpu" + +gpu_common_deps = [ + "$flutter_root/common", + "$flutter_root/flow", + "$flutter_root/fml", + "$flutter_root/shell/common", + "$flutter_root/synchronization", + "//third_party/skia", +] + +source_set("gpu_surface_software") { + sources = [ + "$gpu_dir/gpu_surface_software.cc", + "$gpu_dir/gpu_surface_software.h", + ] + + deps = gpu_common_deps +} + +source_set("gpu_surface_gl") { + sources = [ + "$gpu_dir/gpu_surface_gl.cc", + "$gpu_dir/gpu_surface_gl.h", + "$gpu_dir/gpu_surface_gl_delegate.cc", + "$gpu_dir/gpu_surface_gl_delegate.h", + ] + + deps = gpu_common_deps + [ "//third_party/skia" ] +} + +source_set("gpu_surface_vulkan") { + sources = [ + "$gpu_dir/gpu_surface_vulkan.cc", + "$gpu_dir/gpu_surface_vulkan.h", + ] + + deps = gpu_common_deps + [ + "//third_party/skia", + "$flutter_root/vulkan", + ] +} diff --git a/shell/gpu/gpu.gni b/shell/gpu/gpu.gni index 48cb476017509..3a5771f47a178 100644 --- a/shell/gpu/gpu.gni +++ b/shell/gpu/gpu.gni @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -12,51 +12,19 @@ template("shell_gpu_configuration") { assert(defined(invoker.enable_gl), "Caller must declare if the Open GL backend must be enabled.") - source_set(target_name) { - # vulkan_backend_enabled = shell_enable_vulkan - # gl_backend_enabled = !is_fuchsia - # software_backend_enabled = true - - sources = [] - - gpu_dir = "$flutter_root/shell/gpu" + group(target_name) { + public_deps = [] if (invoker.enable_software) { - sources += [ - "$gpu_dir/gpu_surface_software.cc", - "$gpu_dir/gpu_surface_software.h", - ] + public_deps += [ "$flutter_root/shell/gpu:gpu_surface_software" ] } if (invoker.enable_gl) { - sources += [ - "$gpu_dir/gpu_surface_gl.cc", - "$gpu_dir/gpu_surface_gl.h", - ] - } - - if (invoker.enable_vulkan) { - sources += [ - "$gpu_dir/gpu_surface_vulkan.cc", - "$gpu_dir/gpu_surface_vulkan.h", - ] - } - - deps = [ - "$flutter_root/common", - "$flutter_root/flow", - "$flutter_root/fml", - "$flutter_root/shell/common", - "$flutter_root/synchronization", - "//third_party/skia", - ] - - if (invoker.enable_vulkan || invoker.enable_gl) { - deps += [ "//third_party/skia:gpu" ] + public_deps += [ "$flutter_root/shell/gpu:gpu_surface_gl" ] } if (invoker.enable_vulkan) { - deps += [ "$flutter_root/vulkan" ] + public_deps += [ "$flutter_root/shell/gpu:gpu_surface_vulkan" ] } } } diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 153f68073ba0d..b6248fda4c5f4 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,8 +12,6 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContextOptions.h" -#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" // These are common defines present on all OpenGL headers. However, we don't // want to perform GL header reasolution on each platform we support. So just @@ -22,11 +20,6 @@ #define GPU_GL_RGBA8 0x8058 #define GPU_GL_RGBA4 0x8056 #define GPU_GL_RGB565 0x8D62 -#define GPU_GL_VERSION 0x1F02 - -#ifdef ERROR -#undef ERROR -#endif namespace shell { @@ -37,9 +30,6 @@ static const int kGrCacheMaxCount = 8192; // cache. static const size_t kGrCacheMaxByteSize = 512 * (1 << 20); -// Version string prefix that identifies an OpenGL ES implementation. -static const char kGLESVersionPrefix[] = "OpenGL ES"; - GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) : delegate_(delegate), weak_factory_(this) { if (!delegate_->GLContextMakeCurrent()) { @@ -48,8 +38,6 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) return; } - proc_resolver_ = delegate_->GetGLProcResolver(); - GrContextOptions options; options.fPersistentCache = PersistentCache::GetCacheForProcess(); @@ -60,26 +48,11 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) // ES2 shading language when the ES3 external image extension is missing. options.fPreferExternalImagesOverES3 = true; - sk_sp interface; - - if (proc_resolver_ == nullptr) { - interface = GrGLMakeNativeInterface(); - } else { - auto gl_get_proc = [](void* context, - const char gl_proc_name[]) -> GrGLFuncPtr { - return reinterpret_cast( - reinterpret_cast(context)->proc_resolver_( - gl_proc_name)); - }; - - if (IsProcResolverOpenGLES()) { - interface = GrGLMakeAssembledGLESInterface(this, gl_get_proc); - } else { - interface = GrGLMakeAssembledGLInterface(this, gl_get_proc); - } - } + // TODO(goderbauer): remove option when skbug.com/7523 is fixed. + // A similar work-around is also used in shell/common/io_manager.cc. + options.fDisableGpuYUVConversion = true; - auto context = GrContext::MakeGL(interface, options); + auto context = GrContext::MakeGL(delegate_->GetGLInterface(), options); if (context == nullptr) { FML_LOG(ERROR) << "Failed to setup Skia Gr context."; @@ -93,6 +66,22 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) delegate_->GLContextClearCurrent(); valid_ = true; + context_owner_ = true; +} + +GPUSurfaceGL::GPUSurfaceGL(sk_sp gr_context, + GPUSurfaceGLDelegate* delegate) + : delegate_(delegate), context_(gr_context), weak_factory_(this) { + if (!delegate_->GLContextMakeCurrent()) { + FML_LOG(ERROR) + << "Could not make the context current to setup the gr context."; + return; + } + + delegate_->GLContextClearCurrent(); + + valid_ = true; + context_owner_ = false; } GPUSurfaceGL::~GPUSurfaceGL() { @@ -107,26 +96,14 @@ GPUSurfaceGL::~GPUSurfaceGL() { } onscreen_surface_ = nullptr; - context_->releaseResourcesAndAbandonContext(); + if (context_owner_) { + context_->releaseResourcesAndAbandonContext(); + } context_ = nullptr; delegate_->GLContextClearCurrent(); } -bool GPUSurfaceGL::IsProcResolverOpenGLES() { - using GLGetStringProc = const char* (*)(uint32_t); - GLGetStringProc gl_get_string = - reinterpret_cast(proc_resolver_("glGetString")); - FML_CHECK(gl_get_string) - << "The GL proc resolver could not resolve glGetString"; - const char* gl_version_string = gl_get_string(GPU_GL_VERSION); - FML_CHECK(gl_version_string) - << "The GL proc resolver's glGetString(GL_VERSION) failed"; - - return strncmp(gl_version_string, kGLESVersionPrefix, - strlen(kGLESVersionPrefix)) == 0; -} - // |shell::Surface| bool GPUSurfaceGL::IsValid() { return valid_; @@ -162,7 +139,7 @@ static sk_sp WrapOnscreenSurface(GrContext* context, framebuffer_info // framebuffer info ); - sk_sp colorspace = nullptr; + sk_sp colorspace = SkColorSpace::MakeSRGB(); SkSurfaceProps surface_props( SkSurfaceProps::InitType::kLegacyFontHost_InitType); @@ -179,8 +156,8 @@ static sk_sp WrapOnscreenSurface(GrContext* context, static sk_sp CreateOffscreenSurface(GrContext* context, const SkISize& size) { - const SkImageInfo image_info = - SkImageInfo::MakeN32(size.fWidth, size.fHeight, kOpaque_SkAlphaType); + const SkImageInfo image_info = SkImageInfo::MakeN32( + size.fWidth, size.fHeight, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB()); const SkSurfaceProps surface_props( SkSurfaceProps::InitType::kLegacyFontHost_InitType); @@ -284,8 +261,10 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { if (offscreen_surface_ != nullptr) { TRACE_EVENT0("flutter", "CopyTextureOnscreen"); SkPaint paint; - onscreen_surface_->getCanvas()->drawImage( - offscreen_surface_->makeImageSnapshot(), 0, 0, &paint); + SkCanvas* onscreen_canvas = onscreen_surface_->getCanvas(); + onscreen_canvas->clear(SK_ColorTRANSPARENT); + onscreen_canvas->drawImage(offscreen_surface_->makeImageSnapshot(), 0, 0, + &paint); } { @@ -340,4 +319,14 @@ GrContext* GPUSurfaceGL::GetContext() { return context_.get(); } +// |shell::Surface| +flow::ExternalViewEmbedder* GPUSurfaceGL::GetExternalViewEmbedder() { + return delegate_->GetExternalViewEmbedder(); +} + +// |shell::Surface| +bool GPUSurfaceGL::MakeRenderContextCurrent() { + return delegate_->GLContextMakeCurrent(); +} + } // namespace shell diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 26184141bfd67..d9f85acedd365 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,42 +8,22 @@ #include #include +#include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { -class GPUSurfaceGLDelegate { - public: - virtual bool GLContextMakeCurrent() = 0; - - virtual bool GLContextClearCurrent() = 0; - - virtual bool GLContextPresent() = 0; - - virtual intptr_t GLContextFBO() const = 0; - - virtual bool GLContextFBOResetAfterPresent() const { return false; } - - virtual bool UseOffscreenSurface() const { return false; } - - virtual SkMatrix GLContextSurfaceTransformation() const { - SkMatrix matrix; - matrix.setIdentity(); - return matrix; - } - - using GLProcResolver = - std::function; - virtual GLProcResolver GetGLProcResolver() const { return nullptr; } -}; - class GPUSurfaceGL : public Surface { public: GPUSurfaceGL(GPUSurfaceGLDelegate* delegate); + // Creates a new GL surface reusing an existing GrContext. + GPUSurfaceGL(sk_sp gr_context, GPUSurfaceGLDelegate* delegate); + ~GPUSurfaceGL() override; // |shell::Surface| @@ -58,14 +38,20 @@ class GPUSurfaceGL : public Surface { // |shell::Surface| GrContext* GetContext() override; + // |shell::Surface| + flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + + // |shell::Surface| + bool MakeRenderContextCurrent() override; + private: GPUSurfaceGLDelegate* delegate_; - GPUSurfaceGLDelegate::GLProcResolver proc_resolver_; sk_sp context_; sk_sp onscreen_surface_; sk_sp offscreen_surface_; bool valid_ = false; fml::WeakPtrFactory weak_factory_; + bool context_owner_; bool CreateOrUpdateSurfaces(const SkISize& size); @@ -75,8 +61,6 @@ class GPUSurfaceGL : public Surface { bool PresentSurface(SkCanvas* canvas); - bool IsProcResolverOpenGLES(); - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceGL); }; diff --git a/shell/gpu/gpu_surface_gl_delegate.cc b/shell/gpu/gpu_surface_gl_delegate.cc new file mode 100644 index 0000000000000..1447a3de72a50 --- /dev/null +++ b/shell/gpu/gpu_surface_gl_delegate.cc @@ -0,0 +1,104 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" + +#include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h" + +namespace shell { + +bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const { + return false; +} + +bool GPUSurfaceGLDelegate::UseOffscreenSurface() const { + return false; +} + +SkMatrix GPUSurfaceGLDelegate::GLContextSurfaceTransformation() const { + SkMatrix matrix; + matrix.setIdentity(); + return matrix; +} + +flow::ExternalViewEmbedder* GPUSurfaceGLDelegate::GetExternalViewEmbedder() { + return nullptr; +} + +GPUSurfaceGLDelegate::GLProcResolver GPUSurfaceGLDelegate::GetGLProcResolver() + const { + return nullptr; +} + +static bool IsProcResolverOpenGLES( + GPUSurfaceGLDelegate::GLProcResolver proc_resolver) { + // Version string prefix that identifies an OpenGL ES implementation. +#define GPU_GL_VERSION 0x1F02 + constexpr char kGLESVersionPrefix[] = "OpenGL ES"; + + using GLGetStringProc = const char* (*)(uint32_t); + + GLGetStringProc gl_get_string = + reinterpret_cast(proc_resolver("glGetString")); + + FML_CHECK(gl_get_string) + << "The GL proc resolver could not resolve glGetString"; + + const char* gl_version_string = gl_get_string(GPU_GL_VERSION); + + FML_CHECK(gl_version_string) + << "The GL proc resolver's glGetString(GL_VERSION) failed"; + + return strncmp(gl_version_string, kGLESVersionPrefix, + strlen(kGLESVersionPrefix)) == 0; +} + +static sk_sp CreateGLInterface( + GPUSurfaceGLDelegate::GLProcResolver proc_resolver) { + if (proc_resolver == nullptr) { + // If there is no custom proc resolver, ask Skia to guess the native + // interface. This often leads to interesting results on most platforms. + return GrGLMakeNativeInterface(); + } + + struct ProcResolverContext { + GPUSurfaceGLDelegate::GLProcResolver resolver; + }; + + ProcResolverContext context = { + proc_resolver + }; + + GrGLGetProc gl_get_proc = [](void* context, + const char gl_proc_name[]) -> GrGLFuncPtr { + auto proc_resolver_context = + reinterpret_cast(context); + return reinterpret_cast( + proc_resolver_context->resolver(gl_proc_name)); + }; + + // glGetString indicates an OpenGL ES interface. + if (IsProcResolverOpenGLES(proc_resolver)) { + return GrGLMakeAssembledGLESInterface(&context, gl_get_proc); + } + + // Fallback to OpenGL. + if (auto interface = GrGLMakeAssembledGLInterface(&context, gl_get_proc)) { + return interface; + } + + FML_LOG(ERROR) << "Could not create a valid GL interface."; + return nullptr; +} + +sk_sp GPUSurfaceGLDelegate::GetGLInterface() const { + return CreateGLInterface(GetGLProcResolver()); +} + +sk_sp +GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface() { + return CreateGLInterface(nullptr); +} + +} // namespace shell diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h new file mode 100644 index 0000000000000..b290ae63e5950 --- /dev/null +++ b/shell/gpu/gpu_surface_gl_delegate.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_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_ +#define FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_ + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" + +namespace shell { + +class GPUSurfaceGLDelegate { + public: + // Called to make the main GL context current on the current thread. + virtual bool GLContextMakeCurrent() = 0; + + // Called to clear the current GL context on the thread. This may be called on + // either the GPU or IO threads. + virtual bool GLContextClearCurrent() = 0; + + // Called to present the main GL surface. This is only called for the main GL + // context and not any of the contexts dedicated for IO. + virtual bool GLContextPresent() = 0; + + // The ID of the main window bound framebuffer. Typically FBO0. + virtual intptr_t GLContextFBO() const = 0; + + // The rendering subsystem assumes that the ID of the main window bound + // framebuffer remains constant throughout. If this assumption in incorrect, + // embedders are required to return true from this method. In such cases, + // GLContextFBO() will be called again to acquire the new FBO ID for rendering + // subsequent frames. + virtual bool GLContextFBOResetAfterPresent() const; + + // Create an offscreen surface to render into before onscreen composition. + virtual bool UseOffscreenSurface() const; + + // A transformation applied to the onscreen surface before the canvas is + // flushed. + virtual SkMatrix GLContextSurfaceTransformation() const; + + // Get a reference to the external views embedder. This happens on the same + // thread that the renderer is operating on. + virtual flow::ExternalViewEmbedder* GetExternalViewEmbedder(); + + sk_sp GetGLInterface() const; + + // TODO(chinmaygarde): The presence of this method is to work around the fact + // that not all platforms can accept a custom GL proc table. Migrate all + // platforms to move GL proc resolution to the embedder and remove this + // method. + static sk_sp GetDefaultPlatformGLInterface(); + + using GLProcResolver = + std::function; + // Provide a custom GL proc resolver. If no such resolver is present, Skia + // will attempt to do GL proc address resolution on its own. Embedders that + // have specific opinions on GL API selection or need to add their own + // instrumentation to specific GL calls can specify custom GL functions + // here. + virtual GLProcResolver GetGLProcResolver() const; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_ diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index 7598aa243d33e..cea507c67bf01 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +9,11 @@ namespace shell { +flow::ExternalViewEmbedder* +GPUSurfaceSoftwareDelegate::GetExternalViewEmbedder() { + return nullptr; +} + GPUSurfaceSoftware::GPUSurfaceSoftware(GPUSurfaceSoftwareDelegate* delegate) : delegate_(delegate), weak_factory_(this) {} @@ -75,4 +80,9 @@ GrContext* GPUSurfaceSoftware::GetContext() { return nullptr; } +// |shell::Surface| +flow::ExternalViewEmbedder* GPUSurfaceSoftware::GetExternalViewEmbedder() { + return delegate_->GetExternalViewEmbedder(); +} + } // namespace shell diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index 95940c4702454..eb64920a33630 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -1,10 +1,11 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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_GPU_GPU_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ +#include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" @@ -17,6 +18,8 @@ class GPUSurfaceSoftwareDelegate { virtual sk_sp AcquireBackingStore(const SkISize& size) = 0; virtual bool PresentBackingStore(sk_sp backing_store) = 0; + + virtual flow::ExternalViewEmbedder* GetExternalViewEmbedder(); }; class GPUSurfaceSoftware : public Surface { @@ -37,6 +40,9 @@ class GPUSurfaceSoftware : public Surface { // |shell::Surface| GrContext* GetContext() override; + // |shell::Surface| + flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + private: GPUSurfaceSoftwareDelegate* delegate_; fml::WeakPtrFactory weak_factory_; diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index 728fca6a0028e..3c42bf0876adc 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index fe4a34b814240..ece0cbb81b97c 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/BUILD.gn b/shell/platform/BUILD.gn index ae3b1003f22fe..89b1bbabe6f4d 100644 --- a/shell/platform/BUILD.gn +++ b/shell/platform/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -12,11 +12,13 @@ group("platform") { "android", ] } else if (is_linux) { + deps = [] + } else if (is_win) { deps = [ - "embedder", + "windows", ] - } else if (is_win) { - # There is no platform target on windows. Instead, only a tester is used. + } else if ( is_fuchsia) { + # Fuchsia has its own runner implementation. deps = [] } else { assert(false, "Unknown/Unsupported platform.") diff --git a/shell/platform/android/.gitignore b/shell/platform/android/.gitignore index 2e8ec55e9bf6b..598adcf6cd3d0 100644 --- a/shell/platform/android/.gitignore +++ b/shell/platform/android/.gitignore @@ -1,2 +1,5 @@ # Generated by Intellij's Android plugin gen +android.iml +out/ + diff --git a/shell/platform/android/AndroidManifest.xml b/shell/platform/android/AndroidManifest.xml index 1599c802a6af2..7e10898269888 100644 --- a/shell/platform/android/AndroidManifest.xml +++ b/shell/platform/android/AndroidManifest.xml @@ -1,11 +1,11 @@ - - + diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index f332a60f7a97d..090fdb28ff694 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -49,10 +49,12 @@ shared_library("flutter_shell_native") { "platform_view_android_jni.h", "vsync_waiter_android.cc", "vsync_waiter_android.h", + "$root_build_dir/flutter_icu/icudtl.o", ] deps = [ ":android_gpu_configuration", + ":icudtl_object", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", @@ -61,7 +63,6 @@ shared_library("flutter_shell_native") { "$flutter_root/runtime", "$flutter_root/shell/common", "//third_party/skia", - "//third_party/skia:gpu", ] if (flutter_runtime_mode == "debug" || flutter_runtime_mode == "dynamic_profile" || @@ -70,6 +71,9 @@ shared_library("flutter_shell_native") { } else { deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] } + if (flutter_runtime_mode == "debug") { + deps += [ "$flutter_root/lib/snapshot" ] + } public_configs = [ "$flutter_root:config" ] @@ -91,6 +95,8 @@ shared_library("flutter_shell_native") { "EGL", "GLESv2", ] + + ldflags = ["-Wl,--version-script=" + rebase_path("android_exports.lst")] } java_library("flutter_shell_java") { @@ -103,10 +109,35 @@ java_library("flutter_shell_java") { "io/flutter/app/FlutterApplication.java", "io/flutter/app/FlutterFragmentActivity.java", "io/flutter/app/FlutterPluginRegistry.java", + "io/flutter/embedding/android/AndroidKeyProcessor.java", + "io/flutter/embedding/android/AndroidTouchProcessor.java", + "io/flutter/embedding/android/FlutterActivity.java", + "io/flutter/embedding/android/FlutterFragment.java", + "io/flutter/embedding/android/FlutterSurfaceView.java", + "io/flutter/embedding/android/FlutterTextureView.java", + "io/flutter/embedding/android/FlutterView.java", + "io/flutter/embedding/engine/FlutterEngine.java", + "io/flutter/embedding/engine/FlutterJNI.java", + "io/flutter/embedding/engine/FlutterShellArgs.java", + "io/flutter/embedding/engine/dart/DartExecutor.java", + "io/flutter/embedding/engine/dart/DartMessenger.java", + "io/flutter/embedding/engine/dart/PlatformMessageHandler.java", + "io/flutter/embedding/engine/renderer/FlutterRenderer.java", + "io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java", + "io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java", + "io/flutter/embedding/engine/systemchannels/KeyEventChannel.java", + "io/flutter/embedding/engine/systemchannels/LifecycleChannel.java", + "io/flutter/embedding/engine/systemchannels/LocalizationChannel.java", + "io/flutter/embedding/engine/systemchannels/NavigationChannel.java", + "io/flutter/embedding/engine/systemchannels/PlatformChannel.java", + "io/flutter/embedding/engine/systemchannels/SettingsChannel.java", + "io/flutter/embedding/engine/systemchannels/SystemChannel.java", + "io/flutter/embedding/engine/systemchannels/TextInputChannel.java", "io/flutter/plugin/common/ActivityLifecycleListener.java", "io/flutter/plugin/common/BasicMessageChannel.java", "io/flutter/plugin/common/BinaryCodec.java", "io/flutter/plugin/common/BinaryMessenger.java", + "io/flutter/plugin/common/ErrorLogResult.java", "io/flutter/plugin/common/EventChannel.java", "io/flutter/plugin/common/FlutterException.java", "io/flutter/plugin/common/JSONMessageCodec.java", @@ -122,17 +153,21 @@ java_library("flutter_shell_java") { "io/flutter/plugin/common/StringCodec.java", "io/flutter/plugin/editing/InputConnectionAdaptor.java", "io/flutter/plugin/editing/TextInputPlugin.java", + "io/flutter/plugin/platform/AccessibilityEventsDelegate.java", "io/flutter/plugin/platform/PlatformPlugin.java", "io/flutter/plugin/platform/PlatformView.java", "io/flutter/plugin/platform/PlatformViewFactory.java", "io/flutter/plugin/platform/PlatformViewRegistry.java", "io/flutter/plugin/platform/PlatformViewRegistryImpl.java", + "io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java", "io/flutter/plugin/platform/PlatformViewsController.java", "io/flutter/plugin/platform/SingleViewPresentation.java", "io/flutter/plugin/platform/VirtualDisplayController.java", "io/flutter/util/PathUtils.java", "io/flutter/util/Preconditions.java", + "io/flutter/util/Predicate.java", "io/flutter/view/AccessibilityBridge.java", + "io/flutter/view/AccessibilityViewEmbedder.java", "io/flutter/view/FlutterCallbackInformation.java", "io/flutter/view/FlutterMain.java", "io/flutter/view/FlutterNativeView.java", @@ -141,36 +176,82 @@ java_library("flutter_shell_java") { "io/flutter/view/ResourceCleaner.java", "io/flutter/view/ResourceExtractor.java", "io/flutter/view/ResourcePaths.java", + "io/flutter/view/ResourceUpdater.java", "io/flutter/view/TextureRegistry.java", "io/flutter/view/VsyncWaiter.java", ] + java_files += [ + "$flutter_root/third_party/bsdiff/io/flutter/util/BSDiff.java", + ] + deps = [ - ":android_support_v4", + ":android_support_v13", + ":android_support_annotations", + ":android_support_compat", + ":android_support_fragment", + ":android_arch_lifecycle_common", + ":android_arch_lifecycle_viewmodel", ] jar_path = "$root_out_dir/flutter_java.jar" } -java_prebuilt("android_support_v4") { +java_prebuilt("android_support_v13") { supports_android = true - jar_path = "//third_party/android_tools/sdk/extras/android/support/v4/android-support-v4.jar" + jar_path = "//third_party/android_support/android_support_v13.jar" } -copy("flutter_shell_assets") { - visibility = [ ":*" ] +java_prebuilt("android_support_compat") { + supports_android = true - sources = [ - "//third_party/icu/flutter/icudtl.dat", + jar_path = "//third_party/android_support/android_support_compat.jar" +} + +java_prebuilt("android_support_annotations") { + supports_android = true + + jar_path = "//third_party/android_support/android_support_annotations.jar" +} + +java_prebuilt("android_support_fragment") { + supports_android = true + + jar_path = "//third_party/android_support/android_support_fragment.jar" +} + +java_prebuilt("android_arch_lifecycle_common") { + supports_android = true + + jar_path = "//third_party/android_support/android_arch_lifecycle_common.jar" +} + +java_prebuilt("android_arch_lifecycle_viewmodel") { + supports_android = true + + jar_path = "//third_party/android_support/android_arch_lifecycle_viewmodel.jar" +} + +action("icudtl_object") { + script = "$flutter_root/sky/tools/objcopy.py" + + icudtl_input = "//third_party/icu/flutter/icudtl.dat" + icudtl_output = "$root_build_dir/flutter_icu/icudtl.o" + + inputs = [ + "$icudtl_input", ] outputs = [ - "$root_build_dir/flutter_shell_assets/{{source_file_part}}", + "$icudtl_output", ] - deps = [ - "//third_party/icu:icudata", + args = [ + "--objcopy", rebase_path(android_objcopy), + "--input", rebase_path(icudtl_input), + "--output", rebase_path(icudtl_output), + "--arch", current_cpu, ] } @@ -180,7 +261,6 @@ action("android") { inputs = [ "$root_build_dir/flutter_java.jar", "$root_build_dir/lib.stripped/libflutter.so", - "$root_build_dir/flutter_shell_assets/icudtl.dat", ] outputs = [ @@ -192,8 +272,6 @@ action("android") { rebase_path("flutter.jar", root_build_dir, root_build_dir), "--dist_jar", rebase_path("flutter_java.jar", root_build_dir, root_build_dir), - "--asset_dir", - rebase_path("flutter_shell_assets", root_build_dir, root_build_dir), "--native_lib", rebase_path("lib.stripped/libflutter.so", root_build_dir, root_build_dir), "--android_abi", @@ -201,7 +279,6 @@ action("android") { ] deps = [ - ":flutter_shell_assets", ":flutter_shell_java", ":flutter_shell_native", ] diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index b61f4ec7433c8..8ac43b4031a48 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index 7b420211f34df..e29dc7ac4f69d 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_environment_gl.cc b/shell/platform/android/android_environment_gl.cc index 0482367823672..3d9f919bb0110 100644 --- a/shell/platform/android/android_environment_gl.cc +++ b/shell/platform/android/android_environment_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_environment_gl.h b/shell/platform/android/android_environment_gl.h index 3b9124e275de4..710064e75da12 100644 --- a/shell/platform/android/android_environment_gl.h +++ b/shell/platform/android/android_environment_gl.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_exports.lst b/shell/platform/android/android_exports.lst new file mode 100644 index 0000000000000..3bea8e22e3814 --- /dev/null +++ b/shell/platform/android/android_exports.lst @@ -0,0 +1,14 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Linker script that exports the minimal symbols required for libflutter.so + +{ + global: + JNI_OnLoad; + _binary_icudtl_dat_start; + _binary_icudtl_dat_size; + local: + *; +}; diff --git a/shell/platform/android/android_external_texture_gl.cc b/shell/platform/android/android_external_texture_gl.cc index f644376e5fdd4..0d21841338a37 100644 --- a/shell/platform/android/android_external_texture_gl.cc +++ b/shell/platform/android/android_external_texture_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,7 +7,7 @@ #include #include "flutter/shell/platform/android/platform_view_android_jni.h" -#include "third_party/skia/include/gpu/GrTexture.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" namespace shell { @@ -16,7 +16,11 @@ AndroidExternalTextureGL::AndroidExternalTextureGL( const fml::jni::JavaObjectWeakGlobalRef& surfaceTexture) : Texture(id), surface_texture_(surfaceTexture), transform(SkMatrix::I()) {} -AndroidExternalTextureGL::~AndroidExternalTextureGL() = default; +AndroidExternalTextureGL::~AndroidExternalTextureGL() { + if (state_ == AttachmentState::attached) { + glDeleteTextures(1, &texture_name_); + } +} void AndroidExternalTextureGL::OnGrContextCreated() { state_ = AttachmentState::uninitialized; diff --git a/shell/platform/android/android_external_texture_gl.h b/shell/platform/android/android_external_texture_gl.h index b96eb33191f5d..09a56766e2a8f 100644 --- a/shell/platform/android/android_external_texture_gl.h +++ b/shell/platform/android/android_external_texture_gl.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,13 +19,11 @@ class AndroidExternalTextureGL : public flow::Texture { ~AndroidExternalTextureGL() override; - virtual void Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze) override; + void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze) override; - virtual void OnGrContextCreated() override; + void OnGrContextCreated() override; - virtual void OnGrContextDestroyed() override; + void OnGrContextDestroyed() override; void MarkNewFrameAvailable() override; diff --git a/shell/platform/android/android_native_window.cc b/shell/platform/android/android_native_window.cc index d84e078857ac3..af8b179d1b094 100644 --- a/shell/platform/android/android_native_window.cc +++ b/shell/platform/android/android_native_window.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_native_window.h b/shell/platform/android/android_native_window.h index 3337ba228107c..04938c59630b8 100644 --- a/shell/platform/android/android_native_window.h +++ b/shell/platform/android/android_native_window.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index f7df86addccc6..06a3697da36ce 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -192,12 +192,18 @@ void AndroidShellHolder::DispatchPointerDataPacket( return; } + TRACE_EVENT0("flutter", "AndroidShellHolder::DispatchPointerDataPacket"); + TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = shell_->GetEngine(), packet = std::move(packet)] { + [engine = shell_->GetEngine(), packet = std::move(packet), + flow_id = next_pointer_flow_id_] { if (engine) { - engine->DispatchPointerDataPacket(*packet); + engine->DispatchPointerDataPacket(*packet, flow_id); } })); + + next_pointer_flow_id_++; } Rasterizer::Screenshot AndroidShellHolder::Screenshot( diff --git a/shell/platform/android/android_shell_holder.h b/shell/platform/android/android_shell_holder.h index e401816d3da7c..d01cc5d43d5e4 100644 --- a/shell/platform/android/android_shell_holder.h +++ b/shell/platform/android/android_shell_holder.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -52,6 +52,7 @@ class AndroidShellHolder { std::unique_ptr shell_; bool is_valid_ = false; pthread_key_t thread_destruct_key_; + uint64_t next_pointer_flow_id_; static void ThreadDestructCallback(void* value); diff --git a/shell/platform/android/android_surface.cc b/shell/platform/android/android_surface.cc index a8b41bacbf7c5..cb1ee518ab941 100644 --- a/shell/platform/android/android_surface.cc +++ b/shell/platform/android/android_surface.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_surface.h b/shell/platform/android/android_surface.h index e0a413c1e0dc2..0ecc9ffc76bc1 100644 --- a/shell/platform/android/android_surface.h +++ b/shell/platform/android/android_surface.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 207d2631a0404..4e1ff9f41cd6f 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 5562523799096..5c6f4aa65eb73 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -61,7 +61,6 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, private: fml::RefPtr onscreen_context_; fml::RefPtr offscreen_context_; - sk_sp gr_context_; FML_DISALLOW_COPY_AND_ASSIGN(AndroidSurfaceGL); }; diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index a577ade5c91fb..c412e70f963d4 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -83,8 +83,9 @@ sk_sp AndroidSurfaceSoftware::AcquireBackingStore( return sk_surface_; } - SkImageInfo image_info = SkImageInfo::Make( - size.fWidth, size.fHeight, target_color_type_, target_alpha_type_); + SkImageInfo image_info = + SkImageInfo::Make(size.fWidth, size.fHeight, target_color_type_, + target_alpha_type_, SkColorSpace::MakeSRGB()); sk_surface_ = SkSurface::MakeRaster(image_info); diff --git a/shell/platform/android/android_surface_software.h b/shell/platform/android/android_surface_software.h index 6301515ca4254..c7410642cb22d 100644 --- a/shell/platform/android/android_surface_software.h +++ b/shell/platform/android/android_surface_software.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_surface_vulkan.cc b/shell/platform/android/android_surface_vulkan.cc index 05848f042715c..7753a42081550 100644 --- a/shell/platform/android/android_surface_vulkan.cc +++ b/shell/platform/android/android_surface_vulkan.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/android_surface_vulkan.h b/shell/platform/android/android_surface_vulkan.h index 3bec57febc502..ef6eec003a431 100644 --- a/shell/platform/android/android_surface_vulkan.h +++ b/shell/platform/android/android_surface_vulkan.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/apk_asset_provider.h b/shell/platform/android/apk_asset_provider.h index e425ad205c575..be41a685ebfeb 100644 --- a/shell/platform/android/apk_asset_provider.h +++ b/shell/platform/android/apk_asset_provider.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,7 @@ class APKAssetProvider final : public AssetResolver { explicit APKAssetProvider(JNIEnv* env, jobject assetManager, std::string directory); - virtual ~APKAssetProvider(); + ~APKAssetProvider() override; private: fml::jni::ScopedJavaGlobalRef java_asset_manager_; diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 6311ed72a2b2a..e02eae9faba7a 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -25,6 +25,14 @@ namespace shell { +extern "C" { +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +// Used for debugging dart:* sources. +extern const uint8_t kPlatformStrongDill[]; +extern const intptr_t kPlatformStrongDillSize; +#endif +} + FlutterMain::FlutterMain(blink::Settings settings) : settings_(std::move(settings)) {} @@ -89,6 +97,20 @@ void FlutterMain::Init(JNIEnv* env, settings.task_observer_remove = [](intptr_t key) { fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); }; + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + // There are no ownership concerns here as all mappings are owned by the + // embedder and not the engine. + auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { + return [mapping, size]() { + return std::make_unique(mapping, size); + }; + }; + + settings.dart_library_sources_kernel = + make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); +#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + // Not thread safe. Will be removed when FlutterMain is refactored to no // longer be a singleton. g_flutter_main.reset(new FlutterMain(std::move(settings))); diff --git a/shell/platform/android/flutter_main.h b/shell/platform/android/flutter_main.h index 7d738a15b9c7f..4ce8e49658662 100644 --- a/shell/platform/android/flutter_main.h +++ b/shell/platform/android/flutter_main.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/app/FlutterActivity.java b/shell/platform/android/io/flutter/app/FlutterActivity.java index 9751ef4a009ec..7b8052b261df4 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivity.java +++ b/shell/platform/android/io/flutter/app/FlutterActivity.java @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +9,8 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.util.Log; import io.flutter.app.FlutterActivityDelegate.ViewFactory; import io.flutter.plugin.common.PluginRegistry; import io.flutter.view.FlutterNativeView; @@ -18,6 +20,8 @@ * Base class for activities that use Flutter. */ public class FlutterActivity extends Activity implements FlutterView.Provider, PluginRegistry, ViewFactory { + private static final String TAG = "FlutterActivity"; + private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this); // These aliases ensure that the methods we forward to the delegate adhere @@ -109,7 +113,7 @@ public void onBackPressed() { super.onBackPressed(); } } - + @Override protected void onStop() { eventDelegate.onStop(); @@ -129,7 +133,7 @@ protected void onPostResume() { } // @Override - added in API level 23 - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); } diff --git a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java index 5c5c367723272..c56908db643b3 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,7 @@ import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.app.Application; +import android.app.UiModeManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -32,7 +33,10 @@ import io.flutter.view.FlutterNativeView; import io.flutter.view.FlutterRunArguments; import io.flutter.view.FlutterView; +import io.flutter.view.ResourceUpdater; +import org.json.JSONObject; +import java.io.File; import java.util.ArrayList; /** @@ -166,14 +170,10 @@ public void onCreate(Bundle savedInstanceState) { if (loadIntent(activity.getIntent())) { return; } - if (!flutterView.getFlutterNativeView().isApplicationRunning()) { - String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext()); - if (appBundlePath != null) { - FlutterRunArguments arguments = new FlutterRunArguments(); - arguments.bundlePath = appBundlePath; - arguments.entrypoint = "main"; - flutterView.runFromBundle(arguments); - } + + String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext()); + if (appBundlePath != null) { + runBundle(appBundlePath); } } @@ -214,6 +214,7 @@ public void onStart() { @Override public void onResume() { Application app = (Application) activity.getApplicationContext(); + FlutterMain.onResume(app); if (app instanceof FlutterApplication) { FlutterApplication flutterApp = (FlutterApplication) app; flutterApp.setCurrentActivity(activity); @@ -283,14 +284,13 @@ public void onLowMemory() { } @Override - public void onConfigurationChanged(Configuration newConfig) { - } + public void onConfigurationChanged(Configuration newConfig) {} private static String[] getArgsFromIntent(Intent intent) { // Before adding more entries to this list, consider that arbitrary // Android applications can generate intents with extra data and that // there are many security-sensitive args in the binary. - ArrayList args = new ArrayList(); + ArrayList args = new ArrayList<>(); if (intent.getBooleanExtra("trace-startup", false)) { args.add("--trace-startup"); } @@ -312,6 +312,12 @@ private static String[] getArgsFromIntent(Intent intent) { if (intent.getBooleanExtra("trace-skia", false)) { args.add("--trace-skia"); } + if (intent.getBooleanExtra("trace-systrace", false)) { + args.add("--trace-systrace"); + } + if (intent.getBooleanExtra("dump-skp-on-shader-compilation", false)) { + args.add("--dump-skp-on-shader-compilation"); + } if (intent.getBooleanExtra("verbose-logging", false)) { args.add("--verbose-logging"); } @@ -328,25 +334,39 @@ private boolean loadIntent(Intent intent) { String route = intent.getStringExtra("route"); String appBundlePath = intent.getDataString(); if (appBundlePath == null) { - // Fall back to the installation path if no bundle path - // was specified. + // Fall back to the installation path if no bundle path was specified. appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext()); } if (route != null) { flutterView.setInitialRoute(route); } - if (!flutterView.getFlutterNativeView().isApplicationRunning()) { - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = appBundlePath; - args.entrypoint = "main"; - flutterView.runFromBundle(args); - } + + runBundle(appBundlePath); return true; } return false; } + private void runBundle(String appBundlePath) { + if (!flutterView.getFlutterNativeView().isApplicationRunning()) { + FlutterRunArguments args = new FlutterRunArguments(); + ArrayList bundlePaths = new ArrayList<>(); + ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater(); + if (resourceUpdater != null) { + File patchFile = resourceUpdater.getInstalledPatch(); + JSONObject manifest = resourceUpdater.readManifest(patchFile); + if (resourceUpdater.validateManifest(manifest)) { + bundlePaths.add(patchFile.getPath()); + } + } + bundlePaths.add(appBundlePath); + args.bundlePaths = bundlePaths.toArray(new String[0]); + args.entrypoint = "main"; + flutterView.runFromBundle(args); + } + } + /** * Creates a {@link View} containing the same {@link Drawable} as the one set as the * {@code windowBackground} of the parent activity for use as a launch splash view. @@ -382,7 +402,7 @@ private Drawable getLaunchScreenDrawableFromActivityTheme() { if (!activity.getTheme().resolveAttribute( android.R.attr.windowBackground, typedValue, - true)) {; + true)) { return null; } if (typedValue.resourceId == 0) { diff --git a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java index a9a51d7a7bfcb..69e783f59db19 100644 --- a/shell/platform/android/io/flutter/app/FlutterActivityEvents.java +++ b/shell/platform/android/io/flutter/app/FlutterActivityEvents.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -63,7 +63,7 @@ public interface FlutterActivityEvents * @see android.app.Activity#onStop() */ void onStop(); - + /** * Invoked when the activity has detected the user's press of the back key. * diff --git a/shell/platform/android/io/flutter/app/FlutterApplication.java b/shell/platform/android/io/flutter/app/FlutterApplication.java index d25d68eda35f9..5ac61fd449f1f 100644 --- a/shell/platform/android/io/flutter/app/FlutterApplication.java +++ b/shell/platform/android/io/flutter/app/FlutterApplication.java @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java index 1ecd8857666bd..2aa0d131f8877 100644 --- a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -101,6 +101,12 @@ public void onBackPressed() { super.onBackPressed(); } } + + @Override + protected void onStart() { + super.onStart(); + eventDelegate.onStart(); + } @Override protected void onStop() { diff --git a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java index 4a8617ff2f3a6..13297ae5921ab 100644 --- a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,6 +7,8 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; + +import io.flutter.embedding.engine.FlutterEngine; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.PluginRegistry; import io.flutter.plugin.platform.PlatformViewRegistry; @@ -49,6 +51,12 @@ public FlutterPluginRegistry(FlutterNativeView nativeView, Context context) { mPlatformViewsController = new PlatformViewsController(); } + public FlutterPluginRegistry(FlutterEngine engine, Context context) { + // TODO(mattcarroll): implement use of engine instead of nativeView. + mAppContext = context; + mPlatformViewsController = new PlatformViewsController(); + } + @Override public boolean hasPlugin(String key) { return mPluginMap.containsKey(key); @@ -86,6 +94,10 @@ public void onPreEngineRestart() { mPlatformViewsController.onPreEngineRestart(); } + public PlatformViewsController getPlatformViewsController() { + return mPlatformViewsController; + } + private class FlutterRegistrar implements Registrar { private final String pluginKey; diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java new file mode 100644 index 0000000000000..9ceb3900506e4 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java @@ -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. + +package io.flutter.embedding.android; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; + +import io.flutter.embedding.engine.systemchannels.KeyEventChannel; +import io.flutter.plugin.editing.TextInputPlugin; + +public class AndroidKeyProcessor { + private final KeyEventChannel keyEventChannel; + private final TextInputPlugin textInputPlugin; + private int combiningCharacter; + + public AndroidKeyProcessor(@NonNull KeyEventChannel keyEventChannel, @NonNull TextInputPlugin textInputPlugin) { + this.keyEventChannel = keyEventChannel; + this.textInputPlugin = textInputPlugin; + } + + public void onKeyUp(@NonNull KeyEvent keyEvent) { + Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar()); + keyEventChannel.keyUp( + new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter) + ); + } + + public void onKeyDown(@NonNull KeyEvent keyEvent) { + if (keyEvent.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { + if (textInputPlugin.getLastInputConnection() != null + && textInputPlugin.getInputMethodManager().isAcceptingText()) { + textInputPlugin.getLastInputConnection().sendKeyEvent(keyEvent); + } + } + + Character complexCharacter = applyCombiningCharacterToBaseCharacter(keyEvent.getUnicodeChar()); + keyEventChannel.keyDown( + new KeyEventChannel.FlutterKeyEvent(keyEvent, complexCharacter) + ); + } + + /** + * Applies the given Unicode character in {@code newCharacterCodePoint} to a previously + * entered Unicode combining character and returns the combination of these characters + * if a combination exists. + *

+ * This method mutates {@link #combiningCharacter} over time to combine characters. + *

+ * One of the following things happens in this method: + *

    + *
  • If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint} + * is not a combining character, then {@code newCharacterCodePoint} is returned.
  • + *
  • If no previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint} + * is a combining character, then {@code newCharacterCodePoint} is saved as the + * {@link #combiningCharacter} and null is returned.
  • + *
  • If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint} + * is also a combining character, then the {@code newCharacterCodePoint} is combined with + * the existing {@link #combiningCharacter} and null is returned.
  • + *
  • If a previous {@link #combiningCharacter} exists and the {@code newCharacterCodePoint} + * is not a combining character, then the {@link #combiningCharacter} is applied to the + * regular {@code newCharacterCodePoint} and the resulting complex character is returned. The + * {@link #combiningCharacter} is cleared.
  • + *
+ *

+ * The following reference explains the concept of a "combining character": + * https://en.wikipedia.org/wiki/Combining_character + */ + @Nullable + private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoint) { + if (newCharacterCodePoint == 0) { + return null; + } + + Character complexCharacter = (char) newCharacterCodePoint; + boolean isNewCodePointACombiningCharacter = (newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT) != 0; + if (isNewCodePointACombiningCharacter) { + // If a combining character was entered before, combine this one with that one. + int plainCodePoint = newCharacterCodePoint & KeyCharacterMap.COMBINING_ACCENT_MASK; + if (combiningCharacter != 0) { + combiningCharacter = KeyCharacterMap.getDeadChar(combiningCharacter, plainCodePoint); + } else { + combiningCharacter = plainCodePoint; + } + } else { + // The new character is a regular character. Apply combiningCharacter to it, if it exists. + if (combiningCharacter != 0) { + int combinedChar = KeyCharacterMap.getDeadChar(combiningCharacter, newCharacterCodePoint); + if (combinedChar > 0) { + complexCharacter = (char) combinedChar; + } + combiningCharacter = 0; + } + } + + return complexCharacter; + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java new file mode 100644 index 0000000000000..ce69702143551 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java @@ -0,0 +1,305 @@ +package io.flutter.embedding.android; + +import android.os.Build; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.view.InputDevice; +import android.view.MotionEvent; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +/** + * Sends touch information from Android to Flutter in a format that Flutter + * understands. + */ +public class AndroidTouchProcessor { + + // Must match the PointerChange enum in pointer.dart. + @IntDef({ + PointerChange.CANCEL, + PointerChange.ADD, + PointerChange.REMOVE, + PointerChange.HOVER, + PointerChange.DOWN, + PointerChange.MOVE, + PointerChange.UP + }) + private @interface PointerChange { + int CANCEL = 0; + int ADD = 1; + int REMOVE = 2; + int HOVER = 3; + int DOWN = 4; + int MOVE = 5; + int UP = 6; + } + + // Must match the PointerDeviceKind enum in pointer.dart. + @IntDef({ + PointerDeviceKind.TOUCH, + PointerDeviceKind.MOUSE, + PointerDeviceKind.STYLUS, + PointerDeviceKind.INVERTED_STYLUS, + PointerDeviceKind.UNKNOWN + }) + private @interface PointerDeviceKind { + int TOUCH = 0; + int MOUSE = 1; + int STYLUS = 2; + int INVERTED_STYLUS = 3; + int UNKNOWN = 4; + } + + // Must match the PointerSignalKind enum in pointer.dart. + @IntDef({ + PointerSignalKind.NONE, + PointerSignalKind.SCROLL, + PointerSignalKind.UNKNOWN + }) + private @interface PointerSignalKind { + int NONE = 0; + int SCROLL = 1; + int UNKNOWN = 2; + } + + // Must match the unpacking code in hooks.dart. + private static final int POINTER_DATA_FIELD_COUNT = 24; + private static final int BYTES_PER_FIELD = 8; + + // This value must match the value in framework's platform_view.dart. + // This flag indicates whether the original Android pointer events were batched together. + private static final int POINTER_DATA_FLAG_BATCHED = 1; + + @NonNull + private final FlutterRenderer renderer; + + /** + * Constructs an {@code AndroidTouchProcessor} that will send touch event data + * to the Flutter execution context represented by the given {@link FlutterRenderer}. + */ + // TODO(mattcarroll): consider moving packet behavior to a FlutterInteractionSurface instead of FlutterRenderer + public AndroidTouchProcessor(@NonNull FlutterRenderer renderer) { + this.renderer = renderer; + } + + /** + * Sends the given {@link MotionEvent} data to Flutter in a format that + * Flutter understands. + */ + public boolean onTouchEvent(MotionEvent event) { + int pointerCount = event.getPointerCount(); + + // Prepare a data packet of the appropriate size and order. + ByteBuffer packet = ByteBuffer.allocateDirect( + pointerCount * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD + ); + packet.order(ByteOrder.LITTLE_ENDIAN); + + int maskedAction = event.getActionMasked(); + int pointerChange = getPointerChangeForAction(event.getActionMasked()); + boolean updateForSinglePointer = maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN; + boolean updateForMultiplePointers = !updateForSinglePointer && (maskedAction == MotionEvent.ACTION_UP || maskedAction == MotionEvent.ACTION_POINTER_UP); + if (updateForSinglePointer) { + // ACTION_DOWN and ACTION_POINTER_DOWN always apply to a single pointer only. + addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet); + } else if (updateForMultiplePointers) { + // ACTION_UP and ACTION_POINTER_UP may contain position updates for other pointers. + // We are converting these updates to move events here in order to preserve this data. + // We also mark these events with a flag in order to help the framework reassemble + // the original Android event later, should it need to forward it to a PlatformView. + for (int p = 0; p < pointerCount; p++) { + if (p != event.getActionIndex() && event.getToolType(p) == MotionEvent.TOOL_TYPE_FINGER) { + addPointerForIndex(event, p, PointerChange.MOVE, POINTER_DATA_FLAG_BATCHED, packet); + } + } + // It's important that we're sending the UP event last. This allows PlatformView + // to correctly batch everything back into the original Android event if needed. + addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet); + } else { + // ACTION_MOVE may not actually mean all pointers have moved + // but it's the responsibility of a later part of the system to + // ignore 0-deltas if desired. + for (int p = 0; p < pointerCount; p++) { + addPointerForIndex(event, p, pointerChange, 0, packet); + } + } + + // Verify that the packet is the expected size. + if (packet.position() % (POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD) != 0) { + throw new AssertionError("Packet position is not on field boundary"); + } + + // Send the packet to Flutter. + renderer.dispatchPointerDataPacket(packet, packet.position()); + + return true; + } + + /** + * Sends the given generic {@link MotionEvent} data to Flutter in a format that Flutter + * understands. + * + * Generic motion events include joystick movement, mouse hover, track pad touches, scroll wheel + * movements, etc. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + // Method isFromSource is only available in API 18+ (Jelly Bean MR2) + // Mouse hover support is not implemented for API < 18. + boolean isPointerEvent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 + && event.isFromSource(InputDevice.SOURCE_CLASS_POINTER); + boolean isMovementEvent = (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE + || event.getActionMasked() == MotionEvent.ACTION_SCROLL); + if (!isPointerEvent || !isMovementEvent) { + return false; + } + + int pointerChange = getPointerChangeForAction(event.getActionMasked()); + ByteBuffer packet = ByteBuffer.allocateDirect( + event.getPointerCount() * POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD + ); + packet.order(ByteOrder.LITTLE_ENDIAN); + + // ACTION_HOVER_MOVE always applies to a single pointer only. + addPointerForIndex(event, event.getActionIndex(), pointerChange, 0, packet); + if (packet.position() % (POINTER_DATA_FIELD_COUNT * BYTES_PER_FIELD) != 0) { + throw new AssertionError("Packet position is not on field boundary."); + } + renderer.dispatchPointerDataPacket(packet, packet.position()); + return true; + } + + // TODO(mattcarroll): consider creating a PointerPacket class instead of using a procedure that mutates inputs. + private void addPointerForIndex( + MotionEvent event, + int pointerIndex, + int pointerChange, + int pointerData, + ByteBuffer packet + ) { + if (pointerChange == -1) { + return; + } + + int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex)); + + int signalKind = event.getActionMasked() == MotionEvent.ACTION_SCROLL + ? PointerSignalKind.SCROLL + : PointerSignalKind.NONE; + + long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. + + packet.putLong(timeStamp); // time_stamp + packet.putLong(pointerChange); // change + packet.putLong(pointerKind); // kind + packet.putLong(signalKind); // signal_kind + packet.putLong(event.getPointerId(pointerIndex)); // device + packet.putDouble(event.getX(pointerIndex)); // physical_x + packet.putDouble(event.getY(pointerIndex)); // physical_y + + if (pointerKind == PointerDeviceKind.MOUSE) { + packet.putLong(event.getButtonState() & 0x1F); // buttons + } else if (pointerKind == PointerDeviceKind.STYLUS) { + packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons + } else { + packet.putLong(0); // buttons + } + + packet.putLong(0); // obscured + + packet.putDouble(event.getPressure(pointerIndex)); // pressure + double pressureMin = 0.0; + double pressureMax = 1.0; + if (event.getDevice() != null) { + InputDevice.MotionRange pressureRange = event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE); + if (pressureRange != null) { + pressureMin = pressureRange.getMin(); + pressureMax = pressureRange.getMax(); + } + } + packet.putDouble(pressureMin); // pressure_min + packet.putDouble(pressureMax); // pressure_max + + if (pointerKind == PointerDeviceKind.STYLUS) { + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance + packet.putDouble(0.0); // distance_max + } else { + packet.putDouble(0.0); // distance + packet.putDouble(0.0); // distance_max + } + + packet.putDouble(event.getSize(pointerIndex)); // size + + packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major + packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor + + packet.putDouble(0.0); // radius_min + packet.putDouble(0.0); // radius_max + + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation + + if (pointerKind == PointerDeviceKind.STYLUS) { + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt + } else { + packet.putDouble(0.0); // tilt + } + + packet.putLong(pointerData); // platformData + + if (signalKind == PointerSignalKind.SCROLL) { + packet.putDouble(-event.getAxisValue(MotionEvent.AXIS_HSCROLL)); // scroll_delta_x + packet.putDouble(-event.getAxisValue(MotionEvent.AXIS_VSCROLL)); // scroll_delta_y + } else { + packet.putDouble(0.0); // scroll_delta_x + packet.putDouble(0.0); // scroll_delta_x + } + } + + @PointerChange + private int getPointerChangeForAction(int maskedAction) { + // Primary pointer: + if (maskedAction == MotionEvent.ACTION_DOWN) { + return PointerChange.DOWN; + } + if (maskedAction == MotionEvent.ACTION_UP) { + return PointerChange.UP; + } + // Secondary pointer: + if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) { + return PointerChange.DOWN; + } + if (maskedAction == MotionEvent.ACTION_POINTER_UP) { + return PointerChange.UP; + } + // All pointers: + if (maskedAction == MotionEvent.ACTION_MOVE) { + return PointerChange.MOVE; + } + if (maskedAction == MotionEvent.ACTION_CANCEL) { + return PointerChange.CANCEL; + } + if (maskedAction == MotionEvent.ACTION_SCROLL) { + return PointerChange.HOVER; + } + return -1; + } + + @PointerDeviceKind + private int getPointerDeviceTypeForToolType(int toolType) { + switch (toolType) { + case MotionEvent.TOOL_TYPE_FINGER: + return PointerDeviceKind.TOUCH; + case MotionEvent.TOOL_TYPE_STYLUS: + return PointerDeviceKind.STYLUS; + case MotionEvent.TOOL_TYPE_MOUSE: + return PointerDeviceKind.MOUSE; + case MotionEvent.TOOL_TYPE_ERASER: + return PointerDeviceKind.INVERTED_STYLUS; + default: + // MotionEvent.TOOL_TYPE_UNKNOWN will reach here. + return PointerDeviceKind.UNKNOWN; + } + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java new file mode 100644 index 0000000000000..2ce499870053c --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -0,0 +1,336 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.FlutterShellArgs; +import io.flutter.plugin.platform.PlatformPlugin; +import io.flutter.view.FlutterMain; + +/** + * {@code Activity} which displays a fullscreen Flutter UI. + *

+ * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + *

+ * {@code FlutterActivity} is the simplest and most direct way to integrate Flutter within an + * Android app. + *

+ * The Dart entrypoint executed within this {@code Activity} is "main()" by default. The entrypoint + * may be specified explicitly by passing the name of the entrypoint method as a {@code String} in + * {@link #EXTRA_DART_ENTRYPOINT}, e.g., "myEntrypoint". + *

+ * The Flutter route that is initially loaded within this {@code Activity} is "/". The initial + * route may be specified explicitly by passing the name of the route as a {@code String} in + * {@link #EXTRA_INITIAL_ROUTE}, e.g., "my/deep/link". + *

+ * The app bundle path, Dart entrypoint, and initial route can each be controlled in a subclass of + * {@code FlutterActivity} by overriding their respective methods: + *

    + *
  • {@link #getAppBundlePath()}
  • + *
  • {@link #getDartEntrypoint()}
  • + *
  • {@link #getInitialRoute()}
  • + *
+ * If Flutter is needed in a location that cannot use an {@code Activity}, consider using + * a {@link FlutterFragment}. Using a {@link FlutterFragment} requires forwarding some calls from + * an {@code Activity} to the {@link FlutterFragment}. + *

+ * If Flutter is needed in a location that can only use a {@code View}, consider using a + * {@link FlutterView}. Using a {@link FlutterView} requires forwarding some calls from an + * {@code Activity}, as well as forwarding lifecycle calls from an {@code Activity} or a + * {@code Fragment}. + */ +// TODO(mattcarroll): explain each call forwarded to Fragment (first requires resolution of PluginRegistry API). +public class FlutterActivity extends FragmentActivity { + private static final String TAG = "FlutterActivity"; + + // Meta-data arguments, processed from manifest XML. + protected static final String DART_ENTRYPOINT_META_DATA_KEY = "io.flutter.Entrypoint"; + protected static final String INITIAL_ROUTE_META_DATA_KEY = "io.flutter.InitialRoute"; + + // Intent extra arguments. + protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint"; + protected static final String EXTRA_INITIAL_ROUTE = "initial_route"; + + // Default configuration. + protected static final String DEFAULT_DART_ENTRYPOINT = "main"; + protected static final String DEFAULT_INITIAL_ROUTE = "/"; + + // FlutterFragment management. + private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment"; + // TODO(mattcarroll): replace ID with R.id when build system supports R.java + private static final int FRAGMENT_CONTAINER_ID = 609893468; // random number + private FlutterFragment flutterFragment; + + /** + * Builder to create an {@code Intent} that launches a {@code FlutterActivity} with the + * desired configuration. + */ + public static class IntentBuilder { + private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT; + private String initialRoute = DEFAULT_INITIAL_ROUTE; + + /** + * The name of the initial Dart method to invoke, defaults to "main". + */ + @NonNull + public IntentBuilder dartEntrypoint(@NonNull String dartEntrypoint) { + this.dartEntrypoint = dartEntrypoint; + return this; + } + + /** + * The initial route that a Flutter app will render in this {@link FlutterFragment}, + * defaults to "/". + */ + @NonNull + public IntentBuilder initialRoute(@NonNull String initialRoute) { + this.initialRoute = initialRoute; + return this; + } + + @NonNull + public Intent build(@NonNull Context context) { + return new Intent(context, FlutterActivity.class) + .putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint) + .putExtra(EXTRA_INITIAL_ROUTE, initialRoute); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(createFragmentContainer()); + configureStatusBarForFullscreenFlutterExperience(); + ensureFlutterFragmentCreated(); + } + + private void configureStatusBarForFullscreenFlutterExperience() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(0x40000000); + window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI); + } + } + + /** + * Creates a {@link FrameLayout} with an ID of {@code #FRAGMENT_CONTAINER_ID} that will contain + * the {@link FlutterFragment} displayed by this {@code FlutterActivity}. + *

+ * @return the FrameLayout container + */ + @NonNull + private View createFragmentContainer() { + FrameLayout container = new FrameLayout(this); + container.setId(FRAGMENT_CONTAINER_ID); + container.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); + return container; + } + + /** + * Ensure that a {@link FlutterFragment} is attached to this {@code FlutterActivity}. + *

+ * If no {@link FlutterFragment} exists in this {@code FlutterActivity}, then a {@link FlutterFragment} + * is created and added. If a {@link FlutterFragment} does exist in this {@code FlutterActivity}, then + * a reference to that {@link FlutterFragment} is retained in {@code #flutterFragment}. + */ + private void ensureFlutterFragmentCreated() { + FragmentManager fragmentManager = getSupportFragmentManager(); + flutterFragment = (FlutterFragment) fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT); + if (flutterFragment == null) { + // No FlutterFragment exists yet. This must be the initial Activity creation. We will create + // and add a new FlutterFragment to this Activity. + flutterFragment = createFlutterFragment(); + fragmentManager + .beginTransaction() + .add(FRAGMENT_CONTAINER_ID, flutterFragment, TAG_FLUTTER_FRAGMENT) + .commit(); + } + } + + /** + * Creates the instance of the {@link FlutterFragment} that this {@code FlutterActivity} displays. + *

+ * Subclasses may override this method to return a specialization of {@link FlutterFragment}. + */ + @NonNull + protected FlutterFragment createFlutterFragment() { + return new FlutterFragment.Builder() + .dartEntrypoint(getDartEntrypoint()) + .initialRoute(getInitialRoute()) + .appBundlePath(getAppBundlePath()) + .flutterShellArgs(FlutterShellArgs.fromIntent(getIntent())) + .renderMode(FlutterView.RenderMode.surface) + .build(); + } + + @Override + public void onPostResume() { + super.onPostResume(); + flutterFragment.onPostResume(); + } + + @Override + protected void onNewIntent(Intent intent) { + // Forward Intents to our FlutterFragment in case it cares. + flutterFragment.onNewIntent(intent); + } + + @Override + public void onBackPressed() { + flutterFragment.onBackPressed(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + flutterFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onUserLeaveHint() { + flutterFragment.onUserLeaveHint(); + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + flutterFragment.onTrimMemory(level); + } + + @SuppressWarnings("unused") + @Nullable + protected FlutterEngine getFlutterEngine() { + return flutterFragment.getFlutterEngine(); + } + + /** + * The path to the bundle that contains this Flutter app's resources, e.g., Dart code snapshots. + *

+ * When this {@code FlutterActivity} is run by Flutter tooling and a data String is included + * in the launching {@code Intent}, that data String is interpreted as an app bundle path. + *

+ * By default, the app bundle path is obtained from {@link FlutterMain#findAppBundlePath(Context)}. + *

+ * Subclasses may override this method to return a custom app bundle path. + */ + @NonNull + protected String getAppBundlePath() { + // If this Activity was launched from tooling, and the incoming Intent contains + // a custom app bundle path, return that path. + // TODO(mattcarroll): determine if we should have an explicit FlutterTestActivity instead of conflating. + if (isDebuggable() && Intent.ACTION_RUN.equals(getIntent().getAction())) { + String appBundlePath = getIntent().getDataString(); + if (appBundlePath != null) { + return appBundlePath; + } + } + + // Return the default app bundle path. + // TODO(mattcarroll): move app bundle resolution into an appropriately named class. + return FlutterMain.findAppBundlePath(getApplicationContext()); + } + + /** + * The Dart entrypoint that will be executed as soon as the Dart snapshot is loaded. + *

+ * This preference can be controlled with 2 methods: + *

    + *
  1. Pass a {@code String} as {@link #EXTRA_DART_ENTRYPOINT} with the launching {@code Intent}, or
  2. + *
  3. Set a {@code } called {@link #DART_ENTRYPOINT_META_DATA_KEY} for this + * {@code Activity} in the Android manifest.
  4. + *
+ * If both preferences are set, the {@code Intent} preference takes priority. + *

+ * The reason that a {@code } preference is supported is because this {@code Activity} + * might be the very first {@code Activity} launched, which means the developer won't have + * control over the incoming {@code Intent}. + *

+ * Subclasses may override this method to directly control the Dart entrypoint. + */ + @NonNull + protected String getDartEntrypoint() { + if (getIntent().hasExtra(EXTRA_DART_ENTRYPOINT)) { + return getIntent().getStringExtra(EXTRA_DART_ENTRYPOINT); + } + + try { + ActivityInfo activityInfo = getPackageManager().getActivityInfo( + getComponentName(), + PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + ); + Bundle metadata = activityInfo.metaData; + String desiredDartEntrypoint = metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null; + return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT; + } catch (PackageManager.NameNotFoundException e) { + return DEFAULT_DART_ENTRYPOINT; + } + } + + /** + * The initial route that a Flutter app will render upon loading and executing its Dart code. + *

+ * This preference can be controlled with 2 methods: + *

    + *
  1. Pass a boolean as {@link #EXTRA_INITIAL_ROUTE} with the launching {@code Intent}, or
  2. + *
  3. Set a {@code } called {@link #INITIAL_ROUTE_META_DATA_KEY} for this + * {@code Activity} in the Android manifest.
  4. + *
+ * If both preferences are set, the {@code Intent} preference takes priority. + *

+ * The reason that a {@code } preference is supported is because this {@code Activity} + * might be the very first {@code Activity} launched, which means the developer won't have + * control over the incoming {@code Intent}. + *

+ * Subclasses may override this method to directly control the initial route. + */ + @NonNull + protected String getInitialRoute() { + if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) { + return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE); + } + + try { + ActivityInfo activityInfo = getPackageManager().getActivityInfo( + getComponentName(), + PackageManager.GET_META_DATA|PackageManager.GET_ACTIVITIES + ); + Bundle metadata = activityInfo.metaData; + String desiredInitialRoute = metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null; + return desiredInitialRoute != null ? desiredInitialRoute : DEFAULT_INITIAL_ROUTE; + } catch (PackageManager.NameNotFoundException e) { + return DEFAULT_INITIAL_ROUTE; + } + } + + /** + * Returns true if Flutter is running in "debug mode", and false otherwise. + *

+ * Debug mode allows Flutter to operate with hot reload and hot restart. Release mode does not. + */ + private boolean isDebuggable() { + return (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java new file mode 100644 index 0000000000000..771928f866ed7 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -0,0 +1,567 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.FlutterShellArgs; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.platform.PlatformPlugin; +import io.flutter.view.FlutterMain; + +import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; + +/** + * {@code Fragment} which displays a Flutter UI that takes up all available {@code Fragment} space. + *

+ * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + *

+ * Using a {@code FlutterFragment} requires forwarding a number of calls from an {@code Activity} to + * ensure that the internal Flutter app behaves as expected: + *

    + *
  1. {@link android.app.Activity#onPostResume()}
  2. + *
  3. {@link android.app.Activity#onBackPressed()}
  4. + *
  5. {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])} ()}
  6. + *
  7. {@link android.app.Activity#onNewIntent(Intent)} ()}
  8. + *
  9. {@link android.app.Activity#onUserLeaveHint()}
  10. + *
  11. {@link android.app.Activity#onTrimMemory(int)}
  12. + *
+ * Additionally, when starting an {@code Activity} for a result from this {@code Fragment}, be sure + * to invoke {@link Fragment#startActivityForResult(Intent, int)} rather than + * {@link android.app.Activity#startActivityForResult(Intent, int)}. If the {@code Activity} version + * of the method is invoked then this {@code Fragment} will never receive its + * {@link Fragment#onActivityResult(int, int, Intent)} callback. + *

+ * If convenient, consider using a {@link FlutterActivity} instead of a {@code FlutterFragment} to + * avoid the work of forwarding calls. + *

+ * If Flutter is needed in a location that can only use a {@code View}, consider using a + * {@link FlutterView}. Using a {@link FlutterView} requires forwarding some calls from an + * {@code Activity}, as well as forwarding lifecycle calls from an {@code Activity} or a + * {@code Fragment}. + */ +public class FlutterFragment extends Fragment { + private static final String TAG = "FlutterFragment"; + + private static final String ARG_DART_ENTRYPOINT = "dart_entrypoint"; + private static final String ARG_INITIAL_ROUTE = "initial_route"; + private static final String ARG_APP_BUNDLE_PATH = "app_bundle_path"; + private static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args"; + private static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode"; + + /** + * Builder that creates a new {@code FlutterFragment} with {@code arguments} that correspond + * to the values set on this {@code Builder}. + *

+ * To create a {@code FlutterFragment} with default {@code arguments}, invoke {@code build()} + * immeidately: + * {@code + * FlutterFragment fragment = new FlutterFragment.Builder().build(); + * } + */ + public static class Builder { + private String dartEntrypoint = "main"; + private String initialRoute = "/"; + private String appBundlePath = null; + private FlutterShellArgs shellArgs = null; + private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface; + + /** + * The name of the initial Dart method to invoke, defaults to "main". + */ + @NonNull + public Builder dartEntrypoint(@NonNull String dartEntrypoint) { + this.dartEntrypoint = dartEntrypoint; + return this; + } + + /** + * The initial route that a Flutter app will render in this {@link FlutterFragment}, + * defaults to "/". + */ + @NonNull + public Builder initialRoute(@NonNull String initialRoute) { + this.initialRoute = initialRoute; + return this; + } + + /** + * The path to the app bundle which contains the Dart app to execute, defaults + * to {@link FlutterMain#findAppBundlePath(Context)} + */ + @NonNull + public Builder appBundlePath(@NonNull String appBundlePath) { + this.appBundlePath = appBundlePath; + return this; + } + + /** + * Any special configuration arguments for the Flutter engine + */ + @NonNull + public Builder flutterShellArgs(@NonNull FlutterShellArgs shellArgs) { + this.shellArgs = shellArgs; + return this; + } + + /** + * Render Flutter either as a {@link FlutterView.RenderMode#surface} or a + * {@link FlutterView.RenderMode#texture}. You should use {@code surface} unless + * you have a specific reason to use {@code texture}. {@code texture} comes with + * a significant performance impact, but {@code texture} can be displayed + * beneath other Android {@code View}s and animated, whereas {@code surface} + * cannot. + */ + @NonNull + public Builder renderMode(@NonNull FlutterView.RenderMode renderMode) { + this.renderMode = renderMode; + return this; + } + + @NonNull + public FlutterFragment build() { + FlutterFragment frag = new FlutterFragment(); + + Bundle args = createArgsBundle( + dartEntrypoint, + initialRoute, + appBundlePath, + shellArgs, + renderMode + ); + frag.setArguments(args); + + return frag; + } + } + + /** + * Creates a {@link Bundle} of arguments that can be used to configure a {@link FlutterFragment}. + * This method is exposed so that developers can create subclasses of {@link FlutterFragment}. + * Subclasses should declare static factories that use this method to create arguments that will + * be understood by the base class, and then the subclass can add any additional arguments it + * wants to this {@link Bundle}. Example: + *

{@code
+   * public static MyFlutterFragment newInstance(String myNewArg) {
+   *   // Create an instance of your subclass Fragment.
+   *   MyFlutterFragment myFrag = new MyFlutterFragment();
+   *
+   *   // Create the Bundle or args that FlutterFragment understands.
+   *   Bundle args = FlutterFragment.createArgsBundle(...);
+   *
+   *   // Add your new args to the bundle.
+   *   args.putString(ARG_MY_NEW_ARG, myNewArg);
+   *
+   *   // Give the args to your subclass Fragment.
+   *   myFrag.setArguments(args);
+   *
+   *   // Return the newly created subclass Fragment.
+   *   return myFrag;
+   * }
+   * }
+ * + * @param dartEntrypoint the name of the initial Dart method to invoke, defaults to "main" + * @param initialRoute the first route that a Flutter app will render in this {@link FlutterFragment}, defaults to "/" + * @param appBundlePath the path to the app bundle which contains the Dart app to execute + * @param flutterShellArgs any special configuration arguments for the Flutter engine + * @param renderMode render Flutter either as a {@link FlutterView.RenderMode#surface} or a + * {@link FlutterView.RenderMode#texture}. You should use {@code surface} unless + * you have a specific reason to use {@code texture}. {@code texture} comes with + * a significant performance impact, but {@code texture} can be displayed + * beneath other Android {@code View}s and animated, whereas {@code surface} + * cannot. + * + * @return Bundle of arguments that configure a {@link FlutterFragment} + */ + protected static Bundle createArgsBundle(@Nullable String dartEntrypoint, + @Nullable String initialRoute, + @Nullable String appBundlePath, + @Nullable FlutterShellArgs flutterShellArgs, + @Nullable FlutterView.RenderMode renderMode) { + Bundle args = new Bundle(); + args.putString(ARG_INITIAL_ROUTE, initialRoute); + args.putString(ARG_APP_BUNDLE_PATH, appBundlePath); + args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint); + // TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of conflating. + if (null != flutterShellArgs) { + args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, flutterShellArgs.toArray()); + } + args.putString(ARG_FLUTTERVIEW_RENDER_MODE, renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name()); + return args; + } + + @Nullable + private FlutterEngine flutterEngine; + @Nullable + private FlutterView flutterView; + @Nullable + private PlatformPlugin platformPlugin; + + public FlutterFragment() { + // Ensure that we at least have an empty Bundle of arguments so that we don't + // need to continually check for null arguments before grabbing one. + setArguments(new Bundle()); + } + + /** + * The {@link FlutterEngine} that backs the Flutter content presented by this {@code Fragment}. + * + * @return the {@link FlutterEngine} held by this {@code Fragment} + */ + @Nullable + public FlutterEngine getFlutterEngine() { + return flutterEngine; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + initializeFlutter(getContextCompat()); + + // When "retain instance" is true, the FlutterEngine will survive configuration + // changes. Therefore, we create a new one only if one does not already exist. + if (flutterEngine == null) { + createFlutterEngine(); + } + + // Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin + // is bound to a specific Activity. Therefore, it needs to be created and configured + // every time this Fragment attaches to a new Activity. + // TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes + // control of the entire window. This is unacceptable for non-fullscreen + // use-cases. + platformPlugin = new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel()); + } + + private void initializeFlutter(@NonNull Context context) { + String[] flutterShellArgsArray = getArguments().getStringArray(ARG_FLUTTER_INITIALIZATION_ARGS); + FlutterShellArgs flutterShellArgs = new FlutterShellArgs( + flutterShellArgsArray != null ? flutterShellArgsArray : new String[] {} + ); + + FlutterMain.ensureInitializationComplete(context.getApplicationContext(), flutterShellArgs.toArray()); + } + + /** + * Creates a new FlutterEngine instance. + * + * Subclasses can instantiate their own {@link FlutterEngine} by overriding + * {@link #onCreateFlutterEngine(Context)}. + * + * Subclasses can alter the {@link FlutterEngine} after creation by overriding + * {@link #onFlutterEngineCreated(FlutterEngine)}. + */ + private void createFlutterEngine() { + // Create a FlutterEngine to back our FlutterView. + flutterEngine = onCreateFlutterEngine(getActivity()); + + // Allow subclasses to customize FlutterEngine as desired. + onFlutterEngineCreated(flutterEngine); + } + + /** + * Hook for subclasses to customize the creation of the {@code FlutterEngine}. + * + * By default, this method returns a standard {@link FlutterEngine} without any modification. + */ + @NonNull + protected FlutterEngine onCreateFlutterEngine(@NonNull Context context) { + Log.d(TAG, "onCreateFlutterEngine()"); + return new FlutterEngine(context); + } + + /** + * Hook for subclasses to customize the {@link FlutterEngine} owned by this {@link FlutterFragment} + * after the {@link FlutterEngine} has been instantiated. + * + * Consider using this method to connect desired Flutter plugins to this {@code Fragment}'s + * {@link FlutterEngine}. + */ + protected void onFlutterEngineCreated(@NonNull FlutterEngine flutterEngine) { + // no-op + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + flutterView = new FlutterView(getContext(), getRenderMode()); + flutterView.attachToFlutterEngine(flutterEngine); + + // TODO(mattcarroll): the following call should exist here, but the plugin system needs to be revamped. + // The existing attach() method does not know how to handle this kind of FlutterView. + //flutterEngine.getPluginRegistry().attach(this, getActivity()); + + doInitialFlutterViewRun(); + + return flutterView; + } + + /** + * Starts running Dart within the FlutterView for the first time. + * + * Reloading/restarting Dart within a given FlutterView is not supported. If this method is + * invoked while Dart is already executing then it does nothing. + * + * {@code flutterEngine} must be non-null when invoking this method. + */ + private void doInitialFlutterViewRun() { + if (flutterEngine.getDartExecutor().isExecutingDart()) { + // No warning is logged because this situation will happen on every config + // change if the developer does not choose to retain the Fragment instance. + // So this is expected behavior in many cases. + return; + } + + // The engine needs to receive the Flutter app's initial route before executing any + // Dart code to ensure that the initial route arrives in time to be applied. + if (getInitialRoute() != null) { + flutterEngine.getNavigationChannel().setInitialRoute(getInitialRoute()); + } + + // Configure the Dart entrypoint and execute it. + DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint( + getResources().getAssets(), + getAppBundlePath(), + getDartEntrypointFunctionName() + ); + flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); + } + + /** + * Returns the initial route that should be rendered within Flutter, once the Flutter app starts. + * + * Defaults to {@code null}, which signifies a route of "/" in Flutter. + */ + @Nullable + protected String getInitialRoute() { + return getArguments().getString(ARG_INITIAL_ROUTE); + } + + /** + * Returns the file path to the desired Flutter app's bundle of code. + * + * Defaults to {@link FlutterMain#findAppBundlePath(Context)}. + */ + @NonNull + protected String getAppBundlePath() { + return getArguments().getString(ARG_APP_BUNDLE_PATH, FlutterMain.findAppBundlePath(getContextCompat())); + } + + /** + * Returns the name of the Dart method that this {@code FlutterFragment} should execute to + * start a Flutter app. + * + * Defaults to "main". + */ + @NonNull + protected String getDartEntrypointFunctionName() { + return getArguments().getString(ARG_DART_ENTRYPOINT, "main"); + } + + /** + * Returns the desired {@link FlutterView.RenderMode} for the {@link FlutterView} displayed in + * this {@code FlutterFragment}. + * + * Defaults to {@link FlutterView.RenderMode#surface}. + */ + @NonNull + protected FlutterView.RenderMode getRenderMode() { + String renderModeName = getArguments().getString(ARG_FLUTTERVIEW_RENDER_MODE, FlutterView.RenderMode.surface.name()); + return FlutterView.RenderMode.valueOf(renderModeName); + } + + // TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if possible. + public void onPostResume() { + Log.d(TAG, "onPostResume()"); + if (flutterEngine != null) { + flutterEngine.getLifecycleChannel().appIsResumed(); + + // TODO(mattcarroll): find a better way to handle the update of UI overlays than calling through + // to platformPlugin. We're implicitly entangling the Window, Activity, Fragment, + // and engine all with this one call. + platformPlugin.onPostResume(); + } else { + Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity."); + } + } + + @Override + public void onPause() { + super.onPause(); + Log.d(TAG, "onPause()"); + flutterEngine.getLifecycleChannel().appIsInactive(); + } + + @Override + public void onStop() { + super.onStop(); + Log.d(TAG, "onStop()"); + flutterEngine.getLifecycleChannel().appIsPaused(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + Log.d(TAG, "onDestroyView()"); + flutterView.detachFromFlutterEngine(); + } + + @Override + public void onDetach() { + super.onDetach(); + Log.d(TAG, "onDetach()"); + + // Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment, + // and this Fragment's Activity. + platformPlugin = null; + + // Destroy our FlutterEngine if we're not set to retain it. + if (!retainFlutterIsolateAfterFragmentDestruction()) { + flutterEngine.destroy(); + flutterEngine = null; + } + } + + /** + * Returns true if the {@link FlutterEngine} within this {@code FlutterFragment} should outlive + * the {@code FlutterFragment}, itself. + * + * Defaults to false. This method can be overridden in subclasses to retain the + * {@link FlutterEngine}. + */ + // TODO(mattcarroll): consider a dynamic determination of this preference based on whether the + // engine was created automatically, or if the engine was provided manually. + // Manually provided engines should probably not be destroyed. + protected boolean retainFlutterIsolateAfterFragmentDestruction() { + return false; + } + + /** + * The hardware back button was pressed. + * + * See {@link android.app.Activity#onBackPressed()} + */ + public void onBackPressed() { + Log.d(TAG, "onBackPressed()"); + if (flutterEngine != null) { + flutterEngine.getNavigationChannel().popRoute(); + } else { + Log.w(TAG, "Invoked onBackPressed() before FlutterFragment was attached to an Activity."); + } + } + + /** + * The result of a permission request has been received. + * + * See {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])} + * + * @param requestCode identifier passed with the initial permission request + * @param permissions permissions that were requested + * @param grantResults permission grants or denials + */ + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (flutterEngine != null) { + flutterEngine.getPluginRegistry().onRequestPermissionsResult(requestCode, permissions, grantResults); + } else { + Log.w(TAG, "onRequestPermissionResult() invoked before FlutterFragment was attached to an Activity."); + } + } + + /** + * A new Intent was received by the {@link android.app.Activity} that currently owns this + * {@link Fragment}. + * + * See {@link android.app.Activity#onNewIntent(Intent)} + * + * @param intent new Intent + */ + public void onNewIntent(@NonNull Intent intent) { + if (flutterEngine != null) { + flutterEngine.getPluginRegistry().onNewIntent(intent); + } else { + Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity."); + } + } + + /** + * A result has been returned after an invocation of {@link Fragment#startActivityForResult(Intent, int)}. + * + * @param requestCode request code sent with {@link Fragment#startActivityForResult(Intent, int)} + * @param resultCode code representing the result of the {@code Activity} that was launched + * @param data any corresponding return data, held within an {@code Intent} + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (flutterEngine != null) { + flutterEngine.getPluginRegistry().onActivityResult(requestCode, resultCode, data); + } else { + Log.w(TAG, "onActivityResult() invoked before FlutterFragment was attached to an Activity."); + } + } + + /** + * The {@link android.app.Activity} that owns this {@link Fragment} is about to go to the background + * as the result of a user's choice/action, i.e., not as the result of an OS decision. + * + * See {@link android.app.Activity#onUserLeaveHint()} + */ + public void onUserLeaveHint() { + if (flutterEngine != null) { + flutterEngine.getPluginRegistry().onUserLeaveHint(); + } else { + Log.w(TAG, "onUserLeaveHint() invoked before FlutterFragment was attached to an Activity."); + } + } + + /** + * Callback invoked when memory is low. + * + * This implementation forwards a memory pressure warning to the running Flutter app. + * + * @param level level + */ + public void onTrimMemory(int level) { + if (flutterEngine != null) { + // Use a trim level delivered while the application is running so the + // framework has a chance to react to the notification. + if (level == TRIM_MEMORY_RUNNING_LOW) { + flutterEngine.getSystemChannel().sendMemoryPressureWarning(); + } + } else { + Log.w(TAG, "onTrimMemory() invoked before FlutterFragment was attached to an Activity."); + } + } + + /** + * Callback invoked when memory is low. + * + * This implementation forwards a memory pressure warning to the running Flutter app. + */ + @Override + public void onLowMemory() { + super.onLowMemory(); + flutterEngine.getSystemChannel().sendMemoryPressureWarning(); + } + + @NonNull + private Context getContextCompat() { + return Build.VERSION.SDK_INT >= 23 + ? getContext() + : getActivity(); + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java b/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java new file mode 100644 index 0000000000000..ac19c5ae15798 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java @@ -0,0 +1,187 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +/** + * Paints a Flutter UI on a {@link android.view.Surface}. + * + * To begin rendering a Flutter UI, the owner of this {@code FlutterSurfaceView} must invoke + * {@link #attachToRenderer(FlutterRenderer)} with the desired {@link FlutterRenderer}. + * + * To stop rendering a Flutter UI, the owner of this {@code FlutterSurfaceView} must invoke + * {@link #detachFromRenderer()}. + * + * A {@code FlutterSurfaceView} is intended for situations where a developer needs to render + * a Flutter UI, but does not require any keyboard input, gesture input, accessibility + * integrations or any other interactivity beyond rendering. If standard interactivity is + * desired, consider using a {@link FlutterView} which provides all of these behaviors and + * utilizes a {@code FlutterSurfaceView} internally. + */ +public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.RenderSurface { + private static final String TAG = "FlutterSurfaceView"; + + private boolean isSurfaceAvailableForRendering = false; + private boolean isAttachedToFlutterRenderer = false; + @Nullable + private FlutterRenderer flutterRenderer; + + // Connects the {@code Surface} beneath this {@code SurfaceView} with Flutter's native code. + // Callbacks are received by this Object and then those messages are forwarded to our + // FlutterRenderer, and then on to the JNI bridge over to native Flutter code. + private final SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.d(TAG, "SurfaceHolder.Callback.surfaceCreated()"); + isSurfaceAvailableForRendering = true; + + if (isAttachedToFlutterRenderer) { + Log.d(TAG, "Already attached to renderer. Notifying of surface creation."); + connectSurfaceToRenderer(); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.d(TAG, "SurfaceHolder.Callback.surfaceChanged()"); + if (isAttachedToFlutterRenderer) { + changeSurfaceSize(width, height); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(TAG, "SurfaceHolder.Callback.surfaceDestroyed()"); + isSurfaceAvailableForRendering = false; + + if (isAttachedToFlutterRenderer) { + disconnectSurfaceFromRenderer(); + } + } + }; + + /** + * Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes. + */ + public FlutterSurfaceView(Context context) { + this(context, null); + } + + /** + * Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner. + */ + public FlutterSurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + // Grab a reference to our underlying Surface and register callbacks with that Surface so we + // can monitor changes and forward those changes on to native Flutter code. + getHolder().addCallback(surfaceCallback); + + // Keep this SurfaceView transparent until Flutter has a frame ready to render. This avoids + // displaying a black rectangle in our place. + setAlpha(0.0f); + } + + /** + * Invoked by the owner of this {@code FlutterSurfaceView} when it wants to begin rendering + * a Flutter UI to this {@code FlutterSurfaceView}. + * + * If an Android {@link android.view.Surface} is available, this method will give that + * {@link android.view.Surface} to the given {@link FlutterRenderer} to begin rendering + * Flutter's UI to this {@code FlutterSurfaceView}. + * + * If no Android {@link android.view.Surface} is available yet, this {@code FlutterSurfaceView} + * will wait until a {@link android.view.Surface} becomes available and then give that + * {@link android.view.Surface} to the given {@link FlutterRenderer} to begin rendering + * Flutter's UI to this {@code FlutterSurfaceView}. + */ + public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) { + Log.d(TAG, "attachToRenderer"); + if (this.flutterRenderer != null) { + this.flutterRenderer.detachFromRenderSurface(); + } + + this.flutterRenderer = flutterRenderer; + isAttachedToFlutterRenderer = true; + + // If we're already attached to an Android window then we're now attached to both a renderer + // and the Android window. We can begin rendering now. + if (isSurfaceAvailableForRendering) { + Log.d(TAG, "Surface is available for rendering. Connecting."); + connectSurfaceToRenderer(); + } + } + + /** + * Invoked by the owner of this {@code FlutterSurfaceView} when it no longer wants to render + * a Flutter UI to this {@code FlutterSurfaceView}. + * + * This method will cease any on-going rendering from Flutter to this {@code FlutterSurfaceView}. + */ + public void detachFromRenderer() { + if (flutterRenderer != null) { + // If we're attached to an Android window then we were rendering a Flutter UI. Now that + // this FlutterSurfaceView is detached from the FlutterRenderer, we need to stop rendering. + if (getWindowToken() != null) { + disconnectSurfaceFromRenderer(); + } + + // Make the SurfaceView invisible to avoid showing a black rectangle. + setAlpha(0.0f); + + flutterRenderer = null; + isAttachedToFlutterRenderer = false; + } else { + Log.w(TAG, "detachFromRenderer() invoked when no FlutterRenderer was attached."); + } + } + + // FlutterRenderer and getSurfaceTexture() must both be non-null. + private void connectSurfaceToRenderer() { + if (flutterRenderer == null || getHolder() == null) { + throw new IllegalStateException("connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null."); + } + + flutterRenderer.surfaceCreated(getHolder().getSurface()); + } + + // FlutterRenderer must be non-null. + private void changeSurfaceSize(int width, int height) { + if (flutterRenderer == null) { + throw new IllegalStateException("changeSurfaceSize() should only be called when flutterRenderer is non-null."); + } + + flutterRenderer.surfaceChanged(width, height); + } + + // FlutterRenderer must be non-null. + private void disconnectSurfaceFromRenderer() { + if (flutterRenderer == null) { + throw new IllegalStateException("disconnectSurfaceFromRenderer() should only be called when flutterRenderer is non-null."); + } + + flutterRenderer.surfaceDestroyed(); + } + + @Override + public void onFirstFrameRendered() { + // TODO(mattcarroll): decide where this method should live and what it needs to do. + Log.d(TAG, "onFirstFrameRendered()"); + // Now that a frame is ready to display, take this SurfaceView from transparent to opaque. + setAlpha(1.0f); + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java b/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java new file mode 100644 index 0000000000000..32850ff17e875 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java @@ -0,0 +1,189 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; + +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +/** + * Paints a Flutter UI on a {@link SurfaceTexture}. + * + * To begin rendering a Flutter UI, the owner of this {@code FlutterTextureView} must invoke + * {@link #attachToRenderer(FlutterRenderer)} with the desired {@link FlutterRenderer}. + * + * To stop rendering a Flutter UI, the owner of this {@code FlutterTextureView} must invoke + * {@link #detachFromRenderer()}. + * + * A {@code FlutterTextureView} is intended for situations where a developer needs to render + * a Flutter UI, but does not require any keyboard input, gesture input, accessibility + * integrations or any other interactivity beyond rendering. If standard interactivity is + * desired, consider using a {@link FlutterView} which provides all of these behaviors and + * utilizes a {@code FlutterTextureView} internally. + */ +public class FlutterTextureView extends TextureView implements FlutterRenderer.RenderSurface { + private static final String TAG = "FlutterTextureView"; + + private boolean isSurfaceAvailableForRendering = false; + private boolean isAttachedToFlutterRenderer = false; + @Nullable + private FlutterRenderer flutterRenderer; + + // Connects the {@code SurfaceTexture} beneath this {@code TextureView} with Flutter's native code. + // Callbacks are received by this Object and then those messages are forwarded to our + // FlutterRenderer, and then on to the JNI bridge over to native Flutter code. + private final SurfaceTextureListener surfaceTextureListener = new SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + Log.d(TAG, "SurfaceTextureListener.onSurfaceTextureAvailable()"); + isSurfaceAvailableForRendering = true; + + // If we're already attached to a FlutterRenderer then we're now attached to both a renderer + // and the Android window, so we can begin rendering now. + if (isAttachedToFlutterRenderer) { + Log.d(TAG, "Already attached to renderer. Notifying of surface creation."); + connectSurfaceToRenderer(); + } + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + if (isAttachedToFlutterRenderer) { + changeSurfaceSize(width, height); + } + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // Invoked every time a new frame is available. We don't care. + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + Log.d(TAG, "SurfaceTextureListener.onSurfaceTextureDestroyed()"); + isSurfaceAvailableForRendering = false; + + // If we're attached to a FlutterRenderer then we need to notify it that our SurfaceTexture + // has been destroyed. + if (isAttachedToFlutterRenderer) { + disconnectSurfaceFromRenderer(); + } + + // Return true to indicate that no further painting will take place + // within this SurfaceTexture. + return true; + } + }; + + /** + * Constructs a {@code FlutterTextureView} programmatically, without any XML attributes. + */ + public FlutterTextureView(Context context) { + this(context, null); + } + + /** + * Constructs a {@code FlutterTextureView} in an XML-inflation-compliant manner. + */ + public FlutterTextureView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + // Listen for when our underlying SurfaceTexture becomes available, changes size, or + // gets destroyed, and take the appropriate actions. + setSurfaceTextureListener(surfaceTextureListener); + } + + /** + * Invoked by the owner of this {@code FlutterTextureView} when it wants to begin rendering + * a Flutter UI to this {@code FlutterTextureView}. + * + * If an Android {@link SurfaceTexture} is available, this method will give that + * {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering + * Flutter's UI to this {@code FlutterTextureView}. + * + * If no Android {@link SurfaceTexture} is available yet, this {@code FlutterTextureView} + * will wait until a {@link SurfaceTexture} becomes available and then give that + * {@link SurfaceTexture} to the given {@link FlutterRenderer} to begin rendering + * Flutter's UI to this {@code FlutterTextureView}. + */ + public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) { + if (this.flutterRenderer != null) { + this.flutterRenderer.detachFromRenderSurface(); + } + + this.flutterRenderer = flutterRenderer; + isAttachedToFlutterRenderer = true; + + // If we're already attached to an Android window then we're now attached to both a renderer + // and the Android window. We can begin rendering now. + if (isSurfaceAvailableForRendering) { + connectSurfaceToRenderer(); + } + } + + /** + * Invoked by the owner of this {@code FlutterTextureView} when it no longer wants to render + * a Flutter UI to this {@code FlutterTextureView}. + * + * This method will cease any on-going rendering from Flutter to this {@code FlutterTextureView}. + */ + public void detachFromRenderer() { + if (flutterRenderer != null) { + // If we're attached to an Android window then we were rendering a Flutter UI. Now that + // this FlutterTextureView is detached from the FlutterRenderer, we need to stop rendering. + if (getWindowToken() != null) { + disconnectSurfaceFromRenderer(); + } + + flutterRenderer = null; + isAttachedToFlutterRenderer = false; + } else { + Log.w(TAG, "detachFromRenderer() invoked when no FlutterRenderer was attached."); + } + } + + // FlutterRenderer and getSurfaceTexture() must both be non-null. + private void connectSurfaceToRenderer() { + if (flutterRenderer == null || getSurfaceTexture() == null) { + throw new IllegalStateException("connectSurfaceToRenderer() should only be called when flutterRenderer and getSurfaceTexture() are non-null."); + } + + flutterRenderer.surfaceCreated(new Surface(getSurfaceTexture())); + } + + // FlutterRenderer must be non-null. + private void changeSurfaceSize(int width, int height) { + if (flutterRenderer == null) { + throw new IllegalStateException("changeSurfaceSize() should only be called when flutterRenderer is non-null."); + } + + flutterRenderer.surfaceChanged(width, height); + } + + // FlutterRenderer must be non-null. + private void disconnectSurfaceFromRenderer() { + if (flutterRenderer == null) { + throw new IllegalStateException("disconnectSurfaceFromRenderer() should only be called when flutterRenderer is non-null."); + } + + flutterRenderer.surfaceDestroyed(); + } + + @Override + public void onFirstFrameRendered() { + // TODO(mattcarroll): decide where this method should live and what it needs to do. + Log.d(TAG, "onFirstFrameRendered()"); + } +} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java new file mode 100644 index 0000000000000..8469519b3ee63 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -0,0 +1,577 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Build; +import android.os.LocaleList; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.text.format.DateFormat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.FrameLayout; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.plugin.editing.TextInputPlugin; +import io.flutter.view.AccessibilityBridge; + +/** + * Displays a Flutter UI on an Android device. + *

+ * A {@code FlutterView}'s UI is painted by a corresponding {@link FlutterEngine}. + *

+ * A {@code FlutterView} can operate in 2 different {@link RenderMode}s: + *

    + *
  1. {@link RenderMode#surface}, which paints a Flutter UI to a {@link android.view.SurfaceView}. + * This mode has the best performance, but a {@code FlutterView} in this mode cannot be positioned + * between 2 other Android {@code View}s in the z-index, nor can it be animated/transformed. + * Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are required, + * developers should strongly prefer this render mode.
  2. + *
  3. {@link RenderMode#texture}, which paints a Flutter UI to a {@link android.graphics.SurfaceTexture}. + * This mode is not as performant as {@link RenderMode#surface}, but a {@code FlutterView} in this + * mode can be animated and transformed, as well as positioned in the z-index between 2+ other + * Android {@code Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture} + * are required, developers should strongly prefer the {@link RenderMode#surface} render mode.
  4. + *
+ * See
https://source.android.com/devices/graphics/arch-tv#surface_or_texture for more + * information comparing {@link android.view.SurfaceView} and {@link android.view.TextureView}. + */ +public class FlutterView extends FrameLayout { + private static final String TAG = "FlutterView"; + + // Behavior configuration of this FlutterView. + @NonNull + private RenderMode renderMode; + + // Internal view hierarchy references. + @Nullable + private FlutterRenderer.RenderSurface renderSurface; + + // Connections to a Flutter execution context. + @Nullable + private FlutterEngine flutterEngine; + + // Components that process various types of Android View input and events, + // possibly storing intermediate state, and communicating those events to Flutter. + // + // These components essentially add some additional behavioral logic on top of + // existing, stateless system channels, e.g., KeyEventChannel, TextInputChannel, etc. + @Nullable + private TextInputPlugin textInputPlugin; + @Nullable + private AndroidKeyProcessor androidKeyProcessor; + @Nullable + private AndroidTouchProcessor androidTouchProcessor; + @Nullable + private AccessibilityBridge accessibilityBridge; + + // Directly implemented View behavior that communicates with Flutter. + private final FlutterRenderer.ViewportMetrics viewportMetrics = new FlutterRenderer.ViewportMetrics(); + + private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener = new AccessibilityBridge.OnAccessibilityChangeListener() { + @Override + public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { + resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled); + } + }; + + /** + * Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes. + * + * {@link #renderMode} defaults to {@link RenderMode#surface}. + */ + public FlutterView(@NonNull Context context) { + this(context, null, null); + } + + /** + * Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes, + * and allows selection of a {@link #renderMode}. + */ + public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) { + this(context, null, renderMode); + } + + /** + * Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner. + * + * // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr + */ + public FlutterView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, null); + } + + private FlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode) { + super(context, attrs); + + this.renderMode = renderMode == null ? RenderMode.surface : renderMode; + + init(); + } + + private void init() { + Log.d(TAG, "Initializing FlutterView"); + + switch (renderMode) { + case surface: + Log.d(TAG, "Internally creating a FlutterSurfaceView."); + FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext()); + renderSurface = flutterSurfaceView; + addView(flutterSurfaceView); + break; + case texture: + Log.d(TAG, "Internally creating a FlutterTextureView."); + FlutterTextureView flutterTextureView = new FlutterTextureView(getContext()); + renderSurface = flutterTextureView; + addView(flutterTextureView); + break; + } + } + + //------- Start: Process View configuration that Flutter cares about. ------ + /** + * Sends relevant configuration data from Android to Flutter when the Android + * {@link Configuration} changes. + * + * The Android {@link Configuration} might change as a result of device orientation + * change, device language change, device text scale factor change, etc. + */ + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + sendLocalesToFlutter(newConfig); + sendUserSettingsToFlutter(); + } + + /** + * Invoked when this {@code FlutterView} changes size, including upon initial + * measure. + * + * The initial measure reports an {@code oldWidth} and {@code oldHeight} of zero. + * + * Flutter cares about the width and height of the view that displays it on the host + * platform. Therefore, when this method is invoked, the new width and height are + * communicated to Flutter as the "physical size" of the view that displays Flutter's + * UI. + */ + @Override + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { + super.onSizeChanged(width, height, oldWidth, oldHeight); + viewportMetrics.width = width; + viewportMetrics.height = height; + sendViewportMetricsToFlutter(); + } + + /** + * Invoked when Android's desired window insets change, i.e., padding. + * + * Flutter does not use a standard {@code View} hierarchy and therefore Flutter is + * unaware of these insets. Therefore, this method calculates the viewport metrics + * that Flutter should use and then sends those metrics to Flutter. + * + * This callback is not present in API < 20, which means lower API devices will see + * the wider than expected padding when the status and navigation bars are hidden. + */ + @Override + @TargetApi(20) + @RequiresApi(20) + public final WindowInsets onApplyWindowInsets(WindowInsets insets) { + WindowInsets newInsets = super.onApplyWindowInsets(insets); + + // Status bar (top) and left/right system insets should partially obscure the content (padding). + viewportMetrics.paddingTop = insets.getSystemWindowInsetTop(); + viewportMetrics.paddingRight = insets.getSystemWindowInsetRight(); + viewportMetrics.paddingBottom = 0; + viewportMetrics.paddingLeft = insets.getSystemWindowInsetLeft(); + + // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). + viewportMetrics.viewInsetTop = 0; + viewportMetrics.viewInsetRight = 0; + viewportMetrics.viewInsetBottom = insets.getSystemWindowInsetBottom(); + viewportMetrics.viewInsetLeft = 0; + sendViewportMetricsToFlutter(); + + return newInsets; + } + + /** + * Invoked when Android's desired window insets change, i.e., padding. + * + * {@code fitSystemWindows} is an earlier version of + * {@link #onApplyWindowInsets(WindowInsets)}. See that method for more details + * about how window insets relate to Flutter. + */ + @Override + @SuppressWarnings("deprecation") + protected boolean fitSystemWindows(Rect insets) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + // Status bar, left/right system insets partially obscure content (padding). + viewportMetrics.paddingTop = insets.top; + viewportMetrics.paddingRight = insets.right; + viewportMetrics.paddingBottom = 0; + viewportMetrics.paddingLeft = insets.left; + + // Bottom system inset (keyboard) should adjust scrollable bottom edge (inset). + viewportMetrics.viewInsetTop = 0; + viewportMetrics.viewInsetRight = 0; + viewportMetrics.viewInsetBottom = insets.bottom; + viewportMetrics.viewInsetLeft = 0; + sendViewportMetricsToFlutter(); + return true; + } else { + return super.fitSystemWindows(insets); + } + } + //------- End: Process View configuration that Flutter cares about. -------- + + //-------- Start: Process UI I/O that Flutter cares about. ------- + /** + * Creates an {@link InputConnection} to work with a {@link android.view.inputmethod.InputMethodManager}. + * + * Any {@code View} that can take focus or process text input must implement this + * method by returning a non-null {@code InputConnection}. Flutter may render one or + * many focusable and text-input widgets, therefore {@code FlutterView} must support + * an {@code InputConnection}. + * + * The {@code InputConnection} returned from this method comes from a + * {@link TextInputPlugin}, which is owned by this {@code FlutterView}. A + * {@link TextInputPlugin} exists to encapsulate the nuances of input communication, + * rather than spread that logic throughout this {@code FlutterView}. + */ + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + if (!isAttachedToFlutterEngine()) { + return super.onCreateInputConnection(outAttrs); + } + + return textInputPlugin.createInputConnection(this, outAttrs); + } + + /** + * Invoked when key is released. + * + * This method is typically invoked in response to the release of a physical + * keyboard key or a D-pad button. It is generally not invoked when a virtual + * software keyboard is used, though a software keyboard may choose to invoke + * this method in some situations. + * + * {@link KeyEvent}s are sent from Android to Flutter. {@link AndroidKeyProcessor} + * may do some additional work with the given {@link KeyEvent}, e.g., combine this + * {@code keyCode} with the previous {@code keyCode} to generate a unicode combined + * character. + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (!isAttachedToFlutterEngine()) { + return super.onKeyUp(keyCode, event); + } + + androidKeyProcessor.onKeyUp(event); + return super.onKeyUp(keyCode, event); + } + + /** + * Invoked when key is pressed. + * + * This method is typically invoked in response to the press of a physical + * keyboard key or a D-pad button. It is generally not invoked when a virtual + * software keyboard is used, though a software keyboard may choose to invoke + * this method in some situations. + * + * {@link KeyEvent}s are sent from Android to Flutter. {@link AndroidKeyProcessor} + * may do some additional work with the given {@link KeyEvent}, e.g., combine this + * {@code keyCode} with the previous {@code keyCode} to generate a unicode combined + * character. + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (!isAttachedToFlutterEngine()) { + return super.onKeyDown(keyCode, event); + } + + androidKeyProcessor.onKeyDown(event); + return super.onKeyDown(keyCode, event); + } + + /** + * Invoked by Android when a user touch event occurs. + * + * Flutter handles all of its own gesture detection and processing, therefore this + * method forwards all {@link MotionEvent} data from Android to Flutter. + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isAttachedToFlutterEngine()) { + return super.onTouchEvent(event); + } + + // TODO(abarth): This version check might not be effective in some + // versions of Android that statically compile code and will be upset + // at the lack of |requestUnbufferedDispatch|. Instead, we should factor + // version-dependent code into separate classes for each supported + // version and dispatch dynamically. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + requestUnbufferedDispatch(event); + } + + return androidTouchProcessor.onTouchEvent(event); + } + + /** + * Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover, + * track pad touches, scroll wheel movements, etc. + * + * Flutter handles all of its own gesture detection and processing, therefore this + * method forwards all {@link MotionEvent} data from Android to Flutter. + */ + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + boolean handled = isAttachedToFlutterEngine() && androidTouchProcessor.onGenericMotionEvent(event); + return handled ? true : super.onGenericMotionEvent(event); + } + + /** + * Invoked by Android when a hover-compliant input system causes a hover event. + * + * An example of hover events is a stylus sitting near an Android screen. As the + * stylus moves from outside a {@code View} to hover over a {@code View}, or move + * around within a {@code View}, or moves from over a {@code View} to outside a + * {@code View}, a corresponding {@link MotionEvent} is reported via this method. + * + * Hover events can be used for accessibility touch exploration and therefore are + * processed here for accessibility purposes. + */ + @Override + public boolean onHoverEvent(MotionEvent event) { + if (!isAttachedToFlutterEngine()) { + return super.onHoverEvent(event); + } + + boolean handled = accessibilityBridge.onAccessibilityHoverEvent(event); + if (!handled) { + // TODO(ianh): Expose hover events to the platform, + // implementing ADD, REMOVE, etc. + } + return handled; + } + //-------- End: Process UI I/O that Flutter cares about. --------- + + //-------- Start: Accessibility ------- + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + if (accessibilityBridge != null && accessibilityBridge.isAccessibilityEnabled()) { + return accessibilityBridge; + } else { + // TODO(goderbauer): when a11y is off this should return a one-off snapshot of + // the a11y + // tree. + return null; + } + } + + // TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments. + private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { + if (!flutterEngine.getRenderer().isSoftwareRenderingEnabled()) { + setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled)); + } else { + setWillNotDraw(false); + } + } + //-------- End: Accessibility --------- + + /** + * Connects this {@code FlutterView} to the given {@link FlutterEngine}. + * + * This {@code FlutterView} will begin rendering the UI painted by the given {@link FlutterEngine}. + * This {@code FlutterView} will also begin forwarding interaction events from this + * {@code FlutterView} to the given {@link FlutterEngine}, e.g., user touch events, accessibility + * events, keyboard events, and others. + * + * See {@link #detachFromFlutterEngine()} for information on how to detach from a + * {@link FlutterEngine}. + */ + public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { + Log.d(TAG, "attachToFlutterEngine()"); + if (isAttachedToFlutterEngine()) { + if (flutterEngine == this.flutterEngine) { + // We are already attached to this FlutterEngine + Log.d(TAG, "Already attached to this engine. Doing nothing."); + return; + } + + // Detach from a previous FlutterEngine so we can attach to this new one. + Log.d(TAG, "Currently attached to a different engine. Detaching."); + detachFromFlutterEngine(); + } + + this.flutterEngine = flutterEngine; + + // Instruct our FlutterRenderer that we are now its designated RenderSurface. + this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface); + + // Initialize various components that know how to process Android View I/O + // in a way that Flutter understands. + textInputPlugin = new TextInputPlugin( + this, + this.flutterEngine.getDartExecutor() + ); + androidKeyProcessor = new AndroidKeyProcessor( + this.flutterEngine.getKeyEventChannel(), + textInputPlugin + ); + androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer()); + accessibilityBridge = new AccessibilityBridge( + this, + flutterEngine.getAccessibilityChannel(), + (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE), + getContext().getContentResolver(), + // TODO(mattcaroll): plumb the platform views controller to the accessibility bridge. + // https://github.com/flutter/flutter/issues/29618 + null + ); + accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener); + resetWillNotDraw( + accessibilityBridge.isAccessibilityEnabled(), + accessibilityBridge.isTouchExplorationEnabled() + ); + + // Inform the Android framework that it should retrieve a new InputConnection + // now that an engine is attached. + // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin + textInputPlugin.getInputMethodManager().restartInput(this); + + // Push View and Context related information from Android to Flutter. + sendUserSettingsToFlutter(); + sendLocalesToFlutter(getResources().getConfiguration()); + sendViewportMetricsToFlutter(); + } + + /** + * Disconnects this {@code FlutterView} from a previously attached {@link FlutterEngine}. + * + * This {@code FlutterView} will clear its UI and stop forwarding all events to the previously-attached + * {@link FlutterEngine}. This includes touch events, accessibility events, keyboard events, + * and others. + * + * See {@link #attachToFlutterEngine(FlutterEngine)} for information on how to attach a + * {@link FlutterEngine}. + */ + public void detachFromFlutterEngine() { + Log.d(TAG, "detachFromFlutterEngine()"); + if (!isAttachedToFlutterEngine()) { + Log.d(TAG, "Not attached to an engine. Doing nothing."); + return; + } + Log.d(TAG, "Detaching from Flutter Engine"); + + // Inform the Android framework that it should retrieve a new InputConnection + // now that the engine is detached. The new InputConnection will be null, which + // signifies that this View does not process input (until a new engine is attached). + // TODO(mattcarroll): once this is proven to work, move this line ot TextInputPlugin + textInputPlugin.getInputMethodManager().restartInput(this); + + // Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface. + flutterEngine.getRenderer().detachFromRenderSurface(); + flutterEngine = null; + + // TODO(mattcarroll): clear the surface when JNI doesn't blow up +// if (isSurfaceAvailableForRendering) { +// Canvas canvas = surfaceHolder.lockCanvas(); +// canvas.drawColor(Color.RED); +// surfaceHolder.unlockCanvasAndPost(canvas); +// } + } + + private boolean isAttachedToFlutterEngine() { + return flutterEngine != null; + } + + /** + * Send the current {@link Locale} configuration to Flutter. + * + * FlutterEngine must be non-null when this method is invoked. + */ + @SuppressWarnings("deprecation") + private void sendLocalesToFlutter(Configuration config) { + List locales = new ArrayList<>(); + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + LocaleList localeList = config.getLocales(); + int localeCount = localeList.size(); + for (int index = 0; index < localeCount; ++index) { + Locale locale = localeList.get(index); + locales.add(locale); + } + } else { + locales.add(config.locale); + } + flutterEngine.getLocalizationChannel().sendLocales(locales); + } + + /** + * Send various user preferences of this Android device to Flutter. + * + * For example, sends the user's "text scale factor" preferences, as well as the user's clock + * format preference. + * + * FlutterEngine must be non-null when this method is invoked. + */ + private void sendUserSettingsToFlutter() { + flutterEngine.getSettingsChannel().startMessage() + .setTextScaleFactor(getResources().getConfiguration().fontScale) + .setUse24HourFormat(DateFormat.is24HourFormat(getContext())) + .send(); + } + + // TODO(mattcarroll): consider introducing a system channel for this communication instead of JNI + private void sendViewportMetricsToFlutter() { + Log.d(TAG, "sendViewportMetricsToFlutter()"); + if (!isAttachedToFlutterEngine()) { + Log.w(TAG, "Tried to send viewport metrics from Android to Flutter but this FlutterView was not attached to a FlutterEngine."); + return; + } + + viewportMetrics.devicePixelRatio = getResources().getDisplayMetrics().density; + flutterEngine.getRenderer().setViewportMetrics(viewportMetrics); + } + + /** + * Render modes for a {@link FlutterView}. + */ + public enum RenderMode { + /** + * {@code RenderMode}, which paints a Flutter UI to a {@link android.view.SurfaceView}. + * This mode has the best performance, but a {@code FlutterView} in this mode cannot be positioned + * between 2 other Android {@code View}s in the z-index, nor can it be animated/transformed. + * Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are required, + * developers should strongly prefer this render mode. + */ + surface, + /** + * {@code RenderMode}, which paints a Flutter UI to a {@link android.graphics.SurfaceTexture}. + * This mode is not as performant as {@link RenderMode#surface}, but a {@code FlutterView} in this + * mode can be animated and transformed, as well as positioned in the z-index between 2+ other + * Android {@code Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture} + * are required, developers should strongly prefer the {@link RenderMode#surface} render mode. + */ + texture + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java new file mode 100644 index 0000000000000..2b941f3d01a05 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -0,0 +1,280 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine; + +import android.content.Context; +import android.support.annotation.NonNull; + +import io.flutter.app.FlutterPluginRegistry; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; +import io.flutter.embedding.engine.systemchannels.KeyEventChannel; +import io.flutter.embedding.engine.systemchannels.LifecycleChannel; +import io.flutter.embedding.engine.systemchannels.LocalizationChannel; +import io.flutter.embedding.engine.systemchannels.NavigationChannel; +import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.embedding.engine.systemchannels.SettingsChannel; +import io.flutter.embedding.engine.systemchannels.SystemChannel; +import io.flutter.embedding.engine.systemchannels.TextInputChannel; + +/** + * A single Flutter execution environment. + * + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + * + * A {@code FlutterEngine} can execute in the background, or it can be rendered to the screen by + * using the accompanying {@link FlutterRenderer}. Rendering can be started and stopped, thus + * allowing a {@code FlutterEngine} to move from UI interaction to data-only processing and then + * back to UI interaction. + * + * Multiple {@code FlutterEngine}s may exist, execute Dart code, and render UIs within a single + * Android app. + * + * To start running Flutter within this {@code FlutterEngine}, get a reference to this engine's + * {@link DartExecutor} and then use {@link DartExecutor#executeDartEntrypoint(DartExecutor.DartEntrypoint)}. + * The {@link DartExecutor#executeDartEntrypoint(DartExecutor.DartEntrypoint)} method must not be + * invoked twice on the same {@code FlutterEngine}. + * + * To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a + * {@link FlutterRenderer} and then attach a {@link FlutterRenderer.RenderSurface}. Consider using + * a {@link io.flutter.embedding.android.FlutterView} as a {@link FlutterRenderer.RenderSurface}. + */ +// TODO(mattcarroll): re-evaluate system channel APIs - some are not well named or differentiated +public class FlutterEngine { + private static final String TAG = "FlutterEngine"; + + @NonNull + private final FlutterJNI flutterJNI; + @NonNull + private final FlutterRenderer renderer; + @NonNull + private final DartExecutor dartExecutor; + @NonNull + private final FlutterPluginRegistry pluginRegistry; + + // System channels. + @NonNull + private final AccessibilityChannel accessibilityChannel; + @NonNull + private final KeyEventChannel keyEventChannel; + @NonNull + private final LifecycleChannel lifecycleChannel; + @NonNull + private final LocalizationChannel localizationChannel; + @NonNull + private final NavigationChannel navigationChannel; + @NonNull + private final PlatformChannel platformChannel; + @NonNull + private final SettingsChannel settingsChannel; + @NonNull + private final SystemChannel systemChannel; + @NonNull + private final TextInputChannel textInputChannel; + + private final EngineLifecycleListener engineLifecycleListener = new EngineLifecycleListener() { + @SuppressWarnings("unused") + public void onPreEngineRestart() { + pluginRegistry.onPreEngineRestart(); + } + }; + + /** + * Constructs a new {@code FlutterEngine}. + * + * A new {@code FlutterEngine} does not execute any Dart code automatically. See + * {@link #getDartExecutor()} and {@link DartExecutor#executeDartEntrypoint(DartExecutor.DartEntrypoint)} + * to begin executing Dart code within this {@code FlutterEngine}. + * + * A new {@code FlutterEngine} will not display any UI until a + * {@link io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface} is registered. See + * {@link #getRenderer()} and {@link FlutterRenderer#attachToRenderSurface(FlutterRenderer.RenderSurface)}. + * + * A new {@code FlutterEngine} does not come with any Flutter plugins attached. To attach plugins, + * see {@link #getPluginRegistry()}. + * + * A new {@code FlutterEngine} does come with all default system channels attached. + */ + public FlutterEngine(@NonNull Context context) { + this.flutterJNI = new FlutterJNI(); + flutterJNI.addEngineLifecycleListener(engineLifecycleListener); + attachToJni(); + + this.dartExecutor = new DartExecutor(flutterJNI); + this.dartExecutor.onAttachedToJNI(); + + // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if possible. + this.renderer = new FlutterRenderer(flutterJNI); + + accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI); + keyEventChannel = new KeyEventChannel(dartExecutor); + lifecycleChannel = new LifecycleChannel(dartExecutor); + localizationChannel = new LocalizationChannel(dartExecutor); + navigationChannel = new NavigationChannel(dartExecutor); + platformChannel = new PlatformChannel(dartExecutor); + settingsChannel = new SettingsChannel(dartExecutor); + systemChannel = new SystemChannel(dartExecutor); + textInputChannel = new TextInputChannel(dartExecutor); + + this.pluginRegistry = new FlutterPluginRegistry(this, context); + } + + private void attachToJni() { + // TODO(mattcarroll): update native call to not take in "isBackgroundView" + flutterJNI.attachToNative(false); + + if (!isAttachedToJni()) { + throw new RuntimeException("FlutterEngine failed to attach to its native Object reference."); + } + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean isAttachedToJni() { + return flutterJNI.isAttached(); + } + + /** + * Detaches this {@code FlutterEngine} from Flutter's native implementation, but allows + * reattachment later. + * + * // TODO(mattcarroll): document use-cases for this behavior. + */ + public void detachFromJni() { + pluginRegistry.detach(); + dartExecutor.onDetachedFromJNI(); + flutterJNI.removeEngineLifecycleListener(engineLifecycleListener); + } + + /** + * Cleans up all components within this {@code FlutterEngine} and then detaches from Flutter's + * native implementation. + * + * This {@code FlutterEngine} instance should be discarded after invoking this method. + */ + public void destroy() { + pluginRegistry.destroy(); + dartExecutor.onDetachedFromJNI(); + flutterJNI.removeEngineLifecycleListener(engineLifecycleListener); + flutterJNI.detachFromNativeAndReleaseResources(); + } + + /** + * The Dart execution context associated with this {@code FlutterEngine}. + * + * The {@link DartExecutor} can be used to start executing Dart code from a given entrypoint. + * See {@link DartExecutor#executeDartEntrypoint(DartExecutor.DartEntrypoint)}. + * + * Use the {@link DartExecutor} to connect any desired message channels and method channels + * to facilitate communication between Android and Dart/Flutter. + */ + @NonNull + public DartExecutor getDartExecutor() { + return dartExecutor; + } + + /** + * The rendering system associated with this {@code FlutterEngine}. + * + * To render a Flutter UI that is produced by this {@code FlutterEngine}'s Dart code, attach + * a {@link io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface} to this + * {@link FlutterRenderer}. + */ + @NonNull + public FlutterRenderer getRenderer() { + return renderer; + } + + /** + * System channel that sends accessibility requests and events from Flutter to Android. + */ + @NonNull + public AccessibilityChannel getAccessibilityChannel() { + return accessibilityChannel; + } + + /** + * System channel that sends key events from Android to Flutter. + */ + @NonNull + public KeyEventChannel getKeyEventChannel() { + return keyEventChannel; + } + + /** + * System channel that sends Android lifecycle events to Flutter. + */ + @NonNull + public LifecycleChannel getLifecycleChannel() { + return lifecycleChannel; + } + + /** + * System channel that sends locale data from Android to Flutter. + */ + @NonNull + public LocalizationChannel getLocalizationChannel() { + return localizationChannel; + } + + /** + * System channel that sends Flutter navigation commands from Android to Flutter. + */ + @NonNull + public NavigationChannel getNavigationChannel() { + return navigationChannel; + } + + /** + * System channel that sends platform-oriented requests and information to Flutter, + * e.g., requests to play sounds, requests for haptics, system chrome settings, etc. + */ + @NonNull + public PlatformChannel getPlatformChannel() { + return platformChannel; + } + + /** + * System channel that sends platform/user settings from Android to Flutter, e.g., + * time format, scale factor, etc. + */ + @NonNull + public SettingsChannel getSettingsChannel() { + return settingsChannel; + } + + /** + * System channel that sends memory pressure warnings from Android to Flutter. + */ + @NonNull + public SystemChannel getSystemChannel() { + return systemChannel; + } + + /** + * System channel that sends and receives text input requests and state. + */ + @NonNull + public TextInputChannel getTextInputChannel() { + return textInputChannel; + } + + // TODO(mattcarroll): propose a robust story for plugin backward compability and future facing API. + @NonNull + public FlutterPluginRegistry getPluginRegistry() { + return pluginRegistry; + } + + /** + * Lifecycle callbacks for Flutter engine lifecycle events. + */ + public interface EngineLifecycleListener { + /** + * Lifecycle callback invoked before a hot restart of the Flutter engine. + */ + void onPreEngineRestart(); + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java new file mode 100644 index 0000000000000..66e98087c4e5c --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -0,0 +1,586 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.util.Log; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Set; + +import io.flutter.embedding.engine.dart.PlatformMessageHandler; +import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.view.AccessibilityBridge; + +/** + * Interface between Flutter embedding's Java code and Flutter engine's C/C++ code. + * + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + * + * Flutter's engine is built with C/C++. The Android Flutter embedding is responsible for + * coordinating Android OS events and app user interactions with the C/C++ engine. Such coordination + * requires messaging from an Android app in Java code to the C/C++ engine code. This + * communication requires a JNI (Java Native Interface) API to cross the Java/native boundary. + * + * The entirety of Flutter's JNI API is codified in {@code FlutterJNI}. There are multiple reasons + * that all such calls are centralized in one class. First, JNI calls are inherently static and + * contain no Java implementation, therefore there is little reason to associate calls with different + * classes. Second, every JNI call must be registered in C/C++ code and this registration becomes + * more complicated with every additional Java class that contains JNI calls. Third, most Android + * developers are not familiar with native development or JNI intricacies, therefore it is in the + * interest of future maintenance to reduce the API surface that includes JNI declarations. Thus, + * all Flutter JNI calls are centralized in {@code FlutterJNI}. + * + * Despite the fact that individual JNI calls are inherently static, there is state that exists + * within {@code FlutterJNI}. Most calls within {@code FlutterJNI} correspond to a specific + * "platform view", of which there may be many. Therefore, each {@code FlutterJNI} instance holds + * onto a "native platform view ID" after {@link #attachToNative(boolean)}, which is shared with + * the native C/C++ engine code. That ID is passed to every platform-view-specific native method. + * ID management is handled within {@code FlutterJNI} so that developers don't have to hold onto + * that ID. + * + * To connect part of an Android app to Flutter's C/C++ engine, instantiate a {@code FlutterJNI} and + * then attach it to the native side: + * + * {@code + * // Instantiate FlutterJNI and attach to the native side. + * FlutterJNI flutterJNI = new FlutterJNI(); + * flutterJNI.attachToNative(); + * + * // Use FlutterJNI as desired. + * flutterJNI.dispatchPointerDataPacket(...); + * + * // Destroy the connection to the native side and cleanup. + * flutterJNI.detachFromNativeAndReleaseResources(); + * } + * + * To provide a visual, interactive surface for Flutter rendering and touch events, register a + * {@link FlutterRenderer.RenderSurface} with {@link #setRenderSurface(FlutterRenderer.RenderSurface)} + * + * To receive callbacks for certain events that occur on the native side, register listeners: + * + *
    + *
  1. {@link #addEngineLifecycleListener(EngineLifecycleListener)}
  2. + *
  3. {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}
  4. + *
+ * + * To facilitate platform messages between Java and Dart running in Flutter, register a handler: + * + * {@link #setPlatformMessageHandler(PlatformMessageHandler)} + * + * To invoke a native method that is not associated with a platform view, invoke it statically: + * + * {@code + * String uri = FlutterJNI.nativeGetObservatoryUri(); + * } + */ +public class FlutterJNI { + private static final String TAG = "FlutterJNI"; + + @UiThread + public static native boolean nativeGetIsSoftwareRenderingEnabled(); + + @UiThread + public static native String nativeGetObservatoryUri(); + + private Long nativePlatformViewId; + private FlutterRenderer.RenderSurface renderSurface; + private AccessibilityDelegate accessibilityDelegate; + private PlatformMessageHandler platformMessageHandler; + private final Set engineLifecycleListeners = new HashSet<>(); + private final Set firstFrameListeners = new HashSet<>(); + + /** + * Sets the {@link FlutterRenderer.RenderSurface} delegate for the attached Flutter context. + * + * Flutter expects a user interface to exist on the platform side (Android), and that interface + * is expected to offer some capabilities that Flutter depends upon. The {@link FlutterRenderer.RenderSurface} + * interface represents those expectations. + * + * If an app includes a user interface that renders a Flutter UI then a {@link FlutterRenderer.RenderSurface} + * should be set (this is the typical Flutter scenario). If no UI is being rendered, such as a + * Flutter app that is running Dart code in the background, then no registration may be necessary. + * + * If no {@link FlutterRenderer.RenderSurface} is registered then related messages coming from + * Flutter will be dropped (ignored). + */ + @UiThread + public void setRenderSurface(@Nullable FlutterRenderer.RenderSurface renderSurface) { + this.renderSurface = renderSurface; + } + + /** + * Sets the {@link AccessibilityDelegate} for the attached Flutter context. + * + * The {@link AccessibilityDelegate} is responsible for maintaining an Android-side cache of + * Flutter's semantics tree and custom accessibility actions. This cache should be hooked up + * to Android's accessibility system. + * + * See {@link AccessibilityBridge} for an example of an {@link AccessibilityDelegate} and the + * surrounding responsibilities. + */ + @UiThread + public void setAccessibilityDelegate(@Nullable AccessibilityDelegate accessibilityDelegate) { + this.accessibilityDelegate = accessibilityDelegate; + } + + /** + * Invoked by native to send semantics tree updates from Flutter to Android. + * + * The {@code buffer} and {@code strings} form a communication protocol that is implemented here: + * https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207 + */ + @SuppressWarnings("unused") + @UiThread + private void updateSemantics(ByteBuffer buffer, String[] strings) { + if (accessibilityDelegate != null) { + accessibilityDelegate.updateSemantics(buffer, strings); + } + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) + } + + /** + * Invoked by native to send new custom accessibility events from Flutter to Android. + * + * The {@code buffer} and {@code strings} form a communication protocol that is implemented here: + * https://github.com/flutter/engine/blob/master/shell/platform/android/platform_view_android.cc#L207 + * + * // TODO(cbracken): expand these docs to include more actionable information. + */ + @SuppressWarnings("unused") + @UiThread + private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + if (accessibilityDelegate != null) { + accessibilityDelegate.updateCustomAccessibilityActions(buffer, strings); + } + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) + } + + // Called by native to notify first Flutter frame rendered. + @SuppressWarnings("unused") + @UiThread + private void onFirstFrame() { + if (renderSurface != null) { + renderSurface.onFirstFrameRendered(); + } + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) + + for (OnFirstFrameRenderedListener listener : firstFrameListeners) { + listener.onFirstFrameRendered(); + } + } + + /** + * Sets the handler for all platform messages that come from the attached platform view to Java. + * + * Communication between a specific Flutter context (Dart) and the host platform (Java) is + * accomplished by passing messages. Messages can be sent from Java to Dart with the corresponding + * {@code FlutterJNI} methods: + *
    + *
  • {@link #dispatchPlatformMessage(String, ByteBuffer, int, int)}
  • + *
  • {@link #dispatchEmptyPlatformMessage(String, int)}
  • + *
+ * + * {@code FlutterJNI} is also the recipient of all platform messages sent from its attached + * Flutter context (AKA platform view). {@code FlutterJNI} does not know what to do with these + * messages, so a handler is exposed to allow these messages to be processed in whatever manner is + * desired: + * + * {@code setPlatformMessageHandler(PlatformMessageHandler)} + * + * If a message is received but no {@link PlatformMessageHandler} is registered, that message will + * be dropped (ignored). Therefore, when using {@code FlutterJNI} to integrate a Flutter context + * in an app, a {@link PlatformMessageHandler} must be registered for 2-way Java/Dart communication + * to operate correctly. Moreover, the handler must be implemented such that fundamental platform + * messages are handled as expected. See {@link FlutterNativeView} for an example implementation. + */ + @UiThread + public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { + this.platformMessageHandler = platformMessageHandler; + } + + // Called by native. + @SuppressWarnings("unused") + private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { + if (platformMessageHandler != null) { + platformMessageHandler.handleMessageFromDart(channel, message, replyId); + } + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) + } + + // Called by native to respond to a platform message that we sent. + @SuppressWarnings("unused") + private void handlePlatformMessageResponse(int replyId, byte[] reply) { + if (platformMessageHandler != null) { + platformMessageHandler.handlePlatformMessageResponse(replyId, reply); + } + // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391) + } + + @UiThread + public void addEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.add(engineLifecycleListener); + } + + @UiThread + public void removeEngineLifecycleListener(@NonNull EngineLifecycleListener engineLifecycleListener) { + engineLifecycleListeners.remove(engineLifecycleListener); + } + + @UiThread + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.add(listener); + } + + @UiThread + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + firstFrameListeners.remove(listener); + } + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) + //----- Start from FlutterView ----- + @UiThread + public void onSurfaceCreated(@NonNull Surface surface) { + ensureAttachedToNative(); + nativeSurfaceCreated(nativePlatformViewId, surface); + } + + private native void nativeSurfaceCreated(long nativePlatformViewId, Surface surface); + + @UiThread + public void onSurfaceChanged(int width, int height) { + ensureAttachedToNative(); + nativeSurfaceChanged(nativePlatformViewId, width, height); + } + + private native void nativeSurfaceChanged(long nativePlatformViewId, int width, int height); + + @UiThread + public void onSurfaceDestroyed() { + ensureAttachedToNative(); + nativeSurfaceDestroyed(nativePlatformViewId); + } + + private native void nativeSurfaceDestroyed(long nativePlatformViewId); + + @UiThread + public void setViewportMetrics( + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft + ) { + ensureAttachedToNative(); + nativeSetViewportMetrics( + nativePlatformViewId, + devicePixelRatio, + physicalWidth, + physicalHeight, + physicalPaddingTop, + physicalPaddingRight, + physicalPaddingBottom, + physicalPaddingLeft, + physicalViewInsetTop, + physicalViewInsetRight, + physicalViewInsetBottom, + physicalViewInsetLeft + ); + } + + private native void nativeSetViewportMetrics( + long nativePlatformViewId, + float devicePixelRatio, + int physicalWidth, + int physicalHeight, + int physicalPaddingTop, + int physicalPaddingRight, + int physicalPaddingBottom, + int physicalPaddingLeft, + int physicalViewInsetTop, + int physicalViewInsetRight, + int physicalViewInsetBottom, + int physicalViewInsetLeft + ); + + @UiThread + public Bitmap getBitmap() { + ensureAttachedToNative(); + return nativeGetBitmap(nativePlatformViewId); + } + + private native Bitmap nativeGetBitmap(long nativePlatformViewId); + + @UiThread + public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { + ensureAttachedToNative(); + nativeDispatchPointerDataPacket(nativePlatformViewId, buffer, position); + } + + private native void nativeDispatchPointerDataPacket(long nativePlatformViewId, + ByteBuffer buffer, + int position); + + public void dispatchSemanticsAction(int id, @NonNull AccessibilityBridge.Action action) { + dispatchSemanticsAction(id, action, null); + } + + public void dispatchSemanticsAction(int id, @NonNull AccessibilityBridge.Action action, @Nullable Object args) { + ensureAttachedToNative(); + + ByteBuffer encodedArgs = null; + int position = 0; + if (args != null) { + encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); + position = encodedArgs.position(); + } + dispatchSemanticsAction(id, action.value, encodedArgs, position); + } + + @UiThread + public void dispatchSemanticsAction(int id, int action, ByteBuffer args, int argsPosition) { + ensureAttachedToNative(); + nativeDispatchSemanticsAction(nativePlatformViewId, id, action, args, argsPosition); + } + + private native void nativeDispatchSemanticsAction( + long nativePlatformViewId, + int id, + int action, + ByteBuffer args, + int argsPosition + ); + + @UiThread + public void setSemanticsEnabled(boolean enabled) { + ensureAttachedToNative(); + nativeSetSemanticsEnabled(nativePlatformViewId, enabled); + } + + private native void nativeSetSemanticsEnabled(long nativePlatformViewId, boolean enabled); + + @UiThread + public void setAccessibilityFeatures(int flags) { + ensureAttachedToNative(); + nativeSetAccessibilityFeatures(nativePlatformViewId, flags); + } + + private native void nativeSetAccessibilityFeatures(long nativePlatformViewId, int flags); + + @UiThread + public void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + ensureAttachedToNative(); + nativeRegisterTexture(nativePlatformViewId, textureId, surfaceTexture); + } + + private native void nativeRegisterTexture(long nativePlatformViewId, long textureId, SurfaceTexture surfaceTexture); + + @UiThread + public void markTextureFrameAvailable(long textureId) { + ensureAttachedToNative(); + nativeMarkTextureFrameAvailable(nativePlatformViewId, textureId); + } + + private native void nativeMarkTextureFrameAvailable(long nativePlatformViewId, long textureId); + + @UiThread + public void unregisterTexture(long textureId) { + ensureAttachedToNative(); + nativeUnregisterTexture(nativePlatformViewId, textureId); + } + + private native void nativeUnregisterTexture(long nativePlatformViewId, long textureId); + //------- End from FlutterView ----- + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) + //------ Start from FlutterNativeView ---- + public boolean isAttached() { + return nativePlatformViewId != null; + } + + @UiThread + public void attachToNative(boolean isBackgroundView) { + ensureNotAttachedToNative(); + nativePlatformViewId = nativeAttach(this, isBackgroundView); + } + + private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView); + + @UiThread + public void detachFromNativeAndReleaseResources() { + ensureAttachedToNative(); + nativeDestroy(nativePlatformViewId); + nativePlatformViewId = null; + } + + private native void nativeDestroy(long nativePlatformViewId); + + @UiThread + public void runBundleAndSnapshotFromLibrary( + @NonNull String[] prioritizedBundlePaths, + @Nullable String entrypointFunctionName, + @Nullable String pathToEntrypointFunction, + @NonNull AssetManager assetManager + ) { + ensureAttachedToNative(); + nativeRunBundleAndSnapshotFromLibrary( + nativePlatformViewId, + prioritizedBundlePaths, + entrypointFunctionName, + pathToEntrypointFunction, + assetManager + ); + } + + private native void nativeRunBundleAndSnapshotFromLibrary( + long nativePlatformViewId, + @NonNull String[] prioritizedBundlePaths, + @Nullable String entrypointFunctionName, + @Nullable String pathToEntrypointFunction, + @NonNull AssetManager manager + ); + + @UiThread + public void dispatchEmptyPlatformMessage(String channel, int responseId) { + if (isAttached()) { + nativeDispatchEmptyPlatformMessage(nativePlatformViewId, channel, responseId); + } else { + Log.w(TAG, "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: " + channel + ". Response ID: " + responseId); + } + } + + // Send an empty platform message to Dart. + private native void nativeDispatchEmptyPlatformMessage( + long nativePlatformViewId, + String channel, + int responseId + ); + + @UiThread + public void dispatchPlatformMessage(String channel, ByteBuffer message, int position, int responseId) { + if (isAttached()) { + nativeDispatchPlatformMessage( + nativePlatformViewId, + channel, + message, + position, + responseId + ); + } else { + Log.w(TAG, "Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: " + channel + ". Response ID: " + responseId); + } + } + + // Send a data-carrying platform message to Dart. + private native void nativeDispatchPlatformMessage( + long nativePlatformViewId, + String channel, + ByteBuffer message, + int position, + int responseId + ); + + @UiThread + public void invokePlatformMessageEmptyResponseCallback(int responseId) { + if (isAttached()) { + nativeInvokePlatformMessageEmptyResponseCallback(nativePlatformViewId, responseId); + } else { + Log.w(TAG, "Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: " + responseId); + } + } + + // Send an empty response to a platform message received from Dart. + private native void nativeInvokePlatformMessageEmptyResponseCallback( + long nativePlatformViewId, + int responseId + ); + + @UiThread + public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) { + if (isAttached()) { + nativeInvokePlatformMessageResponseCallback( + nativePlatformViewId, + responseId, + message, + position + ); + } else { + Log.w(TAG, "Tried to send a platform message response, but FlutterJNI was detached from native C++. Could not send. Response ID: " + responseId); + } + } + + // Send a data-carrying response to a platform message received from Dart. + private native void nativeInvokePlatformMessageResponseCallback( + long nativePlatformViewId, + int responseId, + ByteBuffer message, + int position + ); + //------ End from FlutterNativeView ---- + + // TODO(mattcarroll): rename comments after refactor is done and their origin no longer matters (https://github.com/flutter/flutter/issues/25533) + //------ Start from Engine --- + // Called by native. + @SuppressWarnings("unused") + private void onPreEngineRestart() { + for (EngineLifecycleListener listener : engineLifecycleListeners) { + listener.onPreEngineRestart(); + } + } + //------ End from Engine --- + + private void ensureNotAttachedToNative() { + if (nativePlatformViewId != null) { + throw new RuntimeException("Cannot execute operation because FlutterJNI is attached to native."); + } + } + + private void ensureAttachedToNative() { + if (nativePlatformViewId == null) { + throw new RuntimeException("Cannot execute operation because FlutterJNI is not attached to native."); + } + } + + /** + * Delegate responsible for creating and updating Android-side caches of Flutter's semantics + * tree and custom accessibility actions. + * + * {@link AccessibilityBridge} is an example of an {@code AccessibilityDelegate}. + */ + public interface AccessibilityDelegate { + /** + * Sends new custom accessibility actions from Flutter to Android. + * + * Implementers are expected to maintain an Android-side cache of custom accessibility actions. + * This method provides new actions to add to that cache. + */ + void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings); + + /** + * Sends new {@code SemanticsNode} information from Flutter to Android. + * + * Implementers are expected to maintain an Android-side cache of Flutter's semantics tree. + * This method provides updates from Flutter for the Android-side semantics tree cache. + */ + void updateSemantics(ByteBuffer buffer, String[] strings); + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java new file mode 100644 index 0000000000000..6e5ec8c7ba7bc --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java @@ -0,0 +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. + +package io.flutter.embedding.engine; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; + +import java.util.*; + +/** + * Arguments that can be delivered to the Flutter shell when it is created. + *

+ * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + *

+ * The term "shell" refers to the native code that adapts Flutter to different platforms. Flutter's + * Android Java code initializes a native "shell" and passes these arguments to that native shell + * when it is initialized. See {@link io.flutter.view.FlutterMain#ensureInitializationComplete(Context, String[])} + * for more information. + */ +@SuppressWarnings({"WeakerAccess", "unused"}) +public class FlutterShellArgs { + public static final String ARG_KEY_TRACE_STARTUP = "trace-startup"; + public static final String ARG_TRACE_STARTUP = "--trace-startup"; + public static final String ARG_KEY_START_PAUSED = "start-paused"; + public static final String ARG_START_PAUSED = "--start-paused"; + public static final String ARG_KEY_USE_TEST_FONTS = "use-test-fonts"; + public static final String ARG_USE_TEST_FONTS = "--use-test-fonts"; + public static final String ARG_KEY_ENABLE_DART_PROFILING = "enable-dart-profiling"; + public static final String ARG_ENABLE_DART_PROFILING = "--enable-dart-profiling"; + public static final String ARG_KEY_ENABLE_SOFTWARE_RENDERING = "enable-software-rendering"; + public static final String ARG_ENABLE_SOFTWARE_RENDERING = "--enable-software-rendering"; + public static final String ARG_KEY_SKIA_DETERMINISTIC_RENDERING = "skia-deterministic-rendering"; + public static final String ARG_SKIA_DETERMINISTIC_RENDERING = "--skia-deterministic-rendering"; + public static final String ARG_KEY_TRACE_SKIA = "trace-skia"; + public static final String ARG_TRACE_SKIA = "--trace-skia"; + public static final String ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION = "dump-skp-on-shader-compilation"; + public static final String ARG_DUMP_SHADER_SKP_ON_SHADER_COMPILATION = "--dump-skp-on-shader-compilation"; + public static final String ARG_KEY_VERBOSE_LOGGING = "verbose-logging"; + public static final String ARG_VERBOSE_LOGGING = "--verbose-logging"; + + @NonNull + public static FlutterShellArgs fromIntent(@NonNull Intent intent) { + // Before adding more entries to this list, consider that arbitrary + // Android applications can generate intents with extra data and that + // there are many security-sensitive args in the binary. + // TODO(mattcarroll): I left this warning as-is, but we should clarify what exactly this warning is warning against. + ArrayList args = new ArrayList<>(); + + if (intent.getBooleanExtra(ARG_KEY_TRACE_STARTUP, false)) { + args.add(ARG_TRACE_STARTUP); + } + if (intent.getBooleanExtra(ARG_KEY_START_PAUSED, false)) { + args.add(ARG_START_PAUSED); + } + if (intent.getBooleanExtra(ARG_KEY_USE_TEST_FONTS, false)) { + args.add(ARG_USE_TEST_FONTS); + } + if (intent.getBooleanExtra(ARG_KEY_ENABLE_DART_PROFILING, false)) { + args.add(ARG_ENABLE_DART_PROFILING); + } + if (intent.getBooleanExtra(ARG_KEY_ENABLE_SOFTWARE_RENDERING, false)) { + args.add(ARG_ENABLE_SOFTWARE_RENDERING); + } + if (intent.getBooleanExtra(ARG_KEY_SKIA_DETERMINISTIC_RENDERING, false)) { + args.add(ARG_SKIA_DETERMINISTIC_RENDERING); + } + if (intent.getBooleanExtra(ARG_KEY_TRACE_SKIA, false)) { + args.add(ARG_TRACE_SKIA); + } + if (intent.getBooleanExtra(ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION, false)) { + args.add(ARG_KEY_DUMP_SHADER_SKP_ON_SHADER_COMPILATION); + } + if (intent.getBooleanExtra(ARG_KEY_VERBOSE_LOGGING, false)) { + args.add(ARG_VERBOSE_LOGGING); + } + + return new FlutterShellArgs(args); + } + + private Set args; + + /** + * Creates a set of Flutter shell arguments from a given {@code String[]} array. + * The given arguments are automatically de-duplicated. + */ + public FlutterShellArgs(@NonNull String[] args) { + this.args = new HashSet<>(Arrays.asList(args)); + } + + /** + * Creates a set of Flutter shell arguments from a given {@code List}. + * The given arguments are automatically de-duplicated. + */ + public FlutterShellArgs(@NonNull List args) { + this.args = new HashSet<>(args); + } + + /** + * Creates a set of Flutter shell arguments from a given {@code Set}. + */ + public FlutterShellArgs(@NonNull Set args) { + this.args = new HashSet<>(args); + } + + /** + * Adds the given {@code arg} to this set of arguments. + * @param arg argument to add + */ + public void add(@NonNull String arg) { + args.add(arg); + } + + /** + * Removes the given {@code arg} from this set of arguments. + * @param arg argument to remove + */ + public void remove(@NonNull String arg) { + args.remove(arg); + } + + /** + * Returns a new {@code String[]} array which contains each of the arguments + * within this {@code FlutterShellArgs}. + * + * @return array of arguments + */ + @NonNull + public String[] toArray() { + String[] argsArray = new String[args.size()]; + return args.toArray(argsArray); + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java new file mode 100644 index 0000000000000..f7cdd855d77de --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java @@ -0,0 +1,285 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.dart; + +import android.content.res.AssetManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.nio.ByteBuffer; + +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.view.FlutterCallbackInformation; + +/** + * Configures, bootstraps, and starts executing Dart code. + *

+ * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + *

+ * To specify a top-level Dart function to execute, use a {@link DartEntrypoint} to tell + * {@link DartExecutor} where to find the Dart code to execute, and which Dart function to use as + * the entrypoint. To execute the entrypoint, pass the {@link DartEntrypoint} to + * {@link #executeDartEntrypoint(DartEntrypoint)}. + *

+ * To specify a Dart callback to execute, use a {@link DartCallback}. A given Dart callback must + * be registered with the Dart VM to be invoked by a {@link DartExecutor}. To execute the callback, + * pass the {@link DartCallback} to {@link #executeDartCallback(DartCallback)}. + * TODO(mattcarroll): add a reference to docs about background/plugin execution + *

+ * Once started, a {@link DartExecutor} cannot be stopped. The associated Dart code will execute + * until it completes, or until the {@link io.flutter.embedding.engine.FlutterEngine} that owns + * this {@link DartExecutor} is destroyed. + */ +public class DartExecutor implements BinaryMessenger { + private static final String TAG = "DartExecutor"; + + @NonNull + private final FlutterJNI flutterJNI; + @NonNull + private final DartMessenger messenger; + private boolean isApplicationRunning = false; + + public DartExecutor(@NonNull FlutterJNI flutterJNI) { + this.flutterJNI = flutterJNI; + this.messenger = new DartMessenger(flutterJNI); + } + + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this + * {@link DartExecutor} attaches to JNI. + *

+ * When attached to JNI, this {@link DartExecutor} begins handling 2-way communication to/from + * the Dart execution context. This communication is facilitate via 2 APIs: + *

    + *
  • {@link BinaryMessenger}, which sends messages to Dart
  • + *
  • {@link PlatformMessageHandler}, which receives messages from Dart
  • + *
+ */ + public void onAttachedToJNI() { + flutterJNI.setPlatformMessageHandler(messenger); + } + + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this + * {@link DartExecutor} detaches from JNI. + *

+ * When detached from JNI, this {@link DartExecutor} stops handling 2-way communication to/from + * the Dart execution context. + */ + public void onDetachedFromJNI() { + flutterJNI.setPlatformMessageHandler(null); + } + + /** + * Is this {@link DartExecutor} currently executing Dart code? + * + * @return true if Dart code is being executed, false otherwise + */ + public boolean isExecutingDart() { + return isApplicationRunning; + } + + /** + * Starts executing Dart code based on the given {@code dartEntrypoint}. + *

+ * See {@link DartEntrypoint} for configuration options. + * + * @param dartEntrypoint specifies which Dart function to run, and where to find it + */ + public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) { + if (isApplicationRunning) { + Log.w(TAG, "Attempted to run a DartExecutor that is already running."); + return; + } + + flutterJNI.runBundleAndSnapshotFromLibrary( + new String[]{ + dartEntrypoint.pathToPrimaryBundle, + dartEntrypoint.pathToFallbackBundle + }, + dartEntrypoint.dartEntrypointFunctionName, + null, + dartEntrypoint.androidAssetManager + ); + + isApplicationRunning = true; + } + + /** + * Starts executing Dart code based on the given {@code dartCallback}. + *

+ * See {@link DartCallback} for configuration options. + * + * @param dartCallback specifies which Dart callback to run, and where to find it + */ + public void executeDartCallback(@NonNull DartCallback dartCallback) { + if (isApplicationRunning) { + Log.w(TAG, "Attempted to run a DartExecutor that is already running."); + return; + } + + flutterJNI.runBundleAndSnapshotFromLibrary( + new String[]{ + dartCallback.pathToPrimaryBundle, + dartCallback.pathToFallbackBundle + }, + dartCallback.callbackHandle.callbackName, + dartCallback.callbackHandle.callbackLibraryPath, + dartCallback.androidAssetManager + ); + + isApplicationRunning = true; + } + + //------ START BinaryMessenger ----- + + /** + * Sends the given {@code message} from Android to Dart over the given {@code channel}. + * + * @param channel the name of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message bytes + */ + @Override + public void send(@NonNull String channel, @Nullable ByteBuffer message) { + messenger.send(channel, message, null); + } + + /** + * Sends the given {@code messages} from Android to Dart over the given {@code channel} and + * then has the provided {@code callback} invoked when the Dart side responds. + * + * @param channel the name of the logical channel used for the message. + * @param message the message payload, a direct-allocated {@link ByteBuffer} with the message bytes + * between position zero and current position, or null. + * @param callback a callback invoked when the Dart application responds to the message + */ + @Override + public void send(@NonNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback) { + messenger.send(channel, message, callback); + } + + /** + * Sets the given {@link io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler} as the + * singular handler for all incoming messages received from the Dart side of this Dart execution + * context. + * + * @param channel the name of the channel. + * @param handler a {@link BinaryMessageHandler} to be invoked on incoming messages, or null. + */ + @Override + public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) { + messenger.setMessageHandler(channel, handler); + } + //------ END BinaryMessenger ----- + + /** + * Configuration options that specify which Dart entrypoint function is executed and where + * to find that entrypoint and other assets required for Dart execution. + */ + public static class DartEntrypoint { + /** + * Standard Android AssetManager, provided from some {@code Context} or {@code Resources}. + */ + @NonNull + public final AssetManager androidAssetManager; + + /** + * The first place that Dart will look for a given function or asset. + */ + @NonNull + public final String pathToPrimaryBundle; + + /** + * A secondary fallback location that Dart will look for a given function or asset. + */ + @Nullable + public final String pathToFallbackBundle; + + /** + * The name of a Dart function to execute. + */ + @NonNull + public final String dartEntrypointFunctionName; + + public DartEntrypoint( + @NonNull AssetManager androidAssetManager, + @NonNull String pathToBundle, + @NonNull String dartEntrypointFunctionName + ) { + this( + androidAssetManager, + pathToBundle, + null, + dartEntrypointFunctionName + ); + } + + public DartEntrypoint( + @NonNull AssetManager androidAssetManager, + @NonNull String pathToPrimaryBundle, + @Nullable String pathToFallbackBundle, + @NonNull String dartEntrypointFunctionName + ) { + this.androidAssetManager = androidAssetManager; + this.pathToPrimaryBundle = pathToPrimaryBundle; + this.pathToFallbackBundle = pathToFallbackBundle; + this.dartEntrypointFunctionName = dartEntrypointFunctionName; + } + } + + /** + * Configuration options that specify which Dart callback function is executed and where + * to find that callback and other assets required for Dart execution. + */ + public static class DartCallback { + /** + * Standard Android AssetManager, provided from some {@code Context} or {@code Resources}. + */ + public final AssetManager androidAssetManager; + + /** + * The first place that Dart will look for a given function or asset. + */ + public final String pathToPrimaryBundle; + + /** + * A secondary fallback location that Dart will look for a given function or asset. + */ + public final String pathToFallbackBundle; + + /** + * A Dart callback that was previously registered with the Dart VM. + */ + public final FlutterCallbackInformation callbackHandle; + + public DartCallback( + @NonNull AssetManager androidAssetManager, + @NonNull String pathToPrimaryBundle, + @NonNull FlutterCallbackInformation callbackHandle + ) { + this( + androidAssetManager, + pathToPrimaryBundle, + null, + callbackHandle + ); + } + + public DartCallback( + @NonNull AssetManager androidAssetManager, + @NonNull String pathToPrimaryBundle, + @Nullable String pathToFallbackBundle, + @NonNull FlutterCallbackInformation callbackHandle + ) { + this.androidAssetManager = androidAssetManager; + this.pathToPrimaryBundle = pathToPrimaryBundle; + this.pathToFallbackBundle = pathToFallbackBundle; + this.callbackHandle = callbackHandle; + } + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java new file mode 100644 index 0000000000000..e9fe0a7cd8c09 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java @@ -0,0 +1,133 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.dart; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.plugin.common.BinaryMessenger; + +/** + * Message conduit for 2-way communication between Android and Dart. + *

+ * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + *

+ * See {@link BinaryMessenger}, which sends messages from Android to Dart + *

+ * See {@link PlatformMessageHandler}, which handles messages to Android from Dart + */ +class DartMessenger implements BinaryMessenger, PlatformMessageHandler { + private static final String TAG = "DartMessenger"; + + @NonNull + private final FlutterJNI flutterJNI; + @NonNull + private final Map messageHandlers; + @NonNull + private final Map pendingReplies; + private int nextReplyId = 1; + + DartMessenger(@NonNull FlutterJNI flutterJNI) { + this.flutterJNI = flutterJNI; + this.messageHandlers = new HashMap<>(); + this.pendingReplies = new HashMap<>(); + } + + @Override + public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) { + if (handler == null) { + messageHandlers.remove(channel); + } else { + messageHandlers.put(channel, handler); + } + } + + @Override + public void send(@NonNull String channel, @NonNull ByteBuffer message) { + send(channel, message, null); + } + + @Override + public void send( + @NonNull String channel, + @Nullable ByteBuffer message, + @Nullable BinaryMessenger.BinaryReply callback + ) { + int replyId = 0; + if (callback != null) { + replyId = nextReplyId++; + pendingReplies.put(replyId, callback); + } + if (message == null) { + flutterJNI.dispatchEmptyPlatformMessage(channel, replyId); + } else { + flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId); + } + } + + @Override + public void handleMessageFromDart( + @NonNull final String channel, + @Nullable byte[] message, + final int replyId + ) { + BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel); + if (handler != null) { + try { + final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); + handler.onMessage(buffer, new Reply(flutterJNI, replyId)); + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception in binary message listener", ex); + flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); + } + } else { + flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); + } + } + + @Override + public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) { + BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId); + if (callback != null) { + try { + callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); + } catch (Exception ex) { + Log.e(TAG, "Uncaught exception in binary message reply handler", ex); + } + } + } + + private static class Reply implements BinaryMessenger.BinaryReply { + @NonNull + private final FlutterJNI flutterJNI; + private final int replyId; + private final AtomicBoolean done = new AtomicBoolean(false); + + Reply(@NonNull FlutterJNI flutterJNI, int replyId) { + this.flutterJNI = flutterJNI; + this.replyId = replyId; + } + + @Override + public void reply(ByteBuffer reply) { + if (done.getAndSet(true)) { + throw new IllegalStateException("Reply already submitted"); + } + if (reply == null) { + flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); + } else { + flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position()); + } + } + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java new file mode 100644 index 0000000000000..ea1011c09abcf --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java @@ -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. + +package io.flutter.embedding.engine.dart; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public interface PlatformMessageHandler { + void handleMessageFromDart(final String channel, byte[] message, final int replyId); + + void handlePlatformMessageResponse(int replyId, byte[] reply); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java new file mode 100644 index 0000000000000..5fd3521e545df --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -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. + +package io.flutter.embedding.engine.renderer; + +import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +import android.os.Build; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicLong; + +import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.view.TextureRegistry; + +/** + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + * + * {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to create an + * interactive Flutter UI. + * + * {@code FlutterRenderer} manages textures for rendering, and forwards some Java calls to native Flutter + * code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out + * certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy. + * + * {@link FlutterView} is an implementation of a {@link RenderSurface}. + */ +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class FlutterRenderer implements TextureRegistry { + + private final FlutterJNI flutterJNI; + private final AtomicLong nextTextureId = new AtomicLong(0L); + private RenderSurface renderSurface; + + public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { + this.flutterJNI = flutterJNI; + } + + public void attachToRenderSurface(@NonNull RenderSurface renderSurface) { + // TODO(mattcarroll): determine desired behavior when attaching to an already attached renderer + if (this.renderSurface != null) { + detachFromRenderSurface(); + } + + this.renderSurface = renderSurface; + this.renderSurface.attachToRenderer(this); + this.flutterJNI.setRenderSurface(renderSurface); + } + + public void detachFromRenderSurface() { + // TODO(mattcarroll): determine desired behavior if we're asked to detach without first being attached + if (this.renderSurface != null) { + this.renderSurface.detachFromRenderer(); + this.renderSurface = null; + surfaceDestroyed(); + this.flutterJNI.setRenderSurface(null); + } + } + + public void addOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + flutterJNI.addOnFirstFrameRenderedListener(listener); + } + + public void removeOnFirstFrameRenderedListener(@NonNull OnFirstFrameRenderedListener listener) { + flutterJNI.removeOnFirstFrameRenderedListener(listener); + } + + //------ START TextureRegistry IMPLEMENTATION ----- + // TODO(mattcarroll): detachFromGLContext requires API 16. Create solution for earlier APIs. + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public SurfaceTextureEntry createSurfaceTexture() { + final SurfaceTexture surfaceTexture = new SurfaceTexture(0); + surfaceTexture.detachFromGLContext(); + final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry( + nextTextureId.getAndIncrement(), + surfaceTexture + ); + registerTexture(entry.id(), surfaceTexture); + return entry; + } + + final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextureEntry { + private final long id; + private final SurfaceTexture surfaceTexture; + private boolean released; + + SurfaceTextureRegistryEntry(long id, SurfaceTexture surfaceTexture) { + this.id = id; + this.surfaceTexture = surfaceTexture; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // The callback relies on being executed on the UI thread (unsynchronised read of mNativeView + // and also the engine code check for platform thread in Shell::OnPlatformViewMarkTextureFrameAvailable), + // so we explicitly pass a Handler for the current thread. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener, new Handler()); + } else { + // Android documentation states that the listener can be called on an arbitrary thread. + // But in practice, versions of Android that predate the newer API will call the listener + // on the thread where the SurfaceTexture was constructed. + this.surfaceTexture.setOnFrameAvailableListener(onFrameListener); + } + } + + private SurfaceTexture.OnFrameAvailableListener onFrameListener = new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture texture) { + if (released) { + // Even though we make sure to unregister the callback before releasing, as of Android O + // SurfaceTexture has a data race when accessing the callback, so the callback may + // still be called by a stale reference after released==true and mNativeView==null. + return; + } + markTextureFrameAvailable(id); + } + }; + + @Override + public SurfaceTexture surfaceTexture() { + return surfaceTexture; + } + + @Override + public long id() { + return id; + } + + @Override + public void release() { + if (released) { + return; + } + surfaceTexture.release(); + unregisterTexture(id); + released = true; + } + } + //------ END TextureRegistry IMPLEMENTATION ---- + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceCreated(Surface surface) { + flutterJNI.onSurfaceCreated(surface); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceChanged(int width, int height) { + flutterJNI.onSurfaceChanged(width, height); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void surfaceDestroyed() { + flutterJNI.onSurfaceDestroyed(); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void setViewportMetrics(@NonNull ViewportMetrics viewportMetrics) { + flutterJNI.setViewportMetrics( + viewportMetrics.devicePixelRatio, + viewportMetrics.width, + viewportMetrics.height, + viewportMetrics.paddingTop, + viewportMetrics.paddingRight, + viewportMetrics.paddingBottom, + viewportMetrics.paddingLeft, + viewportMetrics.viewInsetTop, + viewportMetrics.viewInsetRight, + viewportMetrics.viewInsetBottom, + viewportMetrics.viewInsetLeft + ); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public Bitmap getBitmap() { + return flutterJNI.getBitmap(); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void dispatchPointerDataPacket(ByteBuffer buffer, int position) { + flutterJNI.dispatchPointerDataPacket(buffer, position); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void registerTexture(long textureId, SurfaceTexture surfaceTexture) { + flutterJNI.registerTexture(textureId, surfaceTexture); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void markTextureFrameAvailable(long textureId) { + flutterJNI.markTextureFrameAvailable(textureId); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + private void unregisterTexture(long textureId) { + flutterJNI.unregisterTexture(textureId); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public boolean isSoftwareRenderingEnabled() { + return FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void setAccessibilityFeatures(int flags) { + flutterJNI.setAccessibilityFeatures(flags); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void setSemanticsEnabled(boolean enabled) { + flutterJNI.setSemanticsEnabled(enabled); + } + + // TODO(mattcarroll): describe the native behavior that this invokes + public void dispatchSemanticsAction(int id, + int action, + ByteBuffer args, + int argsPosition) { + flutterJNI.dispatchSemanticsAction( + id, + action, + args, + argsPosition + ); + } + + /** + * Delegate used in conjunction with a {@link FlutterRenderer} to create an interactive Flutter + * UI. + * + * A {@code RenderSurface} is responsible for carrying out behaviors that are needed by a + * corresponding {@link FlutterRenderer}. + * + * A {@code RenderSurface} also receives callbacks for important events, e.g., + * {@link #onFirstFrameRendered()}. + */ + public interface RenderSurface { + /** + * Invoked by the owner of this {@code RenderSurface} when it wants to begin rendering + * a Flutter UI to this {@code RenderSurface}. + * + * The details of how rendering is handled is an implementation detail. + */ + void attachToRenderer(@NonNull FlutterRenderer renderer); + + /** + * Invoked by the owner of this {@code RenderSurface} when it no longer wants to render + * a Flutter UI to this {@code RenderSurface}. + * + * This method will cease any on-going rendering from Flutter to this {@code RenderSurface}. + */ + void detachFromRenderer(); + + /** + * The {@link FlutterRenderer} corresponding to this {@code RenderSurface} has painted its + * first frame since being initialized. + * + * "Initialized" refers to Flutter engine initialization, not the first frame after attaching + * to the {@link FlutterRenderer}. Therefore, the first frame may have already rendered by + * the time a {@code RenderSurface} has called {@link #attachToRenderSurface(RenderSurface)} + * on a {@link FlutterRenderer}. In such a situation, {@code #onFirstFrameRendered()} will + * never be called. + */ + void onFirstFrameRendered(); + } + + /** + * Mutable data structure that holds all viewport metrics properties that Flutter cares about. + * + * All distance measurements, e.g., width, height, padding, viewInsets, are measured in device + * pixels, not logical pixels. + */ + public static final class ViewportMetrics { + public float devicePixelRatio = 1.0f; + public int width = 0; + public int height = 0; + public int paddingTop = 0; + public int paddingRight = 0; + public int paddingBottom = 0; + public int paddingLeft = 0; + public int viewInsetTop = 0; + public int viewInsetRight = 0; + public int viewInsetBottom = 0; + public int viewInsetLeft = 0; + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java new file mode 100644 index 0000000000000..46d271b7f171b --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.renderer; + +/** + * Listener invoked after Flutter paints its first frame since being initialized. + * + * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE. + * IF YOU USE IT, WE WILL BREAK YOU. + */ +public interface OnFirstFrameRenderedListener { + /** + * A {@link FlutterRenderer} has painted its first frame since being initialized. + * + * This method will not be invoked if this listener is added after the first frame is rendered. + */ + void onFirstFrameRendered(); +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java new file mode 100644 index 0000000000000..c4252e234df0a --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java @@ -0,0 +1,179 @@ +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.StandardMessageCodec; +import io.flutter.view.AccessibilityBridge; + +/** + * System channel that sends accessibility requests and events from Flutter to Android. + *

+ * See {@link AccessibilityMessageHandler}, which lists all accessibility requests and + * events that might be sent from Flutter to the Android platform. + */ +public class AccessibilityChannel { + @NonNull + public final BasicMessageChannel channel; + @NonNull + public final FlutterJNI flutterJNI; + @Nullable + private AccessibilityMessageHandler handler; + + private final BasicMessageChannel.MessageHandler parsingMessageHandler = new BasicMessageChannel.MessageHandler() { + @Override + public void onMessage(Object message, BasicMessageChannel.Reply reply) { + // If there is no handler to respond to this message then we don't need to + // parse it. Return. + if (handler == null) { + return; + } + + @SuppressWarnings("unchecked") + final HashMap annotatedEvent = (HashMap) message; + final String type = (String) annotatedEvent.get("type"); + @SuppressWarnings("unchecked") + final HashMap data = (HashMap) annotatedEvent.get("data"); + + switch (type) { + case "announce": + String announceMessage = (String) data.get("message"); + if (announceMessage != null) { + handler.announce(announceMessage); + } + break; + case "tap": { + Integer nodeId = (Integer) annotatedEvent.get("nodeId"); + if (nodeId != null) { + handler.onTap(nodeId); + } + break; + } + case "longPress": { + Integer nodeId = (Integer) annotatedEvent.get("nodeId"); + if (nodeId != null) { + handler.onLongPress(nodeId); + } + break; + } + case "tooltip": { + String tooltipMessage = (String) data.get("message"); + if (tooltipMessage != null) { + handler.onTooltip(tooltipMessage); + } + break; + } + } + } + }; + + /** + * Constructs an {@code AccessibilityChannel} that connects Android to the Dart code + * running in {@code dartExecutor}. + * + * The given {@code dartExecutor} is permitted to be idle or executing code. + * + * See {@link DartExecutor}. + */ + public AccessibilityChannel(@NonNull DartExecutor dartExecutor, @NonNull FlutterJNI flutterJNI) { + channel = new BasicMessageChannel<>(dartExecutor, "flutter/accessibility", StandardMessageCodec.INSTANCE); + channel.setMessageHandler(parsingMessageHandler); + this.flutterJNI = flutterJNI; + } + + /** + * Informs Flutter that the Android OS currently has accessibility enabled. + * + * To accommodate enabled accessibility, this method instructs Flutter to activate + * its semantics tree, which forms the basis of Flutter's accessibility support. + */ + public void onAndroidAccessibilityEnabled() { + flutterJNI.setSemanticsEnabled(true); + } + + /** + * Informs Flutter that the Android OS currently has accessibility disabled. + * + * Given that accessibility is not required at this time, this method instructs Flutter + * to deactivate its semantics tree. + */ + public void onAndroidAccessibilityDisabled() { + flutterJNI.setSemanticsEnabled(false); + } + + /** + * Instructs Flutter to activate/deactivate accessibility features corresponding to the + * flags provided by {@code accessibilityFeatureFlags}. + */ + public void setAccessibilityFeatures(int accessibilityFeatureFlags) { + flutterJNI.setAccessibilityFeatures(accessibilityFeatureFlags); + } + + /** + * Instructs Flutter to perform the given {@code action} on the {@code SemanticsNode} + * referenced by the given {@code virtualViewId}. + * + * One might wonder why Flutter would need to be instructed that the user wants to perform + * an action. When the user is touching the screen in accessibility mode, Android takes over the + * touch input, categorizing input as one of a many accessibility gestures. Therefore, Flutter + * does not have an opportunity to react to said touch input. Instead, Flutter must be notified + * by Android of the desired action. Additionally, some accessibility systems use other input + * methods, such as speech, to take virtual actions. Android interprets those requests and then + * instructs the app to take the appropriate action. + */ + public void dispatchSemanticsAction(int virtualViewId, @NonNull AccessibilityBridge.Action action) { + flutterJNI.dispatchSemanticsAction(virtualViewId, action); + } + + /** + * Instructs Flutter to perform the given {@code action} on the {@code SemanticsNode} + * referenced by the given {@code virtualViewId}, passing the given {@code args}. + */ + public void dispatchSemanticsAction(int virtualViewId, @NonNull AccessibilityBridge.Action action, @Nullable Object args) { + flutterJNI.dispatchSemanticsAction(virtualViewId, action, args); + } + + /** + * Sets the {@link AccessibilityMessageHandler} which receives all events and requests + * that are parsed from the underlying accessibility channel. + */ + public void setAccessibilityMessageHandler(@Nullable AccessibilityMessageHandler handler) { + this.handler = handler; + flutterJNI.setAccessibilityDelegate(handler); + } + + /** + * Handler that receives accessibility messages sent from Flutter to Android + * through a given {@link AccessibilityChannel}. + * + * To register an {@code AccessibilityMessageHandler} with a {@link AccessibilityChannel}, + * see {@link AccessibilityChannel#setAccessibilityMessageHandler(AccessibilityMessageHandler)}. + */ + public interface AccessibilityMessageHandler extends FlutterJNI.AccessibilityDelegate { + /** + * The Dart application would like the given {@code message} to be announced. + */ + void announce(@NonNull String message); + + /** + * The user has tapped on the widget with the given {@code nodeId}. + */ + void onTap(int nodeId); + + /** + * The user has long pressed on the widget with the given {@code nodeId}. + */ + void onLongPress(int nodeId); + + /** + * The user has opened a tooltip. + */ + void onTooltip(@NonNull String message); + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java new file mode 100644 index 0000000000000..5b90d2a761d34 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java @@ -0,0 +1,112 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.KeyEvent; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.JSONMessageCodec; + +/** + * TODO(mattcarroll): fill in javadoc for KeyEventChannel. + */ +public class KeyEventChannel { + + @NonNull + public final BasicMessageChannel channel; + + public KeyEventChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/keyevent", JSONMessageCodec.INSTANCE); + } + + public void keyUp(@NonNull FlutterKeyEvent keyEvent) { + Map message = new HashMap<>(); + message.put("type", "keyup"); + message.put("keymap", "android"); + encodeKeyEvent(keyEvent, message); + + channel.send(message); + } + + public void keyDown(@NonNull FlutterKeyEvent keyEvent) { + Map message = new HashMap<>(); + message.put("type", "keydown"); + message.put("keymap", "android"); + encodeKeyEvent(keyEvent, message); + + channel.send(message); + } + + private void encodeKeyEvent(@NonNull FlutterKeyEvent event, @NonNull Map message) { + message.put("flags", event.flags); + message.put("plainCodePoint", event.plainCodePoint); + message.put("codePoint", event.codePoint); + message.put("keyCode", event.keyCode); + message.put("scanCode", event.scanCode); + message.put("metaState", event.metaState); + if (event.complexCharacter != null) { + message.put("character", event.complexCharacter.toString()); + } + } + + /** + * Key event as defined by Flutter. + */ + public static class FlutterKeyEvent { + public final int flags; + public final int plainCodePoint; + public final int codePoint; + public final int keyCode; + @Nullable + public final Character complexCharacter; + public final int scanCode; + public final int metaState; + + public FlutterKeyEvent( + @NonNull KeyEvent androidKeyEvent + ) { + this(androidKeyEvent, null); + } + + public FlutterKeyEvent( + @NonNull KeyEvent androidKeyEvent, + @Nullable Character complexCharacter + ) { + this( + androidKeyEvent.getFlags(), + androidKeyEvent.getUnicodeChar(0x0), + androidKeyEvent.getUnicodeChar(), + androidKeyEvent.getKeyCode(), + complexCharacter, + androidKeyEvent.getScanCode(), + androidKeyEvent.getMetaState() + ); + } + + public FlutterKeyEvent( + int flags, + int plainCodePoint, + int codePoint, + int keyCode, + @Nullable Character complexCharacter, + int scanCode, + int metaState + ) { + this.flags = flags; + this.plainCodePoint = plainCodePoint; + this.codePoint = codePoint; + this.keyCode = keyCode; + this.complexCharacter = complexCharacter; + this.scanCode = scanCode; + this.metaState = metaState; + } + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java new file mode 100644 index 0000000000000..7f3e4c8513a9d --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java @@ -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. + +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.StringCodec; + +/** + * TODO(mattcarroll): fill in javadoc for LifecycleChannel. + */ +public class LifecycleChannel { + + @NonNull + public final BasicMessageChannel channel; + + public LifecycleChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/lifecycle", StringCodec.INSTANCE); + } + + public void appIsInactive() { + channel.send("AppLifecycleState.inactive"); + } + + public void appIsResumed() { + channel.send("AppLifecycleState.resumed"); + } + + public void appIsPaused() { + channel.send("AppLifecycleState.paused"); + } + +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java new file mode 100644 index 0000000000000..33acaace59592 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java @@ -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. + +package io.flutter.embedding.engine.systemchannels; + +import android.os.Build; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodChannel; + +/** + * Sends the platform's locales to Dart. + */ +public class LocalizationChannel { + + @NonNull + public final MethodChannel channel; + + public LocalizationChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/localization", JSONMethodCodec.INSTANCE); + } + + /** + * Send the given {@code locales} to Dart. + */ + public void sendLocales(List locales) { + List data = new ArrayList<>(); + for (Locale locale : locales) { + data.add(locale.getLanguage()); + data.add(locale.getCountry()); + // locale.getScript() was added in API 21. + data.add(Build.VERSION.SDK_INT >= 21 ? locale.getScript() : ""); + data.add(locale.getVariant()); + } + channel.invokeMethod("setLocale", data); + } + +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java new file mode 100644 index 0000000000000..f81f24204d392 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/NavigationChannel.java @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodChannel; + +/** + * TODO(mattcarroll): fill in javadoc for NavigationChannel. + */ +public class NavigationChannel { + + @NonNull + public final MethodChannel channel; + + public NavigationChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/navigation", JSONMethodCodec.INSTANCE); + } + + public void setInitialRoute(String initialRoute) { + channel.invokeMethod("setInitialRoute", initialRoute); + } + + public void pushRoute(String route) { + channel.invokeMethod("pushRoute", route); + } + + public void popRoute() { + channel.invokeMethod("popRoute", null); + } + + public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) { + channel.setMethodCallHandler(handler); + } + +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java new file mode 100644 index 0000000000000..9de3fe53646da --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java @@ -0,0 +1,616 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.systemchannels; + +import android.content.pm.ActivityInfo; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +/** + * System channel that receives requests for host platform behavior, e.g., haptic and sound + * effects, system chrome configurations, and clipboard interaction. + */ +public class PlatformChannel { + @NonNull + public final MethodChannel channel; + @Nullable + private PlatformMessageHandler platformMessageHandler; + + private final MethodChannel.MethodCallHandler parsingMethodCallHandler = new MethodChannel.MethodCallHandler() { + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + if (platformMessageHandler == null) { + // If no explicit PlatformMessageHandler has been registered then we don't + // need to forward this call to an API. Return. + return; + } + + String method = call.method; + Object arguments = call.arguments; + try { + switch (method) { + case "SystemSound.play": + try { + SoundType soundType = SoundType.fromValue((String) arguments); + platformMessageHandler.playSystemSound(soundType); + result.success(null); + } catch (NoSuchFieldException exception) { + // The desired sound type does not exist. + result.error("error", exception.getMessage(), null); + } + break; + case "HapticFeedback.vibrate": + try { + HapticFeedbackType feedbackType = HapticFeedbackType.fromValue((String) arguments); + platformMessageHandler.vibrateHapticFeedback(feedbackType); + result.success(null); + } catch (NoSuchFieldException exception) { + // The desired feedback type does not exist. + result.error("error", exception.getMessage(), null); + } + break; + case "SystemChrome.setPreferredOrientations": + try { + int androidOrientation = decodeOrientations((JSONArray) arguments); + platformMessageHandler.setPreferredOrientations(androidOrientation); + result.success(null); + } catch (JSONException | NoSuchFieldException exception) { + // JSONException: One or more expected fields were either omitted or referenced an invalid type. + // NoSuchFieldException: One or more expected fields were either omitted or referenced an invalid type. + result.error("error", exception.getMessage(), null); + } + break; + case "SystemChrome.setApplicationSwitcherDescription": + try { + AppSwitcherDescription description = decodeAppSwitcherDescription((JSONObject) arguments); + platformMessageHandler.setApplicationSwitcherDescription(description); + result.success(null); + } catch (JSONException exception) { + // One or more expected fields were either omitted or referenced an invalid type. + result.error("error", exception.getMessage(), null); + } + break; + case "SystemChrome.setEnabledSystemUIOverlays": + try { + List overlays = decodeSystemUiOverlays((JSONArray) arguments); + platformMessageHandler.showSystemOverlays(overlays); + result.success(null); + } catch (JSONException | NoSuchFieldException exception) { + // JSONException: One or more expected fields were either omitted or referenced an invalid type. + // NoSuchFieldException: One or more of the overlay names are invalid. + result.error("error", exception.getMessage(), null); + } + break; + case "SystemChrome.restoreSystemUIOverlays": + platformMessageHandler.restoreSystemUiOverlays(); + result.success(null); + break; + case "SystemChrome.setSystemUIOverlayStyle": + try { + SystemChromeStyle systemChromeStyle = decodeSystemChromeStyle((JSONObject) arguments); + platformMessageHandler.setSystemUiOverlayStyle(systemChromeStyle); + result.success(null); + } catch (JSONException | NoSuchFieldException exception) { + // JSONException: One or more expected fields were either omitted or referenced an invalid type. + // NoSuchFieldException: One or more of the brightness names are invalid. + result.error("error", exception.getMessage(), null); + } + break; + case "SystemNavigator.pop": + platformMessageHandler.popSystemNavigator(); + result.success(null); + break; + case "Clipboard.getData": { + String contentFormatName = (String) arguments; + ClipboardContentFormat clipboardFormat = null; + if (contentFormatName != null) { + try { + clipboardFormat = ClipboardContentFormat.fromValue(contentFormatName); + } catch (NoSuchFieldException exception) { + // An unsupported content format was requested. Return failure. + result.error("error", "No such clipboard content format: " + contentFormatName, null); + } + } + + CharSequence clipboardContent = platformMessageHandler.getClipboardData(clipboardFormat); + if (clipboardContent != null) { + JSONObject response = new JSONObject(); + response.put("text", clipboardContent); + result.success(response); + } else { + result.success(null); + } + break; + } + case "Clipboard.setData": { + String clipboardContent = ((JSONObject) arguments).getString("text"); + platformMessageHandler.setClipboardData(clipboardContent); + result.success(null); + break; + } + default: + result.notImplemented(); + break; + } + } catch (JSONException e) { + result.error("error", "JSON error: " + e.getMessage(), null); + } + } + }; + + /** + * Constructs a {@code PlatformChannel} that connects Android to the Dart code + * running in {@code dartExecutor}. + * + * The given {@code dartExecutor} is permitted to be idle or executing code. + * + * See {@link DartExecutor}. + */ + public PlatformChannel(@NonNull DartExecutor dartExecutor) { + channel = new MethodChannel(dartExecutor, "flutter/platform", JSONMethodCodec.INSTANCE); + channel.setMethodCallHandler(parsingMethodCallHandler); + } + + /** + * Sets the {@link PlatformMessageHandler} which receives all events and requests + * that are parsed from the underlying platform channel. + */ + public void setPlatformMessageHandler(@Nullable PlatformMessageHandler platformMessageHandler) { + this.platformMessageHandler = platformMessageHandler; + } + + // TODO(mattcarroll): add support for IntDef annotations, then add @ScreenOrientation + + /** + * Decodes a series of orientations to an aggregate desired orientation. + * + * @throws JSONException if {@code encodedOrientations} does not contain expected keys and value types. + * @throws NoSuchFieldException if any given encoded orientation is not a valid orientation name. + */ + private int decodeOrientations(@NonNull JSONArray encodedOrientations) throws JSONException, NoSuchFieldException { + int requestedOrientation = 0x00; + int firstRequestedOrientation = 0x00; + for (int index = 0; index < encodedOrientations.length(); index += 1) { + String encodedOrientation = encodedOrientations.getString(index); + DeviceOrientation orientation = DeviceOrientation.fromValue(encodedOrientation); + + switch (orientation) { + case PORTRAIT_UP: + requestedOrientation |= 0x01; + break; + case PORTRAIT_DOWN: + requestedOrientation |= 0x04; + break; + case LANDSCAPE_LEFT: + requestedOrientation |= 0x02; + break; + case LANDSCAPE_RIGHT: + requestedOrientation |= 0x08; + break; + } + + if (firstRequestedOrientation == 0x00) { + firstRequestedOrientation = requestedOrientation; + } + } + + switch (requestedOrientation) { + case 0x00: + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + case 0x01: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + case 0x02: + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + case 0x04: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case 0x05: + return ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT; + case 0x08: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + case 0x0a: + return ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE; + case 0x0b: + return ActivityInfo.SCREEN_ORIENTATION_USER; + case 0x0f: + return ActivityInfo.SCREEN_ORIENTATION_FULL_USER; + case 0x03: // portraitUp and landscapeLeft + case 0x06: // portraitDown and landscapeLeft + case 0x07: // portraitUp, portraitDown, and landscapeLeft + case 0x09: // portraitUp and landscapeRight + case 0x0c: // portraitDown and landscapeRight + case 0x0d: // portraitUp, portraitDown, and landscapeRight + case 0x0e: // portraitDown, landscapeLeft, and landscapeRight + // Android can't describe these cases, so just default to whatever the first + // specified value was. + switch (firstRequestedOrientation) { + case 0x01: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + case 0x02: + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + case 0x04: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case 0x08: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + } + } + + // Execution should never get this far, but if it does then we default + // to a portrait orientation. + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } + + private AppSwitcherDescription decodeAppSwitcherDescription(@NonNull JSONObject encodedDescription) throws JSONException { + int color = encodedDescription.getInt("primaryColor"); + if (color != 0) { // 0 means color isn't set, use system default + color = color | 0xFF000000; // color must be opaque if set + } + String label = encodedDescription.getString("label"); + return new AppSwitcherDescription(color, label); + } + + /** + * Decodes a list of JSON-encoded overlays to a list of {@link SystemUiOverlay}. + * + * @throws JSONException if {@code encodedSystemUiOverlay} does not contain expected keys and value types. + * @throws NoSuchFieldException if any of the given encoded overlay names are invalid. + */ + private List decodeSystemUiOverlays(@NonNull JSONArray encodedSystemUiOverlay) throws JSONException, NoSuchFieldException { + List overlays = new ArrayList<>(); + for (int i = 0; i < encodedSystemUiOverlay.length(); ++i) { + String encodedOverlay = encodedSystemUiOverlay.getString(i); + SystemUiOverlay overlay = SystemUiOverlay.fromValue(encodedOverlay); + switch(overlay) { + case TOP_OVERLAYS: + overlays.add(SystemUiOverlay.TOP_OVERLAYS); + break; + case BOTTOM_OVERLAYS: + overlays.add(SystemUiOverlay.BOTTOM_OVERLAYS); + break; + } + } + return overlays; + } + + /** + * Decodes a JSON-encoded {@code encodedStyle} to a {@link SystemChromeStyle}. + * + * @throws JSONException if {@code encodedStyle} does not contain expected keys and value types. + * @throws NoSuchFieldException if any provided brightness name is invalid. + */ + private SystemChromeStyle decodeSystemChromeStyle(@NonNull JSONObject encodedStyle) throws JSONException, NoSuchFieldException { + Brightness systemNavigationBarIconBrightness = null; + // TODO(mattcarroll): add color annotation + Integer systemNavigationBarColor = null; + // TODO(mattcarroll): add color annotation + Integer systemNavigationBarDividerColor = null; + Brightness statusBarIconBrightness = null; + // TODO(mattcarroll): add color annotation + Integer statusBarColor = null; + + if (!encodedStyle.isNull("systemNavigationBarIconBrightness")) { + systemNavigationBarIconBrightness = Brightness.fromValue(encodedStyle.getString("systemNavigationBarIconBrightness")); + } + + if (!encodedStyle.isNull("systemNavigationBarColor")) { + systemNavigationBarColor = encodedStyle.getInt("systemNavigationBarColor"); + } + + if (!encodedStyle.isNull("statusBarIconBrightness")) { + statusBarIconBrightness = Brightness.fromValue(encodedStyle.getString("statusBarIconBrightness")); + } + + if (!encodedStyle.isNull("statusBarColor")) { + statusBarColor = encodedStyle.getInt("statusBarColor"); + } + + if (!encodedStyle.isNull("systemNavigationBarDividerColor")) { + systemNavigationBarDividerColor = encodedStyle.getInt("systemNavigationBarDividerColor"); + } + + return new SystemChromeStyle( + statusBarColor, + statusBarIconBrightness, + systemNavigationBarColor, + systemNavigationBarIconBrightness, + systemNavigationBarDividerColor + ); + } + + /** + * Handler that receives platform messages sent from Flutter to Android + * through a given {@link PlatformChannel}. + * + * To register a {@code PlatformMessageHandler} with a {@link PlatformChannel}, + * see {@link PlatformChannel#setPlatformMessageHandler(PlatformMessageHandler)}. + */ + public interface PlatformMessageHandler { + /** + * The Flutter application would like to play the given {@code soundType}. + */ + void playSystemSound(@NonNull SoundType soundType); + + /** + * The Flutter application would like to play the given haptic {@code feedbackType}. + */ + void vibrateHapticFeedback(@NonNull HapticFeedbackType feedbackType); + + /** + * The Flutter application would like to display in the given {@code androidOrientation}. + */ + // TODO(mattcarroll): add @ScreenOrientation annotation + void setPreferredOrientations(int androidOrientation); + + /** + * The Flutter application would like to be displayed in Android's app switcher with + * the visual representation described in the given {@code description}. + *

+ * See the related Android documentation: + * https://developer.android.com/guide/components/activities/recents + */ + void setApplicationSwitcherDescription(@NonNull AppSwitcherDescription description); + + /** + * The Flutter application would like the Android system to display the given + * {@code overlays}. + *

+ * {@link SystemUiOverlay#TOP_OVERLAYS} refers to system overlays such as the + * status bar, while {@link SystemUiOverlay#BOTTOM_OVERLAYS} refers to system + * overlays such as the back/home/recents navigation on the bottom of the screen. + *

+ * An empty list of {@code overlays} should hide all system overlays. + */ + void showSystemOverlays(@NonNull List overlays); + + /** + * The Flutter application would like to restore the visibility of system + * overlays to the last set of overlays sent via {@link #showSystemOverlays(List)}. + *

+ * If {@link #showSystemOverlays(List)} has yet to be called, then a default + * system overlay appearance is desired: + *

+ * {@code + * View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + * } + */ + void restoreSystemUiOverlays(); + + /** + * The Flutter application would like the system chrome to present itself with + * the given {@code systemUiOverlayStyle}, i.e., the given status bar and + * navigation bar colors and brightness. + */ + void setSystemUiOverlayStyle(@NonNull SystemChromeStyle systemUiOverlayStyle); + + /** + * The Flutter application would like to pop the top item off of the Android + * app's navigation back stack. + */ + void popSystemNavigator(); + + /** + * The Flutter application would like to receive the current data in the + * clipboard and have it returned in the given {@code format}. + */ + @Nullable + CharSequence getClipboardData(@Nullable ClipboardContentFormat format); + + /** + * The Flutter application would like to set the current data in the + * clipboard to the given {@code text}. + */ + void setClipboardData(@NonNull String text); + } + + /** + * Types of sounds the Android OS can play on behalf of an application. + */ + public enum SoundType { + CLICK("SystemSoundType.click"); + + static SoundType fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (SoundType soundType : SoundType.values()) { + if (soundType.encodedName.equals(encodedName)) { + return soundType; + } + } + throw new NoSuchFieldException("No such SoundType: " + encodedName); + } + + @NonNull + private final String encodedName; + + SoundType(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * The types of haptic feedback that the Android OS can generate on behalf + * of an application. + */ + public enum HapticFeedbackType { + STANDARD(null), + LIGHT_IMPACT("HapticFeedbackType.lightImpact"), + MEDIUM_IMPACT("HapticFeedbackType.mediumImpact"), + HEAVY_IMPACT("HapticFeedbackType.heavyImpact"), + SELECTION_CLICK("HapticFeedbackType.selectionClick"); + + static HapticFeedbackType fromValue(@Nullable String encodedName) throws NoSuchFieldException { + for (HapticFeedbackType feedbackType : HapticFeedbackType.values()) { + if ((feedbackType.encodedName == null && encodedName == null) + || (feedbackType.encodedName != null && feedbackType.encodedName.equals(encodedName))) { + return feedbackType; + } + } + throw new NoSuchFieldException("No such HapticFeedbackType: " + encodedName); + } + + @Nullable + private final String encodedName; + + HapticFeedbackType(@Nullable String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * The possible desired orientations of a Flutter application. + */ + public enum DeviceOrientation { + PORTRAIT_UP("DeviceOrientation.portraitUp"), + PORTRAIT_DOWN("DeviceOrientation.portraitDown"), + LANDSCAPE_LEFT("DeviceOrientation.landscapeLeft"), + LANDSCAPE_RIGHT("DeviceOrientation.landscapeRight"); + + static DeviceOrientation fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (DeviceOrientation orientation : DeviceOrientation.values()) { + if (orientation.encodedName.equals(encodedName)) { + return orientation; + } + } + throw new NoSuchFieldException("No such DeviceOrientation: " + encodedName); + } + + @NonNull + private String encodedName; + + DeviceOrientation(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * The set of Android system UI overlays as perceived by the Flutter application. + *

+ * Android includes many more overlay options and flags than what is provided by + * {@code SystemUiOverlay}. Flutter only requires control over a subset of the + * overlays and those overlays are represented by {@code SystemUiOverlay} values. + */ + public enum SystemUiOverlay { + TOP_OVERLAYS("SystemUiOverlay.top"), + BOTTOM_OVERLAYS("SystemUiOverlay.bottom"); + + static SystemUiOverlay fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (SystemUiOverlay overlay : SystemUiOverlay.values()) { + if (overlay.encodedName.equals(encodedName)) { + return overlay; + } + } + throw new NoSuchFieldException("No such SystemUiOverlay: " + encodedName); + } + + @NonNull + private String encodedName; + + SystemUiOverlay(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * The color and label of an application that appears in Android's app switcher, AKA + * recents screen. + */ + public static class AppSwitcherDescription { + // TODO(mattcarroll): add color annotation + public final int color; + @NonNull + public final String label; + + public AppSwitcherDescription(int color, @NonNull String label) { + this.color = color; + this.label = label; + } + } + + /** + * The color and brightness of system chrome, e.g., status bar and system navigation bar. + */ + public static class SystemChromeStyle { + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer statusBarColor; + @Nullable + public final Brightness statusBarIconBrightness; + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer systemNavigationBarColor; + @Nullable + public final Brightness systemNavigationBarIconBrightness; + // TODO(mattcarroll): add color annotation + @Nullable + public final Integer systemNavigationBarDividerColor; + + public SystemChromeStyle( + @Nullable Integer statusBarColor, + @Nullable Brightness statusBarIconBrightness, + @Nullable Integer systemNavigationBarColor, + @Nullable Brightness systemNavigationBarIconBrightness, + @Nullable Integer systemNavigationBarDividerColor + ) { + this.statusBarColor = statusBarColor; + this.statusBarIconBrightness = statusBarIconBrightness; + this.systemNavigationBarColor = systemNavigationBarColor; + this.systemNavigationBarIconBrightness = systemNavigationBarIconBrightness; + this.systemNavigationBarDividerColor = systemNavigationBarDividerColor; + } + } + + public enum Brightness { + LIGHT("Brightness.light"), + DARK("Brightness.dark"); + + static Brightness fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (Brightness brightness : Brightness.values()) { + if (brightness.encodedName.equals(encodedName)) { + return brightness; + } + } + throw new NoSuchFieldException("No such Brightness: " + encodedName); + } + + @NonNull + private String encodedName; + + Brightness(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * Data formats of clipboard content. + */ + public enum ClipboardContentFormat { + PLAIN_TEXT("text/plain"); + + static ClipboardContentFormat fromValue(String encodedName) throws NoSuchFieldException { + for (ClipboardContentFormat format : ClipboardContentFormat.values()) { + if (format.encodedName.equals(encodedName)) { + return format; + } + } + throw new NoSuchFieldException("No such ClipboardContentFormat: " + encodedName); + } + + @NonNull + private String encodedName; + + ClipboardContentFormat(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java new file mode 100644 index 0000000000000..612fe8fe49a44 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/SettingsChannel.java @@ -0,0 +1,68 @@ +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.JSONMessageCodec; + +import java.util.HashMap; +import java.util.Map; + +public class SettingsChannel { + public static final String CHANNEL_NAME = "flutter/settings"; + + public final BasicMessageChannel channel; + + public SettingsChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new BasicMessageChannel<>(dartExecutor, CHANNEL_NAME, JSONMessageCodec.INSTANCE); + } + + public MessageBuilder startMessage() { + return new MessageBuilder(channel); + } + + public static class MessageBuilder { + private final BasicMessageChannel channel; + private Map message = new HashMap<>(); + + MessageBuilder(@NonNull BasicMessageChannel channel) { + this.channel = channel; + } + + public MessageBuilder setTextScaleFactor(float textScaleFactor) { + message.put("textScaleFactor", textScaleFactor); + return this; + } + + public MessageBuilder setUse24HourFormat(boolean use24HourFormat) { + message.put("alwaysUse24HourFormat", use24HourFormat); + return this; + } + + public MessageBuilder setPlatformBrightness(@NonNull PlatformBrightness brightness) { + message.put("platformBrightness", brightness.name); + return this; + } + + public void send() { + channel.send(message); + } + } + + /** + * The brightness mode of the host platform. + * + * The {@code name} property is the serialized representation of each + * brightness mode when communicated via message channel. + */ + public enum PlatformBrightness { + light("light"), + dark("dark"); + + public String name; + + PlatformBrightness(@NonNull String name) { + this.name = name; + } + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java new file mode 100644 index 0000000000000..46c85926daf1b --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/SystemChannel.java @@ -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. + +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.JSONMessageCodec; + +/** + * TODO(mattcarroll): fill in javadoc for SystemChannel. + */ +public class SystemChannel { + + @NonNull + public final BasicMessageChannel channel; + + public SystemChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/system", JSONMessageCodec.INSTANCE); + } + + public void sendMemoryPressureWarning() { + Map message = new HashMap<>(1); + message.put("type", "memoryPressure"); + channel.send(message); + } + +} diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java new file mode 100644 index 0000000000000..b23d33fd00996 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java @@ -0,0 +1,409 @@ +package io.flutter.embedding.engine.systemchannels; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.inputmethod.EditorInfo; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.HashMap; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.plugin.common.JSONMethodCodec; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +/** + * {@link TextInputChannel} is a platform channel between Android and Flutter that is used to + * communicate information about the user's text input. + *

+ * When the user presses an action button like "done" or "next", that action is sent from Android + * to Flutter through this {@link TextInputChannel}. + *

+ * When an input system in the Flutter app wants to show the keyboard, or hide it, or configure + * editing state, etc. a message is sent from Flutter to Android through this {@link TextInputChannel}. + *

+ * {@link TextInputChannel} comes with a default {@link io.flutter.plugin.common.MethodChannel.MethodCallHandler} + * that parses incoming messages from Flutter. Register a {@link TextInputMethodHandler} to respond + * to standard Flutter text input messages. + */ +public class TextInputChannel { + private static final String TAG = "TextInputChannel"; + @NonNull + public final MethodChannel channel; + @Nullable + private TextInputMethodHandler textInputMethodHandler; + + private final MethodChannel.MethodCallHandler parsingMethodHandler = new MethodChannel.MethodCallHandler() { + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + if (textInputMethodHandler == null) { + // If no explicit TextInputMethodHandler has been registered then we don't + // need to forward this call to an API. Return. + return; + } + + String method = call.method; + Object args = call.arguments; + switch (method) { + case "TextInput.show": + textInputMethodHandler.show(); + result.success(null); + break; + case "TextInput.hide": + textInputMethodHandler.hide(); + result.success(null); + break; + case "TextInput.setClient": + try { + final JSONArray argumentList = (JSONArray) args; + final int textInputClientId = argumentList.getInt(0); + final JSONObject jsonConfiguration = argumentList.getJSONObject(1); + textInputMethodHandler.setClient(textInputClientId, Configuration.fromJson(jsonConfiguration)); + result.success(null); + } catch (JSONException | NoSuchFieldException exception) { + // JSONException: missing keys or bad value types. + // NoSuchFieldException: one or more values were invalid. + result.error("error", exception.getMessage(), null); + } + break; + case "TextInput.setEditingState": + try { + final JSONObject editingState = (JSONObject) args; + textInputMethodHandler.setEditingState(TextEditState.fromJson(editingState)); + result.success(null); + } catch (JSONException exception) { + result.error("error", exception.getMessage(), null); + } + break; + case "TextInput.clearClient": + textInputMethodHandler.clearClient(); + result.success(null); + break; + default: + result.notImplemented(); + break; + } + } + }; + + /** + * Constructs a {@code TextInputChannel} that connects Android to the Dart code + * running in {@code dartExecutor}. + * + * The given {@code dartExecutor} is permitted to be idle or executing code. + * + * See {@link DartExecutor}. + */ + public TextInputChannel(@NonNull DartExecutor dartExecutor) { + this.channel = new MethodChannel(dartExecutor, "flutter/textinput", JSONMethodCodec.INSTANCE); + channel.setMethodCallHandler(parsingMethodHandler); + } + + /** + * Instructs Flutter to update its text input editing state to reflect the given configuration. + */ + public void updateEditingState(int inputClientId, String text, int selectionStart, int selectionEnd, int composingStart, int composingEnd) { + HashMap state = new HashMap<>(); + state.put("text", text); + state.put("selectionBase", selectionStart); + state.put("selectionExtent", selectionEnd); + state.put("composingBase", composingStart); + state.put("composingExtent", composingEnd); + + channel.invokeMethod( + "TextInputClient.updateEditingState", + Arrays.asList(inputClientId, state) + ); + } + + /** + * Instructs Flutter to execute a "newline" action. + */ + public void newline(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.newline") + ); + } + + /** + * Instructs Flutter to execute a "go" action. + */ + public void go(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.go") + ); + } + + /** + * Instructs Flutter to execute a "search" action. + */ + public void search(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.search") + ); + } + + /** + * Instructs Flutter to execute a "send" action. + */ + public void send(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.send") + ); + } + + /** + * Instructs Flutter to execute a "done" action. + */ + public void done(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.done") + ); + } + + /** + * Instructs Flutter to execute a "next" action. + */ + public void next(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.next") + ); + } + + /** + * Instructs Flutter to execute a "previous" action. + */ + public void previous(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.previous") + ); + } + + /** + * Instructs Flutter to execute an "unspecified" action. + */ + public void unspecifiedAction(int inputClientId) { + channel.invokeMethod( + "TextInputClient.performAction", + Arrays.asList(inputClientId, "TextInputAction.unspecified") + ); + } + + /** + * Sets the {@link TextInputMethodHandler} which receives all events and requests + * that are parsed from the underlying platform channel. + */ + public void setTextInputMethodHandler(@Nullable TextInputMethodHandler textInputMethodHandler) { + this.textInputMethodHandler = textInputMethodHandler; + } + + public interface TextInputMethodHandler { + // TODO(mattcarroll): javadoc + void show(); + + // TODO(mattcarroll): javadoc + void hide(); + + // TODO(mattcarroll): javadoc + void setClient(int textInputClientId, @NonNull Configuration configuration); + + // TODO(mattcarroll): javadoc + void setEditingState(@NonNull TextEditState editingState); + + // TODO(mattcarroll): javadoc + void clearClient(); + } + + /** + * A text editing configuration. + */ + public static class Configuration { + public static Configuration fromJson(@NonNull JSONObject json) throws JSONException, NoSuchFieldException { + final String inputActionName = json.getString("inputAction"); + if (inputActionName == null) { + throw new JSONException("Configuration JSON missing 'inputAction' property."); + } + + final Integer inputAction = inputActionFromTextInputAction(inputActionName); + return new Configuration( + json.optBoolean("obscureText"), + json.optBoolean("autocorrect", true), + TextCapitalization.fromValue(json.getString("textCapitalization")), + InputType.fromJson(json.getJSONObject("inputType")), + inputAction, + json.optString("actionLabel") + ); + } + + private static Integer inputActionFromTextInputAction(@NonNull String inputAction) { + switch (inputAction) { + case "TextInputAction.newline": + return EditorInfo.IME_ACTION_NONE; + case "TextInputAction.none": + return EditorInfo.IME_ACTION_NONE; + case "TextInputAction.unspecified": + return EditorInfo.IME_ACTION_UNSPECIFIED; + case "TextInputAction.done": + return EditorInfo.IME_ACTION_DONE; + case "TextInputAction.go": + return EditorInfo.IME_ACTION_GO; + case "TextInputAction.search": + return EditorInfo.IME_ACTION_SEARCH; + case "TextInputAction.send": + return EditorInfo.IME_ACTION_SEND; + case "TextInputAction.next": + return EditorInfo.IME_ACTION_NEXT; + case "TextInputAction.previous": + return EditorInfo.IME_ACTION_PREVIOUS; + default: + // Present default key if bad input type is given. + return EditorInfo.IME_ACTION_UNSPECIFIED; + } + } + + public final boolean obscureText; + public final boolean autocorrect; + @NonNull + public final TextCapitalization textCapitalization; + @NonNull + public final InputType inputType; + @Nullable + public final Integer inputAction; + @Nullable + public final String actionLabel; + + public Configuration( + boolean obscureText, + boolean autocorrect, + @NonNull TextCapitalization textCapitalization, + @NonNull InputType inputType, + @Nullable Integer inputAction, + @Nullable String actionLabel + ) { + this.obscureText = obscureText; + this.autocorrect = autocorrect; + this.textCapitalization = textCapitalization; + this.inputType = inputType; + this.inputAction = inputAction; + this.actionLabel = actionLabel; + } + } + + /** + * A text input type. + * + * If the {@link #type} is {@link TextInputType#NUMBER}, this {@code InputType} also + * reports whether that number {@link #isSigned} and {@link #isDecimal}. + */ + public static class InputType { + @NonNull + public static InputType fromJson(@NonNull JSONObject json) throws JSONException, NoSuchFieldException { + return new InputType( + TextInputType.fromValue(json.getString("name")), + json.optBoolean("signed", false), + json.optBoolean("decimal", false) + ); + } + + @NonNull + public final TextInputType type; + public final boolean isSigned; + public final boolean isDecimal; + + public InputType(@NonNull TextInputType type, boolean isSigned, boolean isDecimal) { + this.type = type; + this.isSigned = isSigned; + this.isDecimal = isDecimal; + } + } + + /** + * Types of text input. + */ + public enum TextInputType { + TEXT("TextInputType.text"), + DATETIME("TextInputType.datetime"), + NUMBER("TextInputType.number"), + PHONE("TextInputType.phone"), + MULTILINE("TextInputType.multiline"), + EMAIL_ADDRESS("TextInputType.emailAddress"), + URL("TextInputType.url"); + + static TextInputType fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (TextInputType textInputType : TextInputType.values()) { + if (textInputType.encodedName.equals(encodedName)) { + return textInputType; + } + } + throw new NoSuchFieldException("No such TextInputType: " + encodedName); + } + + @NonNull + private final String encodedName; + + TextInputType(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * Text capitalization schemes. + */ + public enum TextCapitalization { + CHARACTERS("TextCapitalization.characters"), + WORDS("TextCapitalization.words"), + SENTENCES("TextCapitalization.sentences"), + NONE("TextCapitalization.none"); + + static TextCapitalization fromValue(@NonNull String encodedName) throws NoSuchFieldException { + for (TextCapitalization textCapitalization : TextCapitalization.values()) { + if (textCapitalization.encodedName.equals(encodedName)) { + return textCapitalization; + } + } + throw new NoSuchFieldException("No such TextCapitalization: " + encodedName); + } + + @NonNull + private final String encodedName; + + TextCapitalization(@NonNull String encodedName) { + this.encodedName = encodedName; + } + } + + /** + * State of an on-going text editing session. + */ + public static class TextEditState { + public static TextEditState fromJson(@NonNull JSONObject textEditState) throws JSONException { + return new TextEditState( + textEditState.getString("text"), + textEditState.getInt("selectionBase"), + textEditState.getInt("selectionExtent") + ); + } + + @NonNull + public final String text; + public final int selectionStart; + public final int selectionEnd; + + public TextEditState(@NonNull String text, int selectionStart, int selectionEnd) { + this.text = text; + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + } + } +} diff --git a/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java b/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java index 5c27bd9613f45..beb51ecb54aca 100644 --- a/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java +++ b/shell/platform/android/io/flutter/plugin/common/ActivityLifecycleListener.java @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java b/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java index 3e7a285793a54..74a193880eb31 100644 --- a/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java +++ b/shell/platform/android/io/flutter/plugin/common/BasicMessageChannel.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java b/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java index 6514dc21b1148..2cc407ebba847 100644 --- a/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java b/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java index b382aba0b44fb..9f1e4eb4b67a8 100644 --- a/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java +++ b/shell/platform/android/io/flutter/plugin/common/BinaryMessenger.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,6 +11,11 @@ * The Flutter Dart code should use * BinaryMessages * to participate. + *

+ * {@code BinaryMessenger} is expected to be utilized from a single thread throughout the duration + * of its existence. If created on the main thread, then all invocations should take place on the + * main thread. If created on a background thread, then all invocations should take place on that + * background thread. * * @see BasicMessageChannel , which supports message passing with Strings and semi-structured messages. * @see MethodChannel , which supports communication using asynchronous method invocation. diff --git a/shell/platform/android/io/flutter/plugin/common/ErrorLogResult.java b/shell/platform/android/io/flutter/plugin/common/ErrorLogResult.java new file mode 100644 index 0000000000000..bfa8c2fd0d458 --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/common/ErrorLogResult.java @@ -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. + +package io.flutter.plugin.common; + +import android.support.annotation.Nullable; +import android.util.Log; + +/** + * An implementation of {@link MethodChannel.Result} that writes error results + * to the Android log. + */ +public class ErrorLogResult implements MethodChannel.Result { + private String tag; + private int level; + + public ErrorLogResult(String tag) { + this(tag, Log.WARN); + } + + public ErrorLogResult(String tag, int level) { + this.tag = tag; + this.level = level; + } + + @Override + public void success(@Nullable Object result) {} + + @Override + public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) { + String details = (errorDetails != null) ? " details: " + errorDetails : ""; + Log.println(level, tag, errorMessage + details); + } + + @Override + public void notImplemented() { + Log.println(level, tag, "method not implemented"); + } +} diff --git a/shell/platform/android/io/flutter/plugin/common/EventChannel.java b/shell/platform/android/io/flutter/plugin/common/EventChannel.java index 8b9c1cbe7881b..57443a0aa1816 100644 --- a/shell/platform/android/io/flutter/plugin/common/EventChannel.java +++ b/shell/platform/android/io/flutter/plugin/common/EventChannel.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/FlutterException.java b/shell/platform/android/io/flutter/plugin/common/FlutterException.java index 72d1b9029d901..61eb12c13c11b 100644 --- a/shell/platform/android/io/flutter/plugin/common/FlutterException.java +++ b/shell/platform/android/io/flutter/plugin/common/FlutterException.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,7 +10,7 @@ public class FlutterException extends RuntimeException { public final String code; public final Object details; - + FlutterException(String code, String message, Object details) { super(message); assert code != null; diff --git a/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java b/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java index 85d14fcc290ee..6b2b88d0d0a23 100644 --- a/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/JSONMessageCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java b/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java index 78c09a9d4935b..c87771d66a01e 100644 --- a/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/JSONMethodCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/MessageCodec.java b/shell/platform/android/io/flutter/plugin/common/MessageCodec.java index 125186f213017..a1a0590df476c 100644 --- a/shell/platform/android/io/flutter/plugin/common/MessageCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/MessageCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/MethodCall.java b/shell/platform/android/io/flutter/plugin/common/MethodCall.java index 6864e42a4643c..9206ba5ecee25 100644 --- a/shell/platform/android/io/flutter/plugin/common/MethodCall.java +++ b/shell/platform/android/io/flutter/plugin/common/MethodCall.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/MethodChannel.java b/shell/platform/android/io/flutter/plugin/common/MethodChannel.java index b05c38027e836..9f725e7bdf545 100644 --- a/shell/platform/android/io/flutter/plugin/common/MethodChannel.java +++ b/shell/platform/android/io/flutter/plugin/common/MethodChannel.java @@ -1,9 +1,10 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.plugin.common; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler; @@ -66,7 +67,7 @@ public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) * @param method the name String of the method. * @param arguments the arguments for the invocation, possibly null. */ - public void invokeMethod(String method, @Nullable Object arguments) { + public void invokeMethod(@NonNull String method, @Nullable Object arguments) { invokeMethod(method, arguments, null); } diff --git a/shell/platform/android/io/flutter/plugin/common/MethodCodec.java b/shell/platform/android/io/flutter/plugin/common/MethodCodec.java index d4e7307230913..49e058ae7efca 100644 --- a/shell/platform/android/io/flutter/plugin/common/MethodCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/MethodCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java index 4fd8a4def4368..7853982e87928 100644 --- a/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java +++ b/shell/platform/android/io/flutter/plugin/common/PluginRegistry.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -117,21 +117,21 @@ interface Registrar { /** * Returns the file name for the given asset. * The returned file name can be used to access the asset in the APK - * through the {@link AssetManager} API. + * through the {@link android.content.res.AssetManager} API. * * @param asset the name of the asset. The name can be hierarchical - * @return the filename to be used with {@link AssetManager} + * @return the filename to be used with {@link android.content.res.AssetManager} */ String lookupKeyForAsset(String asset); /** * Returns the file name for the given asset which originates from the * specified packageName. The returned file name can be used to access - * the asset in the APK through the {@link AssetManager} API. + * the asset in the APK through the {@link android.content.res.AssetManager} API. * * @param asset the name of the asset. The name can be hierarchical * @param packageName the name of the package from which the asset originates - * @return the file name to be used with {@link AssetManager} + * @return the file name to be used with {@link android.content.res.AssetManager} */ String lookupKeyForAsset(String asset, String packageName); diff --git a/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java b/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java index db769b0482aef..c22ea07a9aa87 100644 --- a/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -30,6 +30,7 @@ *

  • null
  • *
  • Booleans
  • *
  • Bytes, Shorts, Integers, Longs
  • + *
  • BigIntegers (see below)
  • *
  • Floats, Doubles
  • *
  • Strings
  • *
  • byte[], int[], long[], double[]
  • @@ -53,11 +54,8 @@ *
  • Map: Map
  • * * - *

    Direct support for BigIntegers has been deprecated on 2018-01-09 to be made - * unavailable four weeks after this change is available on the Flutter alpha - * branch. BigIntegers were needed because the Dart 1.0 int type had no size - * limit. With Dart 2.0, the int type is a fixed-size, 64-bit signed integer. - * If you need to communicate larger integers, use String encoding instead.

    + *

    BigIntegers are represented in Dart as strings with the + * hexadecimal representation of the integer's value.

    * *

    To extend the codec, overwrite the writeValue and readValueOfType methods.

    */ @@ -96,7 +94,6 @@ public Object decodeMessage(ByteBuffer message) { private static final byte FALSE = 2; private static final byte INT = 3; private static final byte LONG = 4; - @Deprecated private static final byte BIGINT = 5; private static final byte DOUBLE = 6; private static final byte STRING = 7; @@ -237,7 +234,6 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { writeAlignment(stream, 8); writeDouble(stream, ((Number) value).doubleValue()); } else if (value instanceof BigInteger) { - Log.w("Flutter", "Support for BigIntegers has been deprecated. Use String encoding instead."); stream.write(BIGINT); writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8)); @@ -367,7 +363,6 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) { result = buffer.getLong(); break; case BIGINT: { - Log.w("Flutter", "Support for BigIntegers has been deprecated. Use String encoding instead."); final byte[] hex = readBytes(buffer); result = new BigInteger(new String(hex, UTF8), 16); break; diff --git a/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java b/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java index 1284c11390e09..132f075f798e6 100644 --- a/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/common/StringCodec.java b/shell/platform/android/io/flutter/plugin/common/StringCodec.java index 9851cf4ccf60c..18f6a102df31c 100644 --- a/shell/platform/android/io/flutter/plugin/common/StringCodec.java +++ b/shell/platform/android/io/flutter/plugin/common/StringCodec.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index ae3a1381e230a..c613e82ff3a01 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,29 +8,36 @@ import android.text.Editable; import android.text.Selection; import android.view.KeyEvent; +import android.view.View; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.view.FlutterView; -import java.util.Arrays; -import java.util.HashMap; +import io.flutter.embedding.engine.systemchannels.TextInputChannel; +import io.flutter.plugin.common.ErrorLogResult; +import io.flutter.plugin.common.MethodChannel; class InputConnectionAdaptor extends BaseInputConnection { - private final FlutterView mFlutterView; + private final View mFlutterView; private final int mClient; - private final MethodChannel mFlutterChannel; + private final TextInputChannel textInputChannel; private final Editable mEditable; private int mBatchCount; private InputMethodManager mImm; - public InputConnectionAdaptor(FlutterView view, int client, - MethodChannel flutterChannel, Editable editable) { + private static final MethodChannel.Result logger = + new ErrorLogResult("FlutterTextInput"); + + public InputConnectionAdaptor( + View view, + int client, + TextInputChannel textInputChannel, + Editable editable + ) { super(view, true); mFlutterView = view; mClient = client; - mFlutterChannel = flutterChannel; + this.textInputChannel = textInputChannel; mEditable = editable; mBatchCount = 0; mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -51,14 +58,14 @@ private void updateEditingState() { selectionStart, selectionEnd, composingStart, composingEnd); - HashMap state = new HashMap(); - state.put("text", mEditable.toString()); - state.put("selectionBase", selectionStart); - state.put("selectionExtent", selectionEnd); - state.put("composingBase", composingStart); - state.put("composingExtent", composingEnd); - mFlutterChannel.invokeMethod("TextInputClient.updateEditingState", - Arrays.asList(mClient, state)); + textInputChannel.updateEditingState( + mClient, + mEditable.toString(), + selectionStart, + selectionEnd, + composingStart, + composingEnd + ); } @Override @@ -174,39 +181,30 @@ public boolean sendKeyEvent(KeyEvent event) { @Override public boolean performEditorAction(int actionCode) { switch (actionCode) { - // TODO(mattcarroll): is newline an appropriate action for "none"? case EditorInfo.IME_ACTION_NONE: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.newline")); + textInputChannel.newline(mClient); break; case EditorInfo.IME_ACTION_UNSPECIFIED: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.unspecified")); + textInputChannel.unspecifiedAction(mClient); break; case EditorInfo.IME_ACTION_GO: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.go")); + textInputChannel.go(mClient); break; case EditorInfo.IME_ACTION_SEARCH: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.search")); + textInputChannel.search(mClient); break; case EditorInfo.IME_ACTION_SEND: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.send")); + textInputChannel.send(mClient); break; case EditorInfo.IME_ACTION_NEXT: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.next")); + textInputChannel.next(mClient); break; case EditorInfo.IME_ACTION_PREVIOUS: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.previous")); + textInputChannel.previous(mClient); break; default: case EditorInfo.IME_ACTION_DONE: - mFlutterChannel.invokeMethod("TextInputClient.performAction", - Arrays.asList(mClient, "TextInputAction.done")); + textInputChannel.done(mClient); break; } return true; diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 220776b35140a..051befd6fab4e 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -1,95 +1,113 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.plugin.editing; import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.Editable; import android.text.InputType; import android.text.Selection; +import android.view.View; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import io.flutter.plugin.common.JSONMethodCodec; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; + +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.view.FlutterView; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; /** * Android implementation of the text input plugin. */ -public class TextInputPlugin implements MethodCallHandler { - private final FlutterView mView; +public class TextInputPlugin { + @NonNull + private final View mView; + @NonNull private final InputMethodManager mImm; - private final MethodChannel mFlutterChannel; + @NonNull + private final TextInputChannel textInputChannel; private int mClient = 0; - private JSONObject mConfiguration; + @Nullable + private TextInputChannel.Configuration configuration; + @Nullable private Editable mEditable; private boolean mRestartInputPending; + @Nullable + private InputConnection lastInputConnection; - public TextInputPlugin(FlutterView view) { + public TextInputPlugin(View view, @NonNull DartExecutor dartExecutor) { mView = view; mImm = (InputMethodManager) view.getContext().getSystemService( Context.INPUT_METHOD_SERVICE); - mFlutterChannel = new MethodChannel(view, "flutter/textinput", JSONMethodCodec.INSTANCE); - mFlutterChannel.setMethodCallHandler(this); - } - @Override - public void onMethodCall(MethodCall call, Result result) { - String method = call.method; - Object args = call.arguments; - try { - if (method.equals("TextInput.show")) { + textInputChannel = new TextInputChannel(dartExecutor); + textInputChannel.setTextInputMethodHandler(new TextInputChannel.TextInputMethodHandler() { + @Override + public void show() { showTextInput(mView); - result.success(null); - } else if (method.equals("TextInput.hide")) { + } + + @Override + public void hide() { hideTextInput(mView); - result.success(null); - } else if (method.equals("TextInput.setClient")) { - final JSONArray argumentList = (JSONArray) args; - setTextInputClient(mView, argumentList.getInt(0), argumentList.getJSONObject(1)); - result.success(null); - } else if (method.equals("TextInput.setEditingState")) { - setTextInputEditingState(mView, (JSONObject) args); - result.success(null); - } else if (method.equals("TextInput.clearClient")) { + } + + @Override + public void setClient(int textInputClientId, TextInputChannel.Configuration configuration) { + setTextInputClient(textInputClientId, configuration); + } + + @Override + public void setEditingState(TextInputChannel.TextEditState editingState) { + setTextInputEditingState(mView, editingState); + } + + @Override + public void clearClient() { clearTextInputClient(); - result.success(null); - } else { - result.notImplemented(); } - } catch (JSONException e) { - result.error("error", "JSON error: " + e.getMessage(), null); - } + }); } - private static int inputTypeFromTextInputType(JSONObject type, boolean obscureText, - boolean autocorrect, String textCapitalization) throws JSONException { - String inputType = type.getString("name"); - if (inputType.equals("TextInputType.datetime")) return InputType.TYPE_CLASS_DATETIME; - if (inputType.equals("TextInputType.number")) { + @NonNull + public InputMethodManager getInputMethodManager() { + return mImm; + } + + private static int inputTypeFromTextInputType( + TextInputChannel.InputType type, + boolean obscureText, + boolean autocorrect, + TextInputChannel.TextCapitalization textCapitalization + ) { + if (type.type == TextInputChannel.TextInputType.DATETIME) { + return InputType.TYPE_CLASS_DATETIME; + } else if (type.type == TextInputChannel.TextInputType.NUMBER) { int textType = InputType.TYPE_CLASS_NUMBER; - if (type.optBoolean("signed")) textType |= InputType.TYPE_NUMBER_FLAG_SIGNED; - if (type.optBoolean("decimal")) textType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + if (type.isSigned) { + textType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if (type.isDecimal) { + textType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } return textType; + } else if (type.type == TextInputChannel.TextInputType.PHONE) { + return InputType.TYPE_CLASS_PHONE; } - if (inputType.equals("TextInputType.phone")) return InputType.TYPE_CLASS_PHONE; int textType = InputType.TYPE_CLASS_TEXT; - if (inputType.equals("TextInputType.multiline")) + if (type.type == TextInputChannel.TextInputType.MULTILINE) { textType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; - else if (inputType.equals("TextInputType.emailAddress")) + } else if (type.type == TextInputChannel.TextInputType.EMAIL_ADDRESS) { textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; - else if (inputType.equals("TextInputType.url")) + } else if (type.type == TextInputChannel.TextInputType.URL) { textType |= InputType.TYPE_TEXT_VARIATION_URI; + } + if (obscureText) { // Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS. textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; @@ -97,86 +115,77 @@ else if (inputType.equals("TextInputType.url")) } else { if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; } - if (textCapitalization.equals("TextCapitalization.characters")) { + + if (textCapitalization == TextInputChannel.TextCapitalization.CHARACTERS) { textType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; - } else if (textCapitalization.equals("TextCapitalization.words")) { + } else if (textCapitalization == TextInputChannel.TextCapitalization.WORDS) { textType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS; - } else if (textCapitalization.equals("TextCapitalization.sentences")) { + } else if (textCapitalization == TextInputChannel.TextCapitalization.SENTENCES) { textType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; } + return textType; } - private static int inputActionFromTextInputAction(String inputAction) { - switch (inputAction) { - case "TextInputAction.newline": - return EditorInfo.IME_ACTION_NONE; - case "TextInputAction.none": - return EditorInfo.IME_ACTION_NONE; - case "TextInputAction.unspecified": - return EditorInfo.IME_ACTION_UNSPECIFIED; - case "TextInputAction.done": - return EditorInfo.IME_ACTION_DONE; - case "TextInputAction.go": - return EditorInfo.IME_ACTION_GO; - case "TextInputAction.search": - return EditorInfo.IME_ACTION_SEARCH; - case "TextInputAction.send": - return EditorInfo.IME_ACTION_SEND; - case "TextInputAction.next": - return EditorInfo.IME_ACTION_NEXT; - case "TextInputAction.previous": - return EditorInfo.IME_ACTION_PREVIOUS; - default: - // Present default key if bad input type is given. - return EditorInfo.IME_ACTION_UNSPECIFIED; + public InputConnection createInputConnection(View view, EditorInfo outAttrs) { + if (mClient == 0) { + lastInputConnection = null; + return lastInputConnection; } - } - - public InputConnection createInputConnection(FlutterView view, EditorInfo outAttrs) - throws JSONException { - if (mClient == 0) return null; - outAttrs.inputType = inputTypeFromTextInputType(mConfiguration.getJSONObject("inputType"), - mConfiguration.optBoolean("obscureText"), - mConfiguration.optBoolean("autocorrect", true), - mConfiguration.getString("textCapitalization")); + outAttrs.inputType = inputTypeFromTextInputType( + configuration.inputType, + configuration.obscureText, + configuration.autocorrect, + configuration.textCapitalization + ); outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; int enterAction; - if (mConfiguration.isNull("inputAction")) { + if (configuration.inputAction == null) { // If an explicit input action isn't set, then default to none for multi-line fields // and done for single line fields. enterAction = (InputType.TYPE_TEXT_FLAG_MULTI_LINE & outAttrs.inputType) != 0 ? EditorInfo.IME_ACTION_NONE : EditorInfo.IME_ACTION_DONE; } else { - enterAction = inputActionFromTextInputAction(mConfiguration.getString("inputAction")); + enterAction = configuration.inputAction; } - if (!mConfiguration.isNull("actionLabel")) { - outAttrs.actionLabel = mConfiguration.getString("actionLabel"); + if (configuration.actionLabel != null) { + outAttrs.actionLabel = configuration.actionLabel; outAttrs.actionId = enterAction; } outAttrs.imeOptions |= enterAction; - InputConnectionAdaptor connection = - new InputConnectionAdaptor(view, mClient, mFlutterChannel, mEditable); + InputConnectionAdaptor connection = new InputConnectionAdaptor( + view, + mClient, + textInputChannel, + mEditable + ); outAttrs.initialSelStart = Selection.getSelectionStart(mEditable); outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable); - return connection; + lastInputConnection = connection; + return lastInputConnection; + } + + @Nullable + public InputConnection getLastInputConnection() { + return lastInputConnection; } - private void showTextInput(FlutterView view) { + private void showTextInput(View view) { + view.requestFocus(); mImm.showSoftInput(view, 0); } - private void hideTextInput(FlutterView view) { + private void hideTextInput(View view) { mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0); } - private void setTextInputClient(FlutterView view, int client, JSONObject configuration) { + private void setTextInputClient(int client, TextInputChannel.Configuration configuration) { mClient = client; - mConfiguration = configuration; + this.configuration = configuration; mEditable = Editable.Factory.getInstance().newEditable(""); // setTextInputClient will be followed by a call to setTextInputEditingState. @@ -184,9 +193,9 @@ private void setTextInputClient(FlutterView view, int client, JSONObject configu mRestartInputPending = true; } - private void applyStateToSelection(JSONObject state) throws JSONException { - int selStart = state.getInt("selectionBase"); - int selEnd = state.getInt("selectionExtent"); + private void applyStateToSelection(TextInputChannel.TextEditState state) { + int selStart = state.selectionStart; + int selEnd = state.selectionEnd; if (selStart >= 0 && selStart <= mEditable.length() && selEnd >= 0 && selEnd <= mEditable.length()) { Selection.setSelection(mEditable, selStart, selEnd); @@ -195,15 +204,15 @@ private void applyStateToSelection(JSONObject state) throws JSONException { } } - private void setTextInputEditingState(FlutterView view, JSONObject state) throws JSONException { - if (!mRestartInputPending && state.getString("text").equals(mEditable.toString())) { + private void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { + if (!mRestartInputPending && state.text.equals(mEditable.toString())) { applyStateToSelection(state); mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0), Math.max(Selection.getSelectionEnd(mEditable), 0), BaseInputConnection.getComposingSpanStart(mEditable), BaseInputConnection.getComposingSpanEnd(mEditable)); } else { - mEditable.replace(0, mEditable.length(), state.getString("text")); + mEditable.replace(0, mEditable.length(), state.text); applyStateToSelection(state); mImm.restartInput(view); mRestartInputPending = false; diff --git a/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java b/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java new file mode 100644 index 0000000000000..1a3022cfc9c42 --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java @@ -0,0 +1,49 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugin.platform; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import io.flutter.view.AccessibilityBridge; + +/** + * Delegates accessibility events to the currently attached accessibility bridge if one is attached. + */ +class AccessibilityEventsDelegate { + private AccessibilityBridge accessibilityBridge; + + /** + * Delegates handling of {@link android.view.ViewParent#requestSendAccessibilityEvent} to the accessibility bridge. + * + * This is a no-op if there is no accessibility delegate set. + * + * This is used by embedded platform views to propagate accessibility events from their view hierarchy to the + * accessibility bridge. + * + * As the embedded view doesn't have to be the only View in the embedded hierarchy (it can have child views) and the + * event might have been originated from any view in this hierarchy, this method gets both a reference to the + * embedded platform view, and a reference to the view from its hierarchy that sent the event. + * + * @param embeddedView the embedded platform view for which the event is delegated + * @param eventOrigin the view in the embedded view's hierarchy that sent the event. + * @return True if the event was sent. + */ + public boolean requestSendAccessibilityEvent(@NonNull View embeddedView, @NonNull View eventOrigin, @NonNull AccessibilityEvent event) { + if (accessibilityBridge == null) { + return false; + } + return accessibilityBridge.externalViewRequestSendAccessibilityEvent(embeddedView, eventOrigin, event); + } + + /* + * This setter should only be used directly in PlatformViewsController when attached/detached to an accessibility + * bridge. + */ + void setAccessibilityBridge(@Nullable AccessibilityBridge accessibilityBridge) { + this.accessibilityBridge = accessibilityBridge; + } +} diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 4396782279413..e1e5f9f9cdbaa 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -1,218 +1,170 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.plugin.platform; import android.app.Activity; +import android.app.ActivityManager.TaskDescription; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.pm.ActivityInfo; import android.os.Build; -import android.util.Log; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.view.HapticFeedbackConstants; import android.view.SoundEffectConstants; import android.view.View; import android.view.Window; + +import java.util.List; + +import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.plugin.common.ActivityLifecycleListener; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; /** * Android implementation of the platform plugin. */ -public class PlatformPlugin implements MethodCallHandler, ActivityLifecycleListener { - private final Activity mActivity; - private JSONObject mCurrentTheme; +public class PlatformPlugin implements ActivityLifecycleListener { public static final int DEFAULT_SYSTEM_UI = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - private static final String kTextPlainFormat = "text/plain"; - public PlatformPlugin(Activity activity) { - mActivity = activity; - mEnabledOverlays = DEFAULT_SYSTEM_UI; - } + private final Activity activity; + private final PlatformChannel platformChannel; + private PlatformChannel.SystemChromeStyle currentTheme; + private int mEnabledOverlays; - @Override - public void onMethodCall(MethodCall call, Result result) { - String method = call.method; - Object arguments = call.arguments; - try { - if (method.equals("SystemSound.play")) { - playSystemSound((String) arguments); - result.success(null); - } else if (method.equals("HapticFeedback.vibrate")) { - vibrateHapticFeedback((String) arguments); - result.success(null); - } else if (method.equals("SystemChrome.setPreferredOrientations")) { - setSystemChromePreferredOrientations((JSONArray) arguments); - result.success(null); - } else if (method.equals("SystemChrome.setApplicationSwitcherDescription")) { - setSystemChromeApplicationSwitcherDescription((JSONObject) arguments); - result.success(null); - } else if (method.equals("SystemChrome.setEnabledSystemUIOverlays")) { - setSystemChromeEnabledSystemUIOverlays((JSONArray) arguments); - result.success(null); - } else if (method.equals("SystemChrome.restoreSystemUIOverlays")) { - restoreSystemChromeSystemUIOverlays(); - result.success(null); - } else if (method.equals("SystemChrome.setSystemUIOverlayStyle")) { - setSystemChromeSystemUIOverlayStyle((JSONObject) arguments); - result.success(null); - } else if (method.equals("SystemNavigator.pop")) { - popSystemNavigator(); - result.success(null); - } else if (method.equals("Clipboard.getData")) { - result.success(getClipboardData((String) arguments)); - } else if (method.equals("Clipboard.setData")) { - setClipboardData((JSONObject) arguments); - result.success(null); - } else { - result.notImplemented(); - } - } catch (JSONException e) { - result.error("error", "JSON error: " + e.getMessage(), null); + private final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler = new PlatformChannel.PlatformMessageHandler() { + @Override + public void playSystemSound(@NonNull PlatformChannel.SoundType soundType) { + PlatformPlugin.this.playSystemSound(soundType); } - } - private void playSystemSound(String soundType) { - if (soundType.equals("SystemSoundType.click")) { - View view = mActivity.getWindow().getDecorView(); - view.playSoundEffect(SoundEffectConstants.CLICK); + @Override + public void vibrateHapticFeedback(@NonNull PlatformChannel.HapticFeedbackType feedbackType) { + PlatformPlugin.this.vibrateHapticFeedback(feedbackType); + } + + @Override + public void setPreferredOrientations(int androidOrientation) { + setSystemChromePreferredOrientations(androidOrientation); } - } - private void vibrateHapticFeedback(String feedbackType) { - View view = mActivity.getWindow().getDecorView(); - if (feedbackType == null) { - view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } else if (feedbackType.equals("HapticFeedbackType.lightImpact")) { - view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); - } else if (feedbackType.equals("HapticFeedbackType.mediumImpact")) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); - } else if (feedbackType.equals("HapticFeedbackType.heavyImpact")) { - // HapticFeedbackConstants.CONTEXT_CLICK from API level 23. - view.performHapticFeedback(6); - } else if (feedbackType.equals("HapticFeedbackType.selectionClick")) { - view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + @Override + public void setApplicationSwitcherDescription(@NonNull PlatformChannel.AppSwitcherDescription description) { + setSystemChromeApplicationSwitcherDescription(description); } + + @Override + public void showSystemOverlays(@NonNull List overlays) { + setSystemChromeEnabledSystemUIOverlays(overlays); + } + + @Override + public void restoreSystemUiOverlays() { + restoreSystemChromeSystemUIOverlays(); + } + + @Override + public void setSystemUiOverlayStyle(@NonNull PlatformChannel.SystemChromeStyle systemUiOverlayStyle) { + setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle); + } + + @Override + public void popSystemNavigator() { + PlatformPlugin.this.popSystemNavigator(); + } + + @Override + public CharSequence getClipboardData(@Nullable PlatformChannel.ClipboardContentFormat format) { + return PlatformPlugin.this.getClipboardData(format); + } + + @Override + public void setClipboardData(@NonNull String text) { + PlatformPlugin.this.setClipboardData(text); + } + }; + + public PlatformPlugin(Activity activity, PlatformChannel platformChannel) { + this.activity = activity; + this.platformChannel = platformChannel; + this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler); + + mEnabledOverlays = DEFAULT_SYSTEM_UI; } - private void setSystemChromePreferredOrientations(JSONArray orientations) throws JSONException { - int requestedOrientation = 0x00; - int firstRequestedOrientation = 0x00; - for (int index = 0; index < orientations.length(); index += 1) { - if (orientations.getString(index).equals("DeviceOrientation.portraitUp")) { - requestedOrientation |= 0x01; - } else if (orientations.getString(index).equals("DeviceOrientation.landscapeLeft")) { - requestedOrientation |= 0x02; - } else if (orientations.getString(index).equals("DeviceOrientation.portraitDown")) { - requestedOrientation |= 0x04; - } else if (orientations.getString(index).equals("DeviceOrientation.landscapeRight")) { - requestedOrientation |= 0x08; - } - if (firstRequestedOrientation == 0x00) { - firstRequestedOrientation = requestedOrientation; - } + private void playSystemSound(PlatformChannel.SoundType soundType) { + if (soundType == PlatformChannel.SoundType.CLICK) { + View view = activity.getWindow().getDecorView(); + view.playSoundEffect(SoundEffectConstants.CLICK); } - switch (requestedOrientation) { - case 0x00: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - break; - case 0x01: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - break; - case 0x02: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - break; - case 0x04: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); - break; - case 0x05: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT); + } + + private void vibrateHapticFeedback(PlatformChannel.HapticFeedbackType feedbackType) { + View view = activity.getWindow().getDecorView(); + switch (feedbackType) { + case STANDARD: + view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); break; - case 0x08: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + case LIGHT_IMPACT: + view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); break; - case 0x0a: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE); + case MEDIUM_IMPACT: + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); break; - case 0x0b: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER); + case HEAVY_IMPACT: + // HapticFeedbackConstants.CONTEXT_CLICK from API level 23. + view.performHapticFeedback(6); break; - case 0x0f: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); + case SELECTION_CLICK: + view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); break; - case 0x03: // portraitUp and landscapeLeft - case 0x06: // portraitDown and landscapeLeft - case 0x07: // portraitUp, portraitDown, and landscapeLeft - case 0x09: // portraitUp and landscapeRight - case 0x0c: // portraitDown and landscapeRight - case 0x0d: // portraitUp, portraitDown, and landscapeRight - case 0x0e: // portraitDown, landscapeLeft, and landscapeRight - // Android can't describe these cases, so just default to whatever the first - // specified value was. - switch (firstRequestedOrientation) { - case 0x01: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - break; - case 0x02: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - break; - case 0x04: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); - break; - case 0x08: - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); - break; - } - break; - } + } } - private void setSystemChromeApplicationSwitcherDescription(JSONObject description) throws JSONException { + private void setSystemChromePreferredOrientations(int androidOrientation) { + activity.setRequestedOrientation(androidOrientation); + } + + private void setSystemChromeApplicationSwitcherDescription(PlatformChannel.AppSwitcherDescription description) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return; } - int color = description.getInt("primaryColor"); - if (color != 0) { // 0 means color isn't set, use system default - color = color | 0xFF000000; // color must be opaque if set + // Linter refuses to believe we're only executing this code in API 28 unless we use distinct if blocks and + // hardcode the API 28 constant. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { + activity.setTaskDescription(new TaskDescription(description.label)); + } + if (Build.VERSION.SDK_INT >= 28) { + TaskDescription taskDescription = new TaskDescription(description.label, 0, description.color); + activity.setTaskDescription(taskDescription); } - - mActivity.setTaskDescription( - new android.app.ActivityManager.TaskDescription( - description.getString("label"), - null, - color - ) - ); } - private int mEnabledOverlays; - - private void setSystemChromeEnabledSystemUIOverlays(JSONArray overlays) throws JSONException { + private void setSystemChromeEnabledSystemUIOverlays(List overlaysToShow) { + // Start by assuming we want to hide all system overlays (like an immersive game). int enabledOverlays = DEFAULT_SYSTEM_UI | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - if (overlays.length() == 0) { + if (overlaysToShow.size() == 0) { enabledOverlays |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } - for (int i = 0; i < overlays.length(); ++i) { - String overlay = overlays.getString(i); - if (overlay.equals("SystemUiOverlay.top")) { - enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; - } else if (overlay.equals("SystemUiOverlay.bottom")) { - enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + // Re-add any desired system overlays. + for (int i = 0; i < overlaysToShow.size(); ++i) { + PlatformChannel.SystemUiOverlay overlayToShow = overlaysToShow.get(i); + switch (overlayToShow) { + case TOP_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + break; + case BOTTOM_OVERLAYS: + enabledOverlays &= ~View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + enabledOverlays &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + break; } } @@ -221,9 +173,9 @@ private void setSystemChromeEnabledSystemUIOverlays(JSONArray overlays) throws J } private void updateSystemUiOverlays(){ - mActivity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays); - if (mCurrentTheme != null) { - setSystemChromeSystemUIOverlayStyle(mCurrentTheme); + activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays); + if (currentTheme != null) { + setSystemChromeSystemUIOverlayStyle(currentTheme); } } @@ -231,83 +183,75 @@ private void restoreSystemChromeSystemUIOverlays() { updateSystemUiOverlays(); } - private void setSystemChromeSystemUIOverlayStyle(JSONObject message) { - Window window = mActivity.getWindow(); + private void setSystemChromeSystemUIOverlayStyle(PlatformChannel.SystemChromeStyle systemChromeStyle) { + Window window = activity.getWindow(); View view = window.getDecorView(); int flags = view.getSystemUiVisibility(); - try { - // You can change the navigation bar color (including translucent colors) - // in Android, but you can't change the color of the navigation buttons until Android O. - // LIGHT vs DARK effectively isn't supported until then. - // Build.VERSION_CODES.O - if (Build.VERSION.SDK_INT >= 26) { - if (!message.isNull("systemNavigationBarIconBrightness")) { - String systemNavigationBarIconBrightness = message.getString("systemNavigationBarIconBrightness"); - switch (systemNavigationBarIconBrightness) { - case "Brightness.dark": - //View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - flags |= 0x10; - break; - case "Brightness.light": - flags &= ~0x10; - break; - } - } - if (!message.isNull("systemNavigationBarColor")) { - window.setNavigationBarColor(message.getInt("systemNavigationBarColor")); + // You can change the navigation bar color (including translucent colors) + // in Android, but you can't change the color of the navigation buttons until Android O. + // LIGHT vs DARK effectively isn't supported until then. + // Build.VERSION_CODES.O + if (Build.VERSION.SDK_INT >= 26) { + if (systemChromeStyle.systemNavigationBarIconBrightness != null) { + switch (systemChromeStyle.systemNavigationBarIconBrightness) { + case DARK: + //View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + flags |= 0x10; + break; + case LIGHT: + flags &= ~0x10; + break; } } - // Build.VERSION_CODES.M - if (Build.VERSION.SDK_INT >= 23) { - if (!message.isNull("statusBarIconBrightness")) { - String statusBarIconBrightness = message.getString("statusBarIconBrightness"); - switch (statusBarIconBrightness) { - case "Brightness.dark": - // View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - flags |= 0x2000; - break; - case "Brightness.light": - flags &= ~0x2000; - break; - } - } - if (!message.isNull("statusBarColor")) { - window.setStatusBarColor(message.getInt("statusBarColor")); + if (systemChromeStyle.systemNavigationBarColor != null) { + window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor); + } + } + // Build.VERSION_CODES.M + if (Build.VERSION.SDK_INT >= 23) { + if (systemChromeStyle.statusBarIconBrightness != null) { + switch (systemChromeStyle.statusBarIconBrightness) { + case DARK: + // View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + flags |= 0x2000; + break; + case LIGHT: + flags &= ~0x2000; + break; } } - if (!message.isNull("systemNavigationBarDividerColor")) { - // Not availible until Android P. - // window.setNavigationBarDividerColor(systemNavigationBarDividerColor); + if (systemChromeStyle.statusBarColor != null) { + window.setStatusBarColor(systemChromeStyle.statusBarColor); } - view.setSystemUiVisibility(flags); - mCurrentTheme = message; - } catch (JSONException err) { - Log.i("PlatformPlugin", err.toString()); } + if (systemChromeStyle.systemNavigationBarDividerColor != null) { + // Not availible until Android P. + // window.setNavigationBarDividerColor(systemNavigationBarDividerColor); + } + view.setSystemUiVisibility(flags); + currentTheme = systemChromeStyle; } private void popSystemNavigator() { - mActivity.finish(); + activity.finish(); } - private JSONObject getClipboardData(String format) throws JSONException { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); + private CharSequence getClipboardData(PlatformChannel.ClipboardContentFormat format) { + ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = clipboard.getPrimaryClip(); if (clip == null) return null; - if (format == null || format.equals(kTextPlainFormat)) { - JSONObject result = new JSONObject(); - result.put("text", clip.getItemAt(0).coerceToText(mActivity)); - return result; + if (format == null || format == PlatformChannel.ClipboardContentFormat.PLAIN_TEXT) { + return clip.getItemAt(0).coerceToText(activity); } return null; } - private void setClipboardData(JSONObject data) throws JSONException { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("text label?", data.getString("text")); + private void setClipboardData(String text) { + ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("text label?", text); clipboard.setPrimaryClip(clip); } diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformView.java b/shell/platform/android/io/flutter/plugin/platform/PlatformView.java index 6e379e96a6ba0..36240f0cc3805 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformView.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformView.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java index f6fcd35b9c55f..e47f6e854ee82 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewFactory.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java index 0a45ab7a65b59..9b48d76799512 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java index ea1b02c4785ab..5c303034c764f 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java new file mode 100644 index 0000000000000..3b81a505552ce --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugin.platform; + +import android.view.View; +import io.flutter.view.AccessibilityBridge; + +/** + * Facilitates interaction between the accessibility bridge and embedded platform views. + */ +public interface PlatformViewsAccessibilityDelegate { + + /** + * Returns the root of the view hierarchy for the platform view with the requested id, or null if there is no + * corresponding view. + */ + View getPlatformViewById(Integer id); + + /** + * Attaches an accessibility bridge for this platform views accessibility delegate. + * + * Accessibility events originating in platform views belonging to this delegate will be delegated + * to this accessibility bridge. + */ + void attachAccessibilityBridge(AccessibilityBridge accessibilityBridge); + + /** + * Detaches the current accessibility bridge. + * + * Any accessibility events sent by platform views belonging to this delegate will be ignored until + * a new accessibility bridge is attached. + */ + void detachAccessibiltyBridge(); +} diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 41b95dc5c7a2c..5470e03a9f2da 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,7 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.StandardMethodCodec; +import io.flutter.view.AccessibilityBridge; import io.flutter.view.TextureRegistry; import java.nio.ByteBuffer; @@ -31,7 +32,7 @@ * Each {@link io.flutter.app.FlutterPluginRegistry} has a single platform views controller. * A platform views controller can be attached to at most one Flutter view. */ -public class PlatformViewsController implements MethodChannel.MethodCallHandler { +public class PlatformViewsController implements MethodChannel.MethodCallHandler, PlatformViewsAccessibilityDelegate { private static final String TAG = "PlatformViewsController"; private static final String CHANNEL_NAME = "flutter/platform_views"; @@ -50,11 +51,15 @@ public class PlatformViewsController implements MethodChannel.MethodCallHandler // The messenger used to communicate with the framework over the platform views channel. private BinaryMessenger mMessenger; + // The accessibility bridge to which accessibility events form the platform views will be dispatched. + private final AccessibilityEventsDelegate mAccessibilityEventsDelegate; + private final HashMap vdControllers; public PlatformViewsController() { mRegistry = new PlatformViewRegistryImpl(); vdControllers = new HashMap<>(); + mAccessibilityEventsDelegate = new AccessibilityEventsDelegate(); } /** @@ -94,6 +99,16 @@ public void detach() { mTextureRegistry = null; } + @Override + public void attachAccessibilityBridge(AccessibilityBridge accessibilityBridge) { + mAccessibilityEventsDelegate.setAccessibilityBridge(accessibilityBridge); + } + + @Override + public void detachAccessibiltyBridge() { + mAccessibilityEventsDelegate.setAccessibilityBridge(null); + } + public PlatformViewRegistry getRegistry() { return mRegistry; } @@ -106,6 +121,15 @@ public void onPreEngineRestart() { flushAllViews(); } + @Override + public View getPlatformViewById(Integer id) { + VirtualDisplayController controller = vdControllers.get(id); + if (controller == null) { + return null; + } + return controller.getView(); + } + @Override public void onMethodCall(final MethodCall call, final MethodChannel.Result result) { if (Build.VERSION.SDK_INT < MINIMAL_SDK) { @@ -178,8 +202,9 @@ private void createPlatformView(MethodCall call, MethodChannel.Result result) { TextureRegistry.SurfaceTextureEntry textureEntry = mTextureRegistry.createSurfaceTexture(); VirtualDisplayController vdController = VirtualDisplayController.create( mContext, + mAccessibilityEventsDelegate, viewFactory, - textureEntry.surfaceTexture(), + textureEntry, toPhysicalPixels(logicalWidth), toPhysicalPixels(logicalHeight), id, diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index 2ea3934ae88ee..a0cbde42b5494 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,6 +13,7 @@ import android.os.Bundle; import android.util.Log; import android.view.*; +import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import java.lang.reflect.*; @@ -57,6 +58,9 @@ static class PresentationState { private final PlatformViewFactory mViewFactory; + // A reference to the current accessibility bridge to which accessibility events will be delegated. + private final AccessibilityEventsDelegate mAccessibilityEventsDelegate; + // This is the view id assigned by the Flutter framework to the embedded view, we keep it here // so when we create the platform view we can tell it its view id. private int mViewId; @@ -67,7 +71,7 @@ static class PresentationState { // The root view for the presentation, it has 2 childs: mContainer which contains the embedded view, and // mFakeWindowRootView which contains views that were added directly to the presentation's window manager. - private FrameLayout mRootView; + private AccessibilityDelegatingFrameLayout mRootView; // Contains the embedded platform view (mView.getView()) when it is attached to the presentation. private FrameLayout mContainer; @@ -82,10 +86,13 @@ public SingleViewPresentation( Context outerContext, Display display, PlatformViewFactory viewFactory, + AccessibilityEventsDelegate accessibilityEventsDelegate, int viewId, - Object createParams) { + Object createParams + ) { super(outerContext, display); mViewFactory = viewFactory; + mAccessibilityEventsDelegate = accessibilityEventsDelegate; mViewId = viewId; mCreateParams = createParams; mState = new PresentationState(); @@ -102,8 +109,14 @@ public SingleViewPresentation( *

    The display's density must match the density of the context used * when the view was created. */ - public SingleViewPresentation(Context outerContext, Display display, PresentationState state) { + public SingleViewPresentation( + Context outerContext, + Display display, + AccessibilityEventsDelegate accessibilityEventsDelegate, + PresentationState state + ) { super(outerContext, display); + mAccessibilityEventsDelegate = accessibilityEventsDelegate; mViewFactory = null; mState = state; getWindow().setFlags( @@ -130,8 +143,9 @@ protected void onCreate(Bundle savedInstanceState) { mState.mView = mViewFactory.create(context, mViewId, mCreateParams); } - mContainer.addView(mState.mView.getView()); - mRootView = new FrameLayout(getContext()); + View embeddedView = mState.mView.getView(); + mContainer.addView(embeddedView); + mRootView = new AccessibilityDelegatingFrameLayout(getContext(), mAccessibilityEventsDelegate, embeddedView); mRootView.addView(mContainer); mRootView.addView(mState.mFakeWindowRootView); setContentView(mRootView); @@ -320,4 +334,24 @@ private void updateViewLayout(Object[] args) { mFakeWindowRootView.updateViewLayout(view, layoutParams); } } + + private static class AccessibilityDelegatingFrameLayout extends FrameLayout { + private final AccessibilityEventsDelegate mAccessibilityEventsDelegate; + private final View mEmbeddedView; + + public AccessibilityDelegatingFrameLayout( + Context context, + AccessibilityEventsDelegate accessibilityEventsDelegate, + View ebeddedView + ) { + super(context); + mAccessibilityEventsDelegate = accessibilityEventsDelegate; + mEmbeddedView = ebeddedView; + } + + @Override + public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) { + return mAccessibilityEventsDelegate.requestSendAccessibilityEvent(mEmbeddedView, child, event); + } + } } diff --git a/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java b/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java index 4495b7388b016..876e6be5cedc7 100644 --- a/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java +++ b/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,28 +6,29 @@ import android.annotation.TargetApi; import android.content.Context; -import android.graphics.SurfaceTexture; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.Build; import android.view.Surface; import android.view.View; import android.view.ViewTreeObserver; +import io.flutter.view.TextureRegistry; @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) class VirtualDisplayController { public static VirtualDisplayController create( Context context, + AccessibilityEventsDelegate accessibilityEventsDelegate, PlatformViewFactory viewFactory, - SurfaceTexture surfaceTexture, + TextureRegistry.SurfaceTextureEntry textureEntry, int width, int height, int viewId, Object createParams ) { - surfaceTexture.setDefaultBufferSize(width, height); - Surface surface = new Surface(surfaceTexture); + textureEntry.surfaceTexture().setDefaultBufferSize(width, height); + Surface surface = new Surface(textureEntry.surfaceTexture()); DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); int densityDpi = context.getResources().getDisplayMetrics().densityDpi; @@ -45,12 +46,13 @@ public static VirtualDisplayController create( } return new VirtualDisplayController( - context, virtualDisplay, viewFactory, surface, surfaceTexture, viewId, createParams); + context, accessibilityEventsDelegate, virtualDisplay, viewFactory, surface, textureEntry, viewId, createParams); } private final Context mContext; + private final AccessibilityEventsDelegate mAccessibilityEventsDelegate; private final int mDensityDpi; - private final SurfaceTexture mSurfaceTexture; + private final TextureRegistry.SurfaceTextureEntry mTextureEntry; private VirtualDisplay mVirtualDisplay; private SingleViewPresentation mPresentation; private Surface mSurface; @@ -58,20 +60,22 @@ public static VirtualDisplayController create( private VirtualDisplayController( Context context, + AccessibilityEventsDelegate accessibilityEventsDelegate, VirtualDisplay virtualDisplay, PlatformViewFactory viewFactory, Surface surface, - SurfaceTexture surfaceTexture, + TextureRegistry.SurfaceTextureEntry textureEntry, int viewId, Object createParams ) { - mSurfaceTexture = surfaceTexture; - mSurface = surface; mContext = context; + mAccessibilityEventsDelegate = accessibilityEventsDelegate; + mTextureEntry = textureEntry; + mSurface = surface; mVirtualDisplay = virtualDisplay; mDensityDpi = context.getResources().getDisplayMetrics().densityDpi; mPresentation = new SingleViewPresentation( - context, mVirtualDisplay.getDisplay(), viewFactory, viewId, createParams); + context, mVirtualDisplay.getDisplay(), viewFactory, accessibilityEventsDelegate, viewId, createParams); mPresentation.show(); } @@ -85,7 +89,7 @@ public void resize(final int width, final int height, final Runnable onNewSizeFr mVirtualDisplay.setSurface(null); mVirtualDisplay.release(); - mSurfaceTexture.setDefaultBufferSize(width, height); + mTextureEntry.surfaceTexture().setDefaultBufferSize(width, height); DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); mVirtualDisplay = displayManager.createVirtualDisplay( "flutter-vd", @@ -121,7 +125,7 @@ public void run() { public void onViewDetachedFromWindow(View v) {} }); - mPresentation = new SingleViewPresentation(mContext, mVirtualDisplay.getDisplay(), presentationState); + mPresentation = new SingleViewPresentation(mContext, mVirtualDisplay.getDisplay(), mAccessibilityEventsDelegate, presentationState); mPresentation.show(); } @@ -130,6 +134,7 @@ public void dispose() { mPresentation.detachState(); view.dispose(); mVirtualDisplay.release(); + mTextureEntry.release(); } public View getView() { diff --git a/shell/platform/android/io/flutter/util/PathUtils.java b/shell/platform/android/io/flutter/util/PathUtils.java index df187cbc0c1ec..94cf131916af2 100644 --- a/shell/platform/android/io/flutter/util/PathUtils.java +++ b/shell/platform/android/io/flutter/util/PathUtils.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/util/Preconditions.java b/shell/platform/android/io/flutter/util/Preconditions.java index cf39129d32910..968d2cb18e5f3 100644 --- a/shell/platform/android/io/flutter/util/Preconditions.java +++ b/shell/platform/android/io/flutter/util/Preconditions.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/util/Predicate.java b/shell/platform/android/io/flutter/util/Predicate.java new file mode 100644 index 0000000000000..e24df12e2a386 --- /dev/null +++ b/shell/platform/android/io/flutter/util/Predicate.java @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.util; + +// TODO(dnfield): remove this if/when we can use appcompat to support it. +// java.util.function.Predicate isn't available until API24 +public interface Predicate { + public abstract boolean test(T t); +} diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index cfba87f96a8bf..a589f54d4ca5d 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -1,28 +1,67 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.view; -import android.app.Activity; +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.database.ContentObserver; import android.graphics.Rect; +import android.net.Uri; import android.opengl.Matrix; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.provider.Settings; +import android.support.annotation.Nullable; +import android.support.annotation.NonNull; +import android.support.annotation.RequiresApi; import android.util.Log; +import android.view.MotionEvent; import android.view.View; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; -import io.flutter.plugin.common.BasicMessageChannel; -import io.flutter.plugin.common.StandardMessageCodec; + +import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; +import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate; +import io.flutter.util.Predicate; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.*; -class AccessibilityBridge - extends AccessibilityNodeProvider implements BasicMessageChannel.MessageHandler { - private static final String TAG = "FlutterView"; +/** + * Bridge between Android's OS accessibility system and Flutter's accessibility system. + * + * An {@code AccessibilityBridge} requires: + *
      + *
    • A real Android {@link View}, called the {@link #rootAccessibilityView}, which contains a + * Flutter UI. The {@link #rootAccessibilityView} is required at the time of + * {@code AccessibilityBridge}'s instantiation and is held for the duration of + * {@code AccessibilityBridge}'s lifespan. {@code AccessibilityBridge} invokes various + * accessibility methods on the {@link #rootAccessibilityView}, e.g., + * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)}. The + * {@link #rootAccessibilityView} is expected to notify the {@code AccessibilityBridge} of + * relevant interactions: {@link #onAccessibilityHoverEvent(MotionEvent)}, {@link #reset()}, + * {@link #updateSemantics(ByteBuffer, String[])}, and {@link #updateCustomAccessibilityActions(ByteBuffer, String[])}
    • + *
    • An {@link AccessibilityChannel} that is connected to the running Flutter app.
    • + *
    • Android's {@link AccessibilityManager} to query and listen for accessibility settings.
    • + *
    • Android's {@link ContentResolver} to listen for changes to system animation settings.
    • + *
    + * + * The {@code AccessibilityBridge} causes Android to treat Flutter {@code SemanticsNode}s as if + * they were accessible Android {@link View}s. Accessibility requests may be sent from + * a Flutter widget to the Android OS, as if it were an Android {@link View}, and + * accessibility events may be consumed by a Flutter widget, as if it were an Android + * {@link View}. {@code AccessibilityBridge} refers to Flutter's accessible widgets as + * "virtual views" and identifies them with "virtual view IDs". + */ +public class AccessibilityBridge extends AccessibilityNodeProvider { + private static final String TAG = "AccessibilityBridge"; // Constants from higher API levels. // TODO(goderbauer): Get these from Android Support Library when @@ -33,205 +72,538 @@ class AccessibilityBridge private static final float SCROLL_POSITION_CAP_FOR_INFINITY = 70000.0f; private static final int ROOT_NODE_ID = 0; - private Map mObjects; - private Map mCustomAccessibilityActions; - private final FlutterView mOwner; - private boolean mAccessibilityEnabled = false; - private SemanticsObject mA11yFocusedObject; - private SemanticsObject mInputFocusedObject; - private SemanticsObject mHoveredObject; + // The minimal ID for an engine generated AccessibilityNodeInfo. + // + // The AccessibilityNodeInfo node IDs are generated by the framework for most Flutter semantic nodes. + // When embedding platform views, the framework does not have the accessibility information for the embedded view; + // in this case the engine generates AccessibilityNodeInfo that mirrors the a11y information exposed by the platform + // view. To avoid the need of synchronizing the framework and engine mechanisms for generating the next ID, we split + // the 32bit range of virtual node IDs into 2. The least significant 16 bits are used for framework generated IDs + // and the most significant 16 bits are used for engine generated IDs. + private static final int MIN_ENGINE_GENERATED_NODE_ID = 1<<16; + + /// Value is derived from ACTION_TYPE_MASK in AccessibilityNodeInfo.java + private static int FIRST_RESOURCE_ID = 267386881; + + // Real Android View, which internally holds a Flutter UI. + @NonNull + private final View rootAccessibilityView; + + // The accessibility communication API between Flutter's Android embedding and + // the Flutter framework. + @NonNull + private final AccessibilityChannel accessibilityChannel; + + // Android's {@link AccessibilityManager}, which we can query to see if accessibility is + // turned on, as well as listen for changes to accessibility's activation. + @NonNull + private final AccessibilityManager accessibilityManager; + + @NonNull + private final AccessibilityViewEmbedder accessibilityViewEmbedder; + + // The delegate for interacting with embedded platform views. Used to embed accessibility data for an embedded + // view in the accessibility tree. + @NonNull + private final PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate; + + // Android's {@link ContentResolver}, which is used to observe the global TRANSITION_ANIMATION_SCALE, + // which determines whether Flutter's animations should be enabled or disabled for accessibility + // purposes. + @NonNull + private final ContentResolver contentResolver; + + // The entire Flutter semantics tree of the running Flutter app, stored as a Map + // from each SemanticsNode's ID to a Java representation of a Flutter SemanticsNode. + // + // Flutter's semantics tree is cached here because Android might ask for information about + // a given SemanticsNode at any moment in time. Caching the tree allows for immediate + // response to Android's request. + // + // The structure of flutterSemanticsTree may be 1 or 2 frames behind the Flutter app + // due to the time required to communicate tree changes from Flutter to Android. + // + // See the Flutter docs on SemanticsNode: + // https://docs.flutter.io/flutter/semantics/SemanticsNode-class.html + @NonNull + private final Map flutterSemanticsTree = new HashMap<>(); + + // The set of all custom Flutter accessibility actions that are present in the running + // Flutter app, stored as a Map from each action's ID to the definition of the custom accessibility + // action. + // + // Flutter and Android support a number of built-in accessibility actions. However, these + // predefined actions are not always sufficient for a desired interaction. Android facilitates + // custom accessibility actions, https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction. + // Flutter supports custom accessibility actions via {@code customSemanticsActions} within + // a {@code Semantics} widget, https://docs.flutter.io/flutter/widgets/Semantics-class.html. + // {@code customAccessibilityActions} are an Android-side cache of all custom accessibility + // types declared within the running Flutter app. + // + // Custom accessibility actions are comprised of only a few fields, and therefore it is likely + // that a given app may define the same custom accessibility action many times. Identical + // custom accessibility actions are de-duped such that {@code customAccessibilityActions} only + // caches unique custom accessibility actions. + // + // See the Android documentation for custom accessibility actions: + // https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction + // + // See the Flutter documentation for the Semantics widget: + // https://docs.flutter.io/flutter/widgets/Semantics-class.html + @NonNull + private final Map customAccessibilityActions = new HashMap<>(); + + // The {@code SemanticsNode} within Flutter that currently has the focus of Android's + // accessibility system. + // + // This is null when a node embedded by the AccessibilityViewEmbedder has the focus. + @Nullable + private SemanticsNode accessibilityFocusedSemanticsNode; + + // The virtual ID of the currently embedded node with accessibility focus. + // + // This is the ID of a node generated by the AccessibilityViewEmbedder if an embedded node is focused, + // null otherwise. + private Integer embeddedAccessibilityFocusedNodeId; + + // The virtual ID of the currently embedded node with input focus. + // + // This is the ID of a node generated by the AccessibilityViewEmbedder if an embedded node is focused, + // null otherwise. + private Integer embeddedInputFocusedNodeId; + + // The accessibility features that should currently be active within Flutter, represented as + // a bitmask whose values comes from {@link AccessibilityFeature}. + private int accessibilityFeatureFlags = 0; + + // The {@code SemanticsNode} within Flutter that currently has the focus of Android's input + // system. + // + // Input focus is independent of accessibility focus. It is possible that accessibility focus + // and input focus target the same {@code SemanticsNode}, but it is also possible that one + // {@code SemanticsNode} has input focus while a different {@code SemanticsNode} has + // accessibility focus. For example, a user may use a D-Pad to navigate to a text field, giving + // it accessibility focus, and then enable input on that text field, giving it input focus. Then + // the user moves the accessibility focus to a nearby label to get info about the label, while + // maintaining input focus on the original text field. + @Nullable + private SemanticsNode inputFocusedSemanticsNode; + + // The widget within Flutter that currently sits beneath a cursor, e.g, + // beneath a stylus or mouse cursor. + @Nullable + private SemanticsNode hoveredObject; + + // A Java/Android cached representation of the Flutter app's navigation stack. The Flutter + // navigation stack is tracked so that accessibility announcements can be made during Flutter's + // navigation changes. + // TODO(mattcarroll): take this cache into account for new routing solution so accessibility does + // not get left behind. + @NonNull + private final List flutterNavigationStack = new ArrayList<>(); + + // TODO(mattcarroll): why do we need previouseRouteId if we have flutterNavigationStack private int previousRouteId = ROOT_NODE_ID; - private List previousRoutes; - private final View mDecorView; - private Integer mLastLeftFrameInset = 0; - private final BasicMessageChannel mFlutterAccessibilityChannel; + // Tracks the left system inset of the screen because Flutter needs to manually adjust + // accessibility positioning when in reverse-landscape. This is an Android bug that Flutter + // is solving for itself. + @NonNull + private Integer lastLeftFrameInset = 0; + + @Nullable + private OnAccessibilityChangeListener onAccessibilityChangeListener; + + // Handler for all messages received from Flutter via the {@code accessibilityChannel} + private final AccessibilityChannel.AccessibilityMessageHandler accessibilityMessageHandler = new AccessibilityChannel.AccessibilityMessageHandler() { + /** + * The Dart application would like the given {@code message} to be announced. + */ + @Override + public void announce(@NonNull String message) { + rootAccessibilityView.announceForAccessibility(message); + } + + /** + * The user has tapped on the widget with the given {@code nodeId}. + */ + @Override + public void onTap(int nodeId) { + sendAccessibilityEvent(nodeId, AccessibilityEvent.TYPE_VIEW_CLICKED); + } + + /** + * The user has long pressed on the widget with the given {@code nodeId}. + */ + @Override + public void onLongPress(int nodeId) { + sendAccessibilityEvent(nodeId, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); + } + + /** + * The user has opened a tooltip. + */ + @Override + public void onTooltip(@NonNull String message) { + AccessibilityEvent e = obtainAccessibilityEvent(ROOT_NODE_ID, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + e.getText().add(message); + sendAccessibilityEvent(e); + } + + /** + * New custom accessibility actions exist in Flutter. Update our Android-side cache. + */ + @Override + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + AccessibilityBridge.this.updateCustomAccessibilityActions(buffer, strings); + } + + /** + * Flutter's semantics tree has changed. Update our Android-side cache. + */ + @Override + public void updateSemantics(ByteBuffer buffer, String[] strings) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + AccessibilityBridge.this.updateSemantics(buffer, strings); + } + }; + + // Listener that is notified when accessibility is turned on/off. + private final AccessibilityManager.AccessibilityStateChangeListener accessibilityStateChangeListener = new AccessibilityManager.AccessibilityStateChangeListener() { + @Override + public void onAccessibilityStateChanged(boolean accessibilityEnabled) { + if (accessibilityEnabled) { + accessibilityChannel.setAccessibilityMessageHandler(accessibilityMessageHandler); + accessibilityChannel.onAndroidAccessibilityEnabled(); + } else { + accessibilityChannel.setAccessibilityMessageHandler(null); + accessibilityChannel.onAndroidAccessibilityDisabled(); + } - enum Action { - TAP(1 << 0), - LONG_PRESS(1 << 1), - SCROLL_LEFT(1 << 2), - SCROLL_RIGHT(1 << 3), - SCROLL_UP(1 << 4), - SCROLL_DOWN(1 << 5), - INCREASE(1 << 6), - DECREASE(1 << 7), - SHOW_ON_SCREEN(1 << 8), - MOVE_CURSOR_FORWARD_BY_CHARACTER(1 << 9), - MOVE_CURSOR_BACKWARD_BY_CHARACTER(1 << 10), - SET_SELECTION(1 << 11), - COPY(1 << 12), - CUT(1 << 13), - PASTE(1 << 14), - DID_GAIN_ACCESSIBILITY_FOCUS(1 << 15), - DID_LOSE_ACCESSIBILITY_FOCUS(1 << 16), - CUSTOM_ACTION(1 << 17), - DISMISS(1 << 18), - MOVE_CURSOR_FORWARD_BY_WORD(1 << 19), - MOVE_CURSOR_BACKWARD_BY_WORD(1 << 20); + if (onAccessibilityChangeListener != null) { + onAccessibilityChangeListener.onAccessibilityChanged( + accessibilityEnabled, + accessibilityManager.isTouchExplorationEnabled() + ); + } + } + }; - Action(int value) { - this.value = value; + // Listener that is notified when accessibility touch exploration is turned on/off. + // This is guarded at instantiation time. + @TargetApi(19) + @RequiresApi(19) + private final AccessibilityManager.TouchExplorationStateChangeListener touchExplorationStateChangeListener; + + // Listener that is notified when the global TRANSITION_ANIMATION_SCALE. When this scale goes + // to zero, we instruct Flutter to disable animations. + private final ContentObserver animationScaleObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + this.onChange(selfChange, null); } - final int value; - } + @Override + public void onChange(boolean selfChange, Uri uri) { + // Retrieve the current value of TRANSITION_ANIMATION_SCALE from the OS. + String value = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 ? null + : Settings.Global.getString( + contentResolver, + Settings.Global.TRANSITION_ANIMATION_SCALE + ); - enum Flag { - HAS_CHECKED_STATE(1 << 0), - IS_CHECKED(1 << 1), - IS_SELECTED(1 << 2), - IS_BUTTON(1 << 3), - IS_TEXT_FIELD(1 << 4), - IS_FOCUSED(1 << 5), - HAS_ENABLED_STATE(1 << 6), - IS_ENABLED(1 << 7), - IS_IN_MUTUALLY_EXCLUSIVE_GROUP(1 << 8), - IS_HEADER(1 << 9), - IS_OBSCURED(1 << 10), - SCOPES_ROUTE(1 << 11), - NAMES_ROUTE(1 << 12), - IS_HIDDEN(1 << 13), - IS_IMAGE(1 << 14), - IS_LIVE_REGION(1 << 15), - HAS_TOGGLED_STATE(1 << 16), - IS_TOGGLED(1 << 17), - HAS_IMPLICIT_SCROLLING(1 << 18); + boolean shouldAnimationsBeDisabled = value != null && value.equals("0"); + if (shouldAnimationsBeDisabled) { + accessibilityFeatureFlags |= AccessibilityFeature.DISABLE_ANIMATIONS.value; + } else { + accessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; + } + sendLatestAccessibilityFlagsToFlutter(); + } + }; + + public AccessibilityBridge( + @NonNull View rootAccessibilityView, + @NonNull AccessibilityChannel accessibilityChannel, + @NonNull AccessibilityManager accessibilityManager, + @NonNull ContentResolver contentResolver, + // This should be @NonNull once the plumbing for io.flutter.embedding.engine.android.FlutterView is done. + // TODO(mattcarrol): Add the annotation once the plumbing is done. + // https://github.com/flutter/flutter/issues/29618 + PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate + ) { + this.rootAccessibilityView = rootAccessibilityView; + this.accessibilityChannel = accessibilityChannel; + this.accessibilityManager = accessibilityManager; + this.contentResolver = contentResolver; + this.platformViewsAccessibilityDelegate = platformViewsAccessibilityDelegate; + + // Tell Flutter whether accessibility is initially active or not. Then register a listener + // to be notified of changes in the future. + accessibilityStateChangeListener.onAccessibilityStateChanged(accessibilityManager.isEnabled()); + this.accessibilityManager.addAccessibilityStateChangeListener(accessibilityStateChangeListener); + + // Tell Flutter whether touch exploration is initially active or not. Then register a listener + // to be notified of changes in the future. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + touchExplorationStateChangeListener = new AccessibilityManager.TouchExplorationStateChangeListener() { + @Override + public void onTouchExplorationStateChanged(boolean isTouchExplorationEnabled) { + if (isTouchExplorationEnabled) { + accessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; + } else { + onTouchExplorationExit(); + accessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; + } + sendLatestAccessibilityFlagsToFlutter(); - Flag(int value) { - this.value = value; + if (onAccessibilityChangeListener != null) { + onAccessibilityChangeListener.onAccessibilityChanged( + accessibilityManager.isEnabled(), + isTouchExplorationEnabled + ); + } + } + }; + touchExplorationStateChangeListener.onTouchExplorationStateChanged(accessibilityManager.isTouchExplorationEnabled()); + this.accessibilityManager.addTouchExplorationStateChangeListener(touchExplorationStateChangeListener); + } else { + touchExplorationStateChangeListener = null; } - final int value; + // Tell Flutter whether animations should initially be enabled or disabled. Then register a + // listener to be notified of changes in the future. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + animationScaleObserver.onChange(false); + Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); + this.contentResolver.registerContentObserver(transitionUri, false, animationScaleObserver); + } + + // platformViewsAccessibilityDelegate should be @NonNull once the plumbing + // for io.flutter.embedding.engine.android.FlutterView is done. + // TODO(mattcarrol): Remove the null check once the plumbing is done. + // https://github.com/flutter/flutter/issues/29618 + if (platformViewsAccessibilityDelegate != null) { + platformViewsAccessibilityDelegate.attachAccessibilityBridge(this); + } + accessibilityViewEmbedder = new AccessibilityViewEmbedder(rootAccessibilityView, MIN_ENGINE_GENERATED_NODE_ID); } - AccessibilityBridge(FlutterView owner) { - assert owner != null; - mOwner = owner; - mObjects = new HashMap(); - mCustomAccessibilityActions = new HashMap(); - previousRoutes = new ArrayList<>(); - mFlutterAccessibilityChannel = new BasicMessageChannel<>( - owner, "flutter/accessibility", StandardMessageCodec.INSTANCE); - mDecorView = ((Activity) owner.getContext()).getWindow().getDecorView(); + /** + * Disconnects any listeners and/or delegates that were initialized in {@code AccessibilityBridge}'s + * constructor, or added after. + * + * Do not use this instance after invoking {@code release}. The behavior of any method invoked + * on this {@code AccessibilityBridge} after invoking {@code release()} is undefined. + */ + public void release() { + // platformViewsAccessibilityDelegate should be @NonNull once the plumbing + // for io.flutter.embedding.engine.android.FlutterView is done. + // TODO(mattcarrol): Remove the null check once the plumbing is done. + // https://github.com/flutter/flutter/issues/29618 + if (platformViewsAccessibilityDelegate != null) { + platformViewsAccessibilityDelegate.detachAccessibiltyBridge(); + } + setOnAccessibilityChangeListener(null); + accessibilityManager.removeAccessibilityStateChangeListener(accessibilityStateChangeListener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + accessibilityManager.removeTouchExplorationStateChangeListener(touchExplorationStateChangeListener); + } + contentResolver.unregisterContentObserver(animationScaleObserver); } - void setAccessibilityEnabled(boolean accessibilityEnabled) { - mAccessibilityEnabled = accessibilityEnabled; - if (accessibilityEnabled) { - mFlutterAccessibilityChannel.setMessageHandler(this); - } else { - mFlutterAccessibilityChannel.setMessageHandler(null); - } + /** + * Returns true if the Android OS currently has accessibility enabled, false otherwise. + */ + public boolean isAccessibilityEnabled() { + return accessibilityManager.isEnabled(); + } + + /** + * Returns true if the Android OS currently has touch exploration enabled, false otherwise. + */ + public boolean isTouchExplorationEnabled() { + return accessibilityManager.isTouchExplorationEnabled(); + } + + /** + * Sets a listener on this {@code AccessibilityBridge}, which is notified whenever accessibility + * activation, or touch exploration activation changes. + */ + public void setOnAccessibilityChangeListener(@Nullable OnAccessibilityChangeListener listener) { + this.onAccessibilityChangeListener = listener; + } + + /** + * Sends the current value of {@link #accessibilityFeatureFlags} to Flutter. + */ + private void sendLatestAccessibilityFlagsToFlutter() { + accessibilityChannel.setAccessibilityFeatures(accessibilityFeatureFlags); + } + + private boolean shouldSetCollectionInfo(final SemanticsNode semanticsNode) { + // TalkBack expects a number of rows and/or columns greater than 0 to announce + // in list and out of list. For an infinite or growing list, you have to + // specify something > 0 to get "in list" announcements. + // TalkBack will also only track one list at a time, so we only want to set this + // for a list that contains the current a11y focused semanticsNode - otherwise, if there + // are two lists or nested lists, we may end up with announcements for only the last + // one that is currently available in the semantics tree. However, we also want + // to set it if we're exiting a list to a non-list, so that we can get the "out of list" + // announcement when A11y focus moves out of a list and not into another list. + return semanticsNode.scrollChildren > 0 + && (SemanticsNode.nullableHasAncestor(accessibilityFocusedSemanticsNode, o -> o == semanticsNode) + || !SemanticsNode.nullableHasAncestor(accessibilityFocusedSemanticsNode, o -> o.hasFlag(Flag.HAS_IMPLICIT_SCROLLING))); } + /** + * Returns {@link AccessibilityNodeInfo} for the view corresponding to the given {@code virtualViewId}. + * + * This method is invoked by Android's accessibility system when Android needs accessibility info + * for a given view. + * + * When a {@code virtualViewId} of {@link View#NO_ID} is requested, accessibility node info is + * returned for our {@link #rootAccessibilityView}. Otherwise, Flutter's semantics tree, + * represented by {@link #flutterSemanticsTree}, is searched for a {@link SemanticsNode} with + * the given {@code virtualViewId}. If no such {@link SemanticsNode} is found, then this method + * returns null. If the desired {@link SemanticsNode} is found, then an {@link AccessibilityNodeInfo} + * is obtained from the {@link #rootAccessibilityView}, filled with appropriate info, and then + * returned. + * + * Depending on the type of Flutter {@code SemanticsNode} that is requested, the returned + * {@link AccessibilityNodeInfo} pretends that the {@code SemanticsNode} in question comes from + * a specialize Android view, e.g., {@link Flag#IS_TEXT_FIELD} maps to {@code android.widget.EditText}, + * {@link Flag#IS_BUTTON} maps to {@code android.widget.Button}, and {@link Flag#IS_IMAGE} maps + * to {@code android.widget.ImageView}. In the case that no specialized view applies, the + * returned {@link AccessibilityNodeInfo} pretends that it represents a {@code android.view.View}. + */ @Override @SuppressWarnings("deprecation") public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { + if (virtualViewId >= MIN_ENGINE_GENERATED_NODE_ID) { + // The node is in the engine generated range, and is provided by the accessibility view embedder. + return accessibilityViewEmbedder.createAccessibilityNodeInfo(virtualViewId); + } + if (virtualViewId == View.NO_ID) { - AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mOwner); - mOwner.onInitializeAccessibilityNodeInfo(result); - if (mObjects.containsKey(ROOT_NODE_ID)) { - result.addChild(mOwner, ROOT_NODE_ID); + AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(rootAccessibilityView); + rootAccessibilityView.onInitializeAccessibilityNodeInfo(result); + // TODO(mattcarroll): what does it mean for the semantics tree to contain or not contain + // the root node ID? + if (flutterSemanticsTree.containsKey(ROOT_NODE_ID)) { + result.addChild(rootAccessibilityView, ROOT_NODE_ID); } return result; } - SemanticsObject object = mObjects.get(virtualViewId); - if (object == null) { + SemanticsNode semanticsNode = flutterSemanticsTree.get(virtualViewId); + if (semanticsNode == null) { return null; } - AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mOwner, virtualViewId); + if (semanticsNode.platformViewId != -1) { + // For platform views we delegate the node creation to the accessibility view embedder. + View embeddedView = platformViewsAccessibilityDelegate.getPlatformViewById(semanticsNode.platformViewId); + Rect bounds = semanticsNode.getGlobalRect(); + return accessibilityViewEmbedder.getRootNode(embeddedView, semanticsNode.id, bounds); + } + + AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(rootAccessibilityView, virtualViewId); // Work around for https://github.com/flutter/flutter/issues/2101 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { result.setViewIdResourceName(""); } - result.setPackageName(mOwner.getContext().getPackageName()); + result.setPackageName(rootAccessibilityView.getContext().getPackageName()); result.setClassName("android.view.View"); - result.setSource(mOwner, virtualViewId); - result.setFocusable(object.isFocusable()); - if (mInputFocusedObject != null) { - result.setFocused(mInputFocusedObject.id == virtualViewId); + result.setSource(rootAccessibilityView, virtualViewId); + result.setFocusable(semanticsNode.isFocusable()); + if (inputFocusedSemanticsNode != null) { + result.setFocused(inputFocusedSemanticsNode.id == virtualViewId); } - if (mA11yFocusedObject != null) { - result.setAccessibilityFocused(mA11yFocusedObject.id == virtualViewId); + if (accessibilityFocusedSemanticsNode != null) { + result.setAccessibilityFocused(accessibilityFocusedSemanticsNode.id == virtualViewId); } - if (object.hasFlag(Flag.IS_TEXT_FIELD)) { - result.setPassword(object.hasFlag(Flag.IS_OBSCURED)); + if (semanticsNode.hasFlag(Flag.IS_TEXT_FIELD)) { + result.setPassword(semanticsNode.hasFlag(Flag.IS_OBSCURED)); result.setClassName("android.widget.EditText"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { result.setEditable(true); - if (object.textSelectionBase != -1 && object.textSelectionExtent != -1) { - result.setTextSelection(object.textSelectionBase, object.textSelectionExtent); + if (semanticsNode.textSelectionBase != -1 && semanticsNode.textSelectionExtent != -1) { + result.setTextSelection(semanticsNode.textSelectionBase, semanticsNode.textSelectionExtent); } - // Text fields will always be created as a live region, so that updates to - // the label trigger polite announcements. This makes it easy to follow a11y - // guidelines for text fields on Android. - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { + // Text fields will always be created as a live region when they have input focus, + // so that updates to the label trigger polite announcements. This makes it easy to + // follow a11y guidelines for text fields on Android. + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == virtualViewId) { result.setLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); } } // Cursor movements int granularities = 0; - if (object.hasAction(Action.MOVE_CURSOR_FORWARD_BY_CHARACTER)) { + if (semanticsNode.hasAction(Action.MOVE_CURSOR_FORWARD_BY_CHARACTER)) { result.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); granularities |= AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER; } - if (object.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER)) { + if (semanticsNode.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER)) { result.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); granularities |= AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER; } - if (object.hasAction(Action.MOVE_CURSOR_FORWARD_BY_WORD)) { + if (semanticsNode.hasAction(Action.MOVE_CURSOR_FORWARD_BY_WORD)) { result.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); granularities |= AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD; } - if (object.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_WORD)) { + if (semanticsNode.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_WORD)) { result.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); granularities |= AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD; } result.setMovementGranularities(granularities); } - if (object.hasAction(Action.SET_SELECTION)) { - result.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); - } - if (object.hasAction(Action.COPY)) { - result.addAction(AccessibilityNodeInfo.ACTION_COPY); - } - if (object.hasAction(Action.CUT)) { - result.addAction(AccessibilityNodeInfo.ACTION_CUT); - } - if (object.hasAction(Action.PASTE)) { - result.addAction(AccessibilityNodeInfo.ACTION_PASTE); + + // These are non-ops on older devices. Attempting to interact with the text will cause Talkback to read the + // contents of the text box instead. + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (semanticsNode.hasAction(Action.SET_SELECTION)) { + result.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION); + } + if (semanticsNode.hasAction(Action.COPY)) { + result.addAction(AccessibilityNodeInfo.ACTION_COPY); + } + if (semanticsNode.hasAction(Action.CUT)) { + result.addAction(AccessibilityNodeInfo.ACTION_CUT); + } + if (semanticsNode.hasAction(Action.PASTE)) { + result.addAction(AccessibilityNodeInfo.ACTION_PASTE); + } } - if (object.hasFlag(Flag.IS_BUTTON)) { + if (semanticsNode.hasFlag(Flag.IS_BUTTON)) { result.setClassName("android.widget.Button"); } - if (object.hasFlag(Flag.IS_IMAGE)) { + if (semanticsNode.hasFlag(Flag.IS_IMAGE)) { result.setClassName("android.widget.ImageView"); // TODO(jonahwilliams): Figure out a way conform to the expected id from TalkBack's // CustomLabelManager. talkback/src/main/java/labeling/CustomLabelManager.java#L525 } - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && object.hasAction(Action.DISMISS)) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && semanticsNode.hasAction(Action.DISMISS)) { result.setDismissable(true); result.addAction(AccessibilityNodeInfo.ACTION_DISMISS); } - if (object.parent != null) { - assert object.id > ROOT_NODE_ID; - result.setParent(mOwner, object.parent.id); + if (semanticsNode.parent != null) { + assert semanticsNode.id > ROOT_NODE_ID; + result.setParent(rootAccessibilityView, semanticsNode.parent.id); } else { - assert object.id == ROOT_NODE_ID; - result.setParent(mOwner); + assert semanticsNode.id == ROOT_NODE_ID; + result.setParent(rootAccessibilityView); } - Rect bounds = object.getGlobalRect(); - if (object.parent != null) { - Rect parentBounds = object.parent.getGlobalRect(); + Rect bounds = semanticsNode.getGlobalRect(); + if (semanticsNode.parent != null) { + Rect parentBounds = semanticsNode.parent.getGlobalRect(); Rect boundsInParent = new Rect(bounds); boundsInParent.offset(-parentBounds.left, -parentBounds.top); result.setBoundsInParent(boundsInParent); @@ -241,91 +613,123 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { result.setBoundsInScreen(bounds); result.setVisibleToUser(true); result.setEnabled( - !object.hasFlag(Flag.HAS_ENABLED_STATE) || object.hasFlag(Flag.IS_ENABLED)); + !semanticsNode.hasFlag(Flag.HAS_ENABLED_STATE) || semanticsNode.hasFlag(Flag.IS_ENABLED) + ); - if (object.hasAction(Action.TAP)) { - if (Build.VERSION.SDK_INT >= 21 && object.onTapOverride != null) { + if (semanticsNode.hasAction(Action.TAP)) { + if (Build.VERSION.SDK_INT >= 21 && semanticsNode.onTapOverride != null) { result.addAction(new AccessibilityNodeInfo.AccessibilityAction( - AccessibilityNodeInfo.ACTION_CLICK, object.onTapOverride.hint)); + AccessibilityNodeInfo.ACTION_CLICK, + semanticsNode.onTapOverride.hint + )); result.setClickable(true); } else { result.addAction(AccessibilityNodeInfo.ACTION_CLICK); result.setClickable(true); } } - if (object.hasAction(Action.LONG_PRESS)) { - if (Build.VERSION.SDK_INT >= 21 && object.onLongPressOverride != null) { - result.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, - object.onLongPressOverride.hint)); + if (semanticsNode.hasAction(Action.LONG_PRESS)) { + if (Build.VERSION.SDK_INT >= 21 && semanticsNode.onLongPressOverride != null) { + result.addAction(new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.ACTION_LONG_CLICK, + semanticsNode.onLongPressOverride.hint + )); result.setLongClickable(true); } else { result.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK); result.setLongClickable(true); } } - if (object.hasAction(Action.SCROLL_LEFT) || object.hasAction(Action.SCROLL_UP) - || object.hasAction(Action.SCROLL_RIGHT) || object.hasAction(Action.SCROLL_DOWN)) { + if (semanticsNode.hasAction(Action.SCROLL_LEFT) || semanticsNode.hasAction(Action.SCROLL_UP) + || semanticsNode.hasAction(Action.SCROLL_RIGHT) || semanticsNode.hasAction(Action.SCROLL_DOWN)) { result.setScrollable(true); + // This tells Android's a11y to send scroll events when reaching the end of // the visible viewport of a scrollable, unless the node itself does not // allow implicit scrolling - then we leave the className as view.View. - if (object.hasFlag(Flag.HAS_IMPLICIT_SCROLLING)) { - if (object.hasAction(Action.SCROLL_LEFT) || object.hasAction(Action.SCROLL_RIGHT)) { - result.setClassName("android.widget.HorizontalScrollView"); + // + // We should prefer setCollectionInfo to the class names, as this way we get "In List" + // and "Out of list" announcements. But we don't always know the counts, so we + // can fallback to the generic scroll view class names. + // + // On older APIs, we always fall back to the generic scroll view class names here. + // + // TODO(dnfield): We should add semantics properties for rows and columns in 2 dimensional lists, e.g. + // GridView. Right now, we're only supporting ListViews and only if they have scroll children. + if (semanticsNode.hasFlag(Flag.HAS_IMPLICIT_SCROLLING)) { + if (semanticsNode.hasAction(Action.SCROLL_LEFT) || semanticsNode.hasAction(Action.SCROLL_RIGHT)) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT && shouldSetCollectionInfo(semanticsNode)) { + result.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain( + 0, // rows + semanticsNode.scrollChildren, // columns + false // hierarchical + )); + } else { + result.setClassName("android.widget.HorizontalScrollView"); + } } else { - result.setClassName("android.widget.ScrollView"); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 && shouldSetCollectionInfo(semanticsNode)) { + result.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain( + semanticsNode.scrollChildren, // rows + 0, // columns + false // hierarchical + )); + } else { + result.setClassName("android.widget.ScrollView"); + } } } // TODO(ianh): Once we're on SDK v23+, call addAction to // expose AccessibilityAction.ACTION_SCROLL_LEFT, _RIGHT, // _UP, and _DOWN when appropriate. - if (object.hasAction(Action.SCROLL_LEFT) || object.hasAction(Action.SCROLL_UP)) { + if (semanticsNode.hasAction(Action.SCROLL_LEFT) || semanticsNode.hasAction(Action.SCROLL_UP)) { result.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); } - if (object.hasAction(Action.SCROLL_RIGHT) || object.hasAction(Action.SCROLL_DOWN)) { + if (semanticsNode.hasAction(Action.SCROLL_RIGHT) || semanticsNode.hasAction(Action.SCROLL_DOWN)) { result.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); } } - if (object.hasAction(Action.INCREASE) || object.hasAction(Action.DECREASE)) { + if (semanticsNode.hasAction(Action.INCREASE) || semanticsNode.hasAction(Action.DECREASE)) { // TODO(jonahwilliams): support AccessibilityAction.ACTION_SET_PROGRESS once SDK is // updated. result.setClassName("android.widget.SeekBar"); - if (object.hasAction(Action.INCREASE)) { + if (semanticsNode.hasAction(Action.INCREASE)) { result.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); } - if (object.hasAction(Action.DECREASE)) { + if (semanticsNode.hasAction(Action.DECREASE)) { result.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); } } - if (object.hasFlag(Flag.IS_LIVE_REGION) && Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (semanticsNode.hasFlag(Flag.IS_LIVE_REGION) && Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { result.setLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); } - boolean hasCheckedState = object.hasFlag(Flag.HAS_CHECKED_STATE); - boolean hasToggledState = object.hasFlag(Flag.HAS_TOGGLED_STATE); + boolean hasCheckedState = semanticsNode.hasFlag(Flag.HAS_CHECKED_STATE); + boolean hasToggledState = semanticsNode.hasFlag(Flag.HAS_TOGGLED_STATE); assert !(hasCheckedState && hasToggledState); result.setCheckable(hasCheckedState || hasToggledState); if (hasCheckedState) { - result.setChecked(object.hasFlag(Flag.IS_CHECKED)); - result.setContentDescription(object.getValueLabelHint()); - if (object.hasFlag(Flag.IS_IN_MUTUALLY_EXCLUSIVE_GROUP)) + result.setChecked(semanticsNode.hasFlag(Flag.IS_CHECKED)); + result.setContentDescription(semanticsNode.getValueLabelHint()); + if (semanticsNode.hasFlag(Flag.IS_IN_MUTUALLY_EXCLUSIVE_GROUP)) { result.setClassName("android.widget.RadioButton"); - else + } else { result.setClassName("android.widget.CheckBox"); + } } else if (hasToggledState) { - result.setChecked(object.hasFlag(Flag.IS_TOGGLED)); + result.setChecked(semanticsNode.hasFlag(Flag.IS_TOGGLED)); result.setClassName("android.widget.Switch"); - result.setContentDescription(object.getValueLabelHint()); + result.setContentDescription(semanticsNode.getValueLabelHint()); } else { // Setting the text directly instead of the content description // will replace the "checked" or "not-checked" label. - result.setText(object.getValueLabelHint()); + result.setText(semanticsNode.getValueLabelHint()); } - result.setSelected(object.hasFlag(Flag.IS_SELECTED)); + result.setSelected(semanticsNode.hasFlag(Flag.IS_SELECTED)); // Accessibility Focus - if (mA11yFocusedObject != null && mA11yFocusedObject.id == virtualViewId) { + if (accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == virtualViewId) { result.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } else { result.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); @@ -333,18 +737,20 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { // Actions on the local context menu if (Build.VERSION.SDK_INT >= 21) { - if (object.customAccessibilityActions != null) { - for (CustomAccessibilityAction action : object.customAccessibilityActions) { + if (semanticsNode.customAccessibilityActions != null) { + for (CustomAccessibilityAction action : semanticsNode.customAccessibilityActions) { result.addAction(new AccessibilityNodeInfo.AccessibilityAction( - action.resourceId, action.label)); + action.resourceId, + action.label + )); } } } - if (object.childrenInTraversalOrder != null) { - for (SemanticsObject child : object.childrenInTraversalOrder) { + if (semanticsNode.childrenInTraversalOrder != null) { + for (SemanticsNode child : semanticsNode.childrenInTraversalOrder) { if (!child.hasFlag(Flag.IS_HIDDEN)) { - result.addChild(mOwner, child.id); + result.addChild(rootAccessibilityView, child.id); } } } @@ -352,86 +758,130 @@ public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { return result; } + /** + * Instructs the view represented by {@code virtualViewId} to carry out the desired {@code accessibilityAction}, + * perhaps configured by additional {@code arguments}. + * + * This method is invoked by Android's accessibility system. This method returns true if the + * desired {@code SemanticsNode} was found and was capable of performing the desired action, + * false otherwise. + * + * In a traditional Android app, the given view ID refers to a {@link View} within an Android + * {@link View} hierarchy. Flutter does not have an Android {@link View} hierarchy, therefore + * the given view ID is a {@code virtualViewId} that refers to a {@code SemanticsNode} within + * a Flutter app. The given arguments of this method are forwarded from Android to Flutter. + */ @Override - public boolean performAction(int virtualViewId, int action, Bundle arguments) { - SemanticsObject object = mObjects.get(virtualViewId); - if (object == null) { + public boolean performAction(int virtualViewId, int accessibilityAction, @Nullable Bundle arguments) { + if (virtualViewId >= MIN_ENGINE_GENERATED_NODE_ID) { + // The node is in the engine generated range, and is handled by the accessibility view embedder. + boolean didPerform = accessibilityViewEmbedder.performAction(virtualViewId, accessibilityAction, arguments); + if (didPerform && accessibilityAction == AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) { + embeddedAccessibilityFocusedNodeId = null; + } + return didPerform; + } + SemanticsNode semanticsNode = flutterSemanticsTree.get(virtualViewId); + if (semanticsNode == null) { return false; } - switch (action) { + switch (accessibilityAction) { case AccessibilityNodeInfo.ACTION_CLICK: { // Note: TalkBack prior to Oreo doesn't use this handler and instead simulates a // click event at the center of the SemanticsNode. Other a11y services might go // through this handler though. - mOwner.dispatchSemanticsAction(virtualViewId, Action.TAP); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.TAP); return true; } case AccessibilityNodeInfo.ACTION_LONG_CLICK: { // Note: TalkBack doesn't use this handler and instead simulates a long click event // at the center of the SemanticsNode. Other a11y services might go through this // handler though. - mOwner.dispatchSemanticsAction(virtualViewId, Action.LONG_PRESS); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.LONG_PRESS); return true; } case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { - if (object.hasAction(Action.SCROLL_UP)) { - mOwner.dispatchSemanticsAction(virtualViewId, Action.SCROLL_UP); - } else if (object.hasAction(Action.SCROLL_LEFT)) { + if (semanticsNode.hasAction(Action.SCROLL_UP)) { + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_UP); + } else if (semanticsNode.hasAction(Action.SCROLL_LEFT)) { // TODO(ianh): bidi support using textDirection - mOwner.dispatchSemanticsAction(virtualViewId, Action.SCROLL_LEFT); - } else if (object.hasAction(Action.INCREASE)) { - object.value = object.increasedValue; + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_LEFT); + } else if (semanticsNode.hasAction(Action.INCREASE)) { + semanticsNode.value = semanticsNode.increasedValue; // Event causes Android to read out the updated value. sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED); - mOwner.dispatchSemanticsAction(virtualViewId, Action.INCREASE); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.INCREASE); } else { return false; } return true; } case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { - if (object.hasAction(Action.SCROLL_DOWN)) { - mOwner.dispatchSemanticsAction(virtualViewId, Action.SCROLL_DOWN); - } else if (object.hasAction(Action.SCROLL_RIGHT)) { + if (semanticsNode.hasAction(Action.SCROLL_DOWN)) { + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_DOWN); + } else if (semanticsNode.hasAction(Action.SCROLL_RIGHT)) { // TODO(ianh): bidi support using textDirection - mOwner.dispatchSemanticsAction(virtualViewId, Action.SCROLL_RIGHT); - } else if (object.hasAction(Action.DECREASE)) { - object.value = object.decreasedValue; + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_RIGHT); + } else if (semanticsNode.hasAction(Action.DECREASE)) { + semanticsNode.value = semanticsNode.decreasedValue; // Event causes Android to read out the updated value. sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED); - mOwner.dispatchSemanticsAction(virtualViewId, Action.DECREASE); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.DECREASE); } else { return false; } return true; } case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: { - return performCursorMoveAction(object, virtualViewId, arguments, false); + // Text selection APIs aren't available until API 18. We can't handle the case here so return false + // instead. It's extremely unlikely that this case would ever be triggered in the first place in API < + // 18. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + return false; + } + return performCursorMoveAction(semanticsNode, virtualViewId, arguments, false); } case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: { - return performCursorMoveAction(object, virtualViewId, arguments, true); + // Text selection APIs aren't available until API 18. We can't handle the case here so return false + // instead. It's extremely unlikely that this case would ever be triggered in the first place in API < + // 18. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + return false; + } + return performCursorMoveAction(semanticsNode, virtualViewId, arguments, true); } case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.DID_LOSE_ACCESSIBILITY_FOCUS); + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.DID_LOSE_ACCESSIBILITY_FOCUS + ); sendAccessibilityEvent( - virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); - mA11yFocusedObject = null; + virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + ); + accessibilityFocusedSemanticsNode = null; + embeddedAccessibilityFocusedNodeId = null; return true; } case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.DID_GAIN_ACCESSIBILITY_FOCUS); + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.DID_GAIN_ACCESSIBILITY_FOCUS + ); sendAccessibilityEvent( - virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED + ); - if (mA11yFocusedObject == null) { + if (accessibilityFocusedSemanticsNode == null) { // When Android focuses a node, it doesn't invalidate the view. // (It does when it sends ACTION_CLEAR_ACCESSIBILITY_FOCUS, so // we only have to worry about this when the focused node is null.) - mOwner.invalidate(); + rootAccessibilityView.invalidate(); } - mA11yFocusedObject = object; + accessibilityFocusedSemanticsNode = semanticsNode; - if (object.hasAction(Action.INCREASE) || object.hasAction(Action.DECREASE)) { + if (semanticsNode.hasAction(Action.INCREASE) || semanticsNode.hasAction(Action.DECREASE)) { // SeekBars only announce themselves after this event. sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED); } @@ -439,90 +889,122 @@ public boolean performAction(int virtualViewId, int action, Bundle arguments) { return true; } case ACTION_SHOW_ON_SCREEN: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.SHOW_ON_SCREEN); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SHOW_ON_SCREEN); return true; } case AccessibilityNodeInfo.ACTION_SET_SELECTION: { - final Map selection = new HashMap(); + // Text selection APIs aren't available until API 18. We can't handle the case here so return false + // instead. It's extremely unlikely that this case would ever be triggered in the first place in API < + // 18. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + return false; + } + final Map selection = new HashMap<>(); final boolean hasSelection = arguments != null - && arguments.containsKey( - AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT) - && arguments.containsKey( - AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT); + && arguments.containsKey(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT) + && arguments.containsKey(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT); if (hasSelection) { - selection.put("base", - arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT)); - selection.put("extent", - arguments.getInt( - AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT)); + selection.put( + "base", + arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT) + ); + selection.put( + "extent", + arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT) + ); } else { // Clear the selection - selection.put("base", object.textSelectionExtent); - selection.put("extent", object.textSelectionExtent); + selection.put("base", semanticsNode.textSelectionExtent); + selection.put("extent", semanticsNode.textSelectionExtent); } - mOwner.dispatchSemanticsAction(virtualViewId, Action.SET_SELECTION, selection); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SET_SELECTION, selection); return true; } case AccessibilityNodeInfo.ACTION_COPY: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.COPY); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.COPY); return true; } case AccessibilityNodeInfo.ACTION_CUT: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.CUT); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.CUT); return true; } case AccessibilityNodeInfo.ACTION_PASTE: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.PASTE); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.PASTE); return true; } case AccessibilityNodeInfo.ACTION_DISMISS: { - mOwner.dispatchSemanticsAction(virtualViewId, Action.DISMISS); + accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.DISMISS); return true; } default: - // might be a custom accessibility action. - final int flutterId = action - firstResourceId; - CustomAccessibilityAction contextAction = - mCustomAccessibilityActions.get(flutterId); + // might be a custom accessibility accessibilityAction. + final int flutterId = accessibilityAction - FIRST_RESOURCE_ID; + CustomAccessibilityAction contextAction = customAccessibilityActions.get(flutterId); if (contextAction != null) { - mOwner.dispatchSemanticsAction( - virtualViewId, Action.CUSTOM_ACTION, contextAction.id); + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.CUSTOM_ACTION, + contextAction.id + ); return true; } } return false; } - boolean performCursorMoveAction( - SemanticsObject object, int virtualViewId, Bundle arguments, boolean forward) { - final int granularity = - arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT); + /** + * Handles the responsibilities of {@link #performAction(int, int, Bundle)} for the specific + * scenario of cursor movement. + */ + @TargetApi(18) + @RequiresApi(18) + private boolean performCursorMoveAction( + @NonNull SemanticsNode semanticsNode, + int virtualViewId, + @NonNull Bundle arguments, + boolean forward + ) { + final int granularity = arguments.getInt( + AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + ); final boolean extendSelection = arguments.getBoolean( - AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN); + AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + ); switch (granularity) { case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER: { - if (forward && object.hasAction(Action.MOVE_CURSOR_FORWARD_BY_CHARACTER)) { - mOwner.dispatchSemanticsAction(virtualViewId, - Action.MOVE_CURSOR_FORWARD_BY_CHARACTER, extendSelection); + if (forward && semanticsNode.hasAction(Action.MOVE_CURSOR_FORWARD_BY_CHARACTER)) { + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.MOVE_CURSOR_FORWARD_BY_CHARACTER, + extendSelection + ); return true; } - if (!forward && object.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER)) { - mOwner.dispatchSemanticsAction(virtualViewId, - Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER, extendSelection); + if (!forward && semanticsNode.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER)) { + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.MOVE_CURSOR_BACKWARD_BY_CHARACTER, + extendSelection + ); return true; } break; } case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD: - if (forward && object.hasAction(Action.MOVE_CURSOR_FORWARD_BY_WORD)) { - mOwner.dispatchSemanticsAction(virtualViewId, - Action.MOVE_CURSOR_FORWARD_BY_WORD, extendSelection); + if (forward && semanticsNode.hasAction(Action.MOVE_CURSOR_FORWARD_BY_WORD)) { + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.MOVE_CURSOR_FORWARD_BY_WORD, + extendSelection + ); return true; } - if (!forward && object.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_WORD)) { - mOwner.dispatchSemanticsAction(virtualViewId, - Action.MOVE_CURSOR_BACKWARD_BY_WORD, extendSelection); + if (!forward && semanticsNode.hasAction(Action.MOVE_CURSOR_BACKWARD_BY_WORD)) { + accessibilityChannel.dispatchSemanticsAction( + virtualViewId, + Action.MOVE_CURSOR_BACKWARD_BY_WORD, + extendSelection + ); return true; } break; @@ -532,76 +1014,175 @@ boolean performCursorMoveAction( // TODO(ianh): implement findAccessibilityNodeInfosByText() + /** + * Finds the view in a hierarchy that currently has the given type of {@code focus}. + * + * This method is invoked by Android's accessibility system. + * + * Flutter does not have an Android {@link View} hierarchy. Therefore, Flutter conceptually + * handles this request by searching its semantics tree for the given {@code focus}, represented + * by {@link #flutterSemanticsTree}. In practice, this {@code AccessibilityBridge} always + * caches any active {@link #accessibilityFocusedSemanticsNode} and {@link #inputFocusedSemanticsNode}. + * Therefore, no searching is necessary. This method directly inspects the given {@code focus} + * type to return one of the cached nodes, null if the cached node is null, or null if a different + * {@code focus} type is requested. + */ @Override public AccessibilityNodeInfo findFocus(int focus) { switch (focus) { case AccessibilityNodeInfo.FOCUS_INPUT: { - if (mInputFocusedObject != null) - return createAccessibilityNodeInfo(mInputFocusedObject.id); + if (inputFocusedSemanticsNode != null) { + return createAccessibilityNodeInfo(inputFocusedSemanticsNode.id); + } + if (embeddedInputFocusedNodeId != null) { + return createAccessibilityNodeInfo(embeddedInputFocusedNodeId); + } } // Fall through to check FOCUS_ACCESSIBILITY case AccessibilityNodeInfo.FOCUS_ACCESSIBILITY: { - if (mA11yFocusedObject != null) - return createAccessibilityNodeInfo(mA11yFocusedObject.id); + if (accessibilityFocusedSemanticsNode != null) { + return createAccessibilityNodeInfo(accessibilityFocusedSemanticsNode.id); + } + if (embeddedAccessibilityFocusedNodeId != null) { + return createAccessibilityNodeInfo(embeddedAccessibilityFocusedNodeId); + } } } return null; } - private SemanticsObject getRootObject() { - assert mObjects.containsKey(0); - return mObjects.get(0); + /** + * Returns the {@link SemanticsNode} at the root of Flutter's semantics tree. + */ + private SemanticsNode getRootSemanticsNode() { + assert flutterSemanticsTree.containsKey(0); + return flutterSemanticsTree.get(0); } - private SemanticsObject getOrCreateObject(int id) { - SemanticsObject object = mObjects.get(id); - if (object == null) { - object = new SemanticsObject(); - object.id = id; - mObjects.put(id, object); - } - return object; + /** + * Returns an existing {@link SemanticsNode} with the given {@code id}, if it exists within + * {@link #flutterSemanticsTree}, or creates and returns a new {@link SemanticsNode} with the + * given {@code id}, adding the new {@link SemanticsNode} to the {@link #flutterSemanticsTree}. + * + * This method should only be invoked as a result of receiving new information from Flutter. + * The {@link #flutterSemanticsTree} is an Android cache of the last known state of a Flutter + * app's semantics tree, therefore, invoking this method in any other situation will result in + * a corrupt cache of Flutter's semantics tree. + */ + private SemanticsNode getOrCreateSemanticsNode(int id) { + SemanticsNode semanticsNode = flutterSemanticsTree.get(id); + if (semanticsNode == null) { + semanticsNode = new SemanticsNode(this); + semanticsNode.id = id; + flutterSemanticsTree.put(id, semanticsNode); + } + return semanticsNode; } - private CustomAccessibilityAction getOrCreateAction(int id) { - CustomAccessibilityAction action = mCustomAccessibilityActions.get(id); + /** + * Returns an existing {@link CustomAccessibilityAction} with the given {@code id}, if it exists + * within {@link #customAccessibilityActions}, or creates and returns a new {@link CustomAccessibilityAction} + * with the given {@code id}, adding the new {@link CustomAccessibilityAction} to the + * {@link #customAccessibilityActions}. + * + * This method should only be invoked as a result of receiving new information from Flutter. + * The {@link #customAccessibilityActions} is an Android cache of the last known state of a Flutter + * app's registered custom accessibility actions, therefore, invoking this method in any other + * situation will result in a corrupt cache of Flutter's accessibility actions. + */ + private CustomAccessibilityAction getOrCreateAccessibilityAction(int id) { + CustomAccessibilityAction action = customAccessibilityActions.get(id); if (action == null) { action = new CustomAccessibilityAction(); action.id = id; - action.resourceId = id + firstResourceId; - mCustomAccessibilityActions.put(id, action); + action.resourceId = id + FIRST_RESOURCE_ID; + customAccessibilityActions.put(id, action); } return action; } - void handleTouchExplorationExit() { - if (mHoveredObject != null) { - sendAccessibilityEvent(mHoveredObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); - mHoveredObject = null; + /** + * A hover {@link MotionEvent} has occurred in the {@code View} that corresponds to this + * {@code AccessibilityBridge}. + * + * This method returns true if Flutter's accessibility system handled the hover event, false + * otherwise. + * + * This method should be invoked from the corresponding {@code View}'s + * {@link View#onHoverEvent(MotionEvent)}. + */ + public boolean onAccessibilityHoverEvent(MotionEvent event) { + if (!accessibilityManager.isTouchExplorationEnabled()) { + return false; + } + + SemanticsNode semanticsNodeUnderCursor = getRootSemanticsNode().hitTest(new float[] {event.getX(), event.getY(), 0, 1}); + if (semanticsNodeUnderCursor.platformViewId != -1) { + return accessibilityViewEmbedder.onAccessibilityHoverEvent(semanticsNodeUnderCursor.id, event); + } + + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { + handleTouchExploration(event.getX(), event.getY()); + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + onTouchExplorationExit(); + } else { + Log.d("flutter", "unexpected accessibility hover event: " + event); + return false; + } + return true; + } + + /** + * This method should be invoked when a hover interaction has the cursor move off of a + * {@code SemanticsNode}. + * + * This method informs the Android accessibility system that a {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} + * has occurred. + */ + private void onTouchExplorationExit() { + if (hoveredObject != null) { + sendAccessibilityEvent(hoveredObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); + hoveredObject = null; } } - void handleTouchExploration(float x, float y) { - if (mObjects.isEmpty()) { + /** + * This method should be invoked when a new hover interaction begins with a {@code SemanticsNode}, + * or when an existing hover interaction sees a movement of the cursor. + * + * This method checks to see if the cursor has moved from one {@code SemanticsNode} to another. + * If it has, this method informs the Android accessibility system of the change by first sending + * a {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} event for the new hover node, followed by + * a {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} event for the old hover node. + */ + private void handleTouchExploration(float x, float y) { + if (flutterSemanticsTree.isEmpty()) { return; } - SemanticsObject newObject = getRootObject().hitTest(new float[] {x, y, 0, 1}); - if (newObject != mHoveredObject) { + SemanticsNode semanticsNodeUnderCursor = getRootSemanticsNode().hitTest(new float[] {x, y, 0, 1}); + if (semanticsNodeUnderCursor != hoveredObject) { // sending ENTER before EXIT is how Android wants it - if (newObject != null) { - sendAccessibilityEvent(newObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); + if (semanticsNodeUnderCursor != null) { + sendAccessibilityEvent(semanticsNodeUnderCursor.id, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); } - if (mHoveredObject != null) { - sendAccessibilityEvent(mHoveredObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); + if (hoveredObject != null) { + sendAccessibilityEvent(hoveredObject.id, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); } - mHoveredObject = newObject; + hoveredObject = semanticsNodeUnderCursor; } } - void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + /** + * Updates the Android cache of Flutter's currently registered custom accessibility actions. + */ + // TODO(mattcarroll): Consider introducing ability to delete custom actions because they can + // probably come and go in Flutter, so we may want to reflect that here in + // the Android cache as well. + // TODO(mattcarroll): where is the encoding code for reference? + void updateCustomAccessibilityActions(@NonNull ByteBuffer buffer, @NonNull String[] strings) { while (buffer.hasRemaining()) { int id = buffer.getInt(); - CustomAccessibilityAction action = getOrCreateAction(id); + CustomAccessibilityAction action = getOrCreateAccessibilityAction(id); action.overrideId = buffer.getInt(); int stringIndex = buffer.getInt(); action.label = stringIndex == -1 ? null : strings[stringIndex]; @@ -610,26 +1191,32 @@ void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { } } - void updateSemantics(ByteBuffer buffer, String[] strings) { - ArrayList updated = new ArrayList(); + /** + * Updates {@link #flutterSemanticsTree} to reflect the latest state of Flutter's semantics tree. + * + * The latest state of Flutter's semantics tree is encoded in the given {@code buffer}. + */ + // TODO(mattcarroll): where is the encoding code for reference? + void updateSemantics(@NonNull ByteBuffer buffer, @NonNull String[] strings) { + ArrayList updated = new ArrayList<>(); while (buffer.hasRemaining()) { int id = buffer.getInt(); - SemanticsObject object = getOrCreateObject(id); - object.updateWith(buffer, strings); - if (object.hasFlag(Flag.IS_HIDDEN)) { + SemanticsNode semanticsNode = getOrCreateSemanticsNode(id); + semanticsNode.updateWith(buffer, strings); + if (semanticsNode.hasFlag(Flag.IS_HIDDEN)) { continue; } - if (object.hasFlag(Flag.IS_FOCUSED)) { - mInputFocusedObject = object; + if (semanticsNode.hasFlag(Flag.IS_FOCUSED)) { + inputFocusedSemanticsNode = semanticsNode; } - if (object.hadPreviousConfig) { - updated.add(object); + if (semanticsNode.hadPreviousConfig) { + updated.add(semanticsNode); } } - Set visitedObjects = new HashSet(); - SemanticsObject rootObject = getRootObject(); - List newRoutes = new ArrayList<>(); + Set visitedObjects = new HashSet<>(); + SemanticsNode rootObject = getRootSemanticsNode(); + List newRoutes = new ArrayList<>(); if (rootObject != null) { final float[] identity = new float[16]; Matrix.setIdentityM(identity, 0); @@ -637,14 +1224,15 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { // of the screen in landscape mode. We must handle the translation ourselves for the // a11y nodes. if (Build.VERSION.SDK_INT >= 23) { - Rect visibleFrame = new Rect(); - mDecorView.getWindowVisibleDisplayFrame(visibleFrame); - if (!mLastLeftFrameInset.equals(visibleFrame.left)) { + WindowInsets insets = rootAccessibilityView.getRootWindowInsets(); + if (insets != null) { + if (!lastLeftFrameInset.equals(insets.getSystemWindowInsetLeft())) { rootObject.globalGeometryDirty = true; rootObject.inverseTransformDirty = true; + } + lastLeftFrameInset = insets.getSystemWindowInsetLeft(); + Matrix.translateM(identity, 0, lastLeftFrameInset, 0, 0); } - mLastLeftFrameInset = visibleFrame.left; - Matrix.translateM(identity, 0, visibleFrame.left, 0, 0); } rootObject.updateRecursively(identity, visitedObjects, false); rootObject.collectRoutes(newRoutes); @@ -652,10 +1240,10 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { // Dispatch a TYPE_WINDOW_STATE_CHANGED event if the most recent route id changed from the // previously cached route id. - SemanticsObject lastAdded = null; - for (SemanticsObject semanticsObject : newRoutes) { - if (!previousRoutes.contains(semanticsObject.id)) { - lastAdded = semanticsObject; + SemanticsNode lastAdded = null; + for (SemanticsNode semanticsNode : newRoutes) { + if (!flutterNavigationStack.contains(semanticsNode.id)) { + lastAdded = semanticsNode; } } if (lastAdded == null && newRoutes.size() > 0) { @@ -663,19 +1251,19 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { } if (lastAdded != null && lastAdded.id != previousRouteId) { previousRouteId = lastAdded.id; - createWindowChangeEvent(lastAdded); + createAndSendWindowChangeEvent(lastAdded); } - previousRoutes.clear(); - for (SemanticsObject semanticsObject : newRoutes) { - previousRoutes.add(semanticsObject.id); + flutterNavigationStack.clear(); + for (SemanticsNode semanticsNode : newRoutes) { + flutterNavigationStack.add(semanticsNode.id); } - Iterator> it = mObjects.entrySet().iterator(); + Iterator> it = flutterSemanticsTree.entrySet().iterator(); while (it.hasNext()) { - Map.Entry entry = it.next(); - SemanticsObject object = entry.getValue(); + Map.Entry entry = it.next(); + SemanticsNode object = entry.getValue(); if (!visitedObjects.contains(object)) { - willRemoveSemanticsObject(object); + willRemoveSemanticsNode(object); it.remove(); } } @@ -684,7 +1272,7 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { // see https://github.com/flutter/flutter/issues/14534 sendAccessibilityEvent(0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - for (SemanticsObject object : updated) { + for (SemanticsNode object : updated) { if (object.didScroll()) { AccessibilityEvent event = obtainAccessibilityEvent(object.id, AccessibilityEvent.TYPE_VIEW_SCROLLED); @@ -724,7 +1312,7 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { event.setFromIndex(object.scrollIndex); int visibleChildren = 0; // handle hidden children at the beginning and end of the list. - for (SemanticsObject child : object.childrenInHitTestOrder) { + for (SemanticsNode child : object.childrenInHitTestOrder) { if (!child.hasFlag(Flag.IS_HIDDEN)) { visibleChildren += 1; } @@ -750,20 +1338,25 @@ void updateSemantics(ByteBuffer buffer, String[] strings) { sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); } } else if (object.hasFlag(Flag.IS_TEXT_FIELD) && object.didChangeLabel() - && mInputFocusedObject != null && mInputFocusedObject.id == object.id) { + && inputFocusedSemanticsNode != null && inputFocusedSemanticsNode.id == object.id) { // Text fields should announce when their label changes while focused. We use a live // region tag to do so, and this event triggers that update. sendAccessibilityEvent(object.id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); } - if (mA11yFocusedObject != null && mA11yFocusedObject.id == object.id + if (accessibilityFocusedSemanticsNode != null && accessibilityFocusedSemanticsNode.id == object.id && !object.hadFlag(Flag.IS_SELECTED) && object.hasFlag(Flag.IS_SELECTED)) { AccessibilityEvent event = obtainAccessibilityEvent(object.id, AccessibilityEvent.TYPE_VIEW_SELECTED); event.getText().add(object.label); sendAccessibilityEvent(event); } - if (mInputFocusedObject != null && mInputFocusedObject.id == object.id - && object.hadFlag(Flag.IS_TEXT_FIELD) && object.hasFlag(Flag.IS_TEXT_FIELD)) { + if (inputFocusedSemanticsNode != null && inputFocusedSemanticsNode.id == object.id + && object.hadFlag(Flag.IS_TEXT_FIELD) && object.hasFlag(Flag.IS_TEXT_FIELD) + // If we have a TextField that has InputFocus, we should avoid announcing it if something + // else we track has a11y focus. This needs to still work when, e.g., IME has a11y focus + // or the "PASTE" popup is used though. + // See more discussion at https://github.com/flutter/flutter/issues/23180 + && (accessibilityFocusedSemanticsNode == null || (accessibilityFocusedSemanticsNode.id == inputFocusedSemanticsNode.id))) { String oldValue = object.previousValue != null ? object.previousValue : ""; String newValue = object.value != null ? object.value : ""; AccessibilityEvent event = createTextChangedEvent(object.id, oldValue, newValue); @@ -818,103 +1411,203 @@ private AccessibilityEvent createTextChangedEvent(int id, String oldValue, Strin return e; } - private AccessibilityEvent obtainAccessibilityEvent(int virtualViewId, int eventType) { - assert virtualViewId != ROOT_NODE_ID; - AccessibilityEvent event = AccessibilityEvent.obtain(eventType); - event.setPackageName(mOwner.getContext().getPackageName()); - event.setSource(mOwner, virtualViewId); - return event; - } - - private void sendAccessibilityEvent(int virtualViewId, int eventType) { - if (!mAccessibilityEnabled) { + /** + * Sends an accessibility event of the given {@code eventType} to Android's accessibility + * system with the given {@code viewId} represented as the source of the event. + * + * The given {@code viewId} may either belong to {@link #rootAccessibilityView}, or any + * Flutter {@link SemanticsNode}. + */ + private void sendAccessibilityEvent(int viewId, int eventType) { + if (!accessibilityManager.isEnabled()) { return; } - if (virtualViewId == ROOT_NODE_ID) { - mOwner.sendAccessibilityEvent(eventType); + if (viewId == ROOT_NODE_ID) { + rootAccessibilityView.sendAccessibilityEvent(eventType); } else { - sendAccessibilityEvent(obtainAccessibilityEvent(virtualViewId, eventType)); + sendAccessibilityEvent(obtainAccessibilityEvent(viewId, eventType)); } } - private void sendAccessibilityEvent(AccessibilityEvent event) { - if (!mAccessibilityEnabled) { + /** + * Sends the given {@link AccessibilityEvent} to Android's accessibility system for a given + * Flutter {@link SemanticsNode}. + * + * This method should only be called for a Flutter {@link SemanticsNode}, not a traditional + * Android {@code View}, i.e., {@link #rootAccessibilityView}. + */ + private void sendAccessibilityEvent(@NonNull AccessibilityEvent event) { + if (!accessibilityManager.isEnabled()) { return; } - mOwner.getParent().requestSendAccessibilityEvent(mOwner, event); + // TODO(mattcarroll): why are we explicitly talking to the root view's parent? + rootAccessibilityView.getParent().requestSendAccessibilityEvent(rootAccessibilityView, event); } - // Message Handler for [mFlutterAccessibilityChannel]. - public void onMessage(Object message, BasicMessageChannel.Reply reply) { - @SuppressWarnings("unchecked") - final HashMap annotatedEvent = (HashMap) message; - final String type = (String) annotatedEvent.get("type"); - @SuppressWarnings("unchecked") - final HashMap data = (HashMap) annotatedEvent.get("data"); - - switch (type) { - case "announce": - mOwner.announceForAccessibility((String) data.get("message")); - break; - case "longPress": { - Integer nodeId = (Integer) annotatedEvent.get("nodeId"); - if (nodeId == null) { - return; - } - sendAccessibilityEvent(nodeId, AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); - break; - } - case "tap": { - Integer nodeId = (Integer) annotatedEvent.get("nodeId"); - if (nodeId == null) { - return; - } - sendAccessibilityEvent(nodeId, AccessibilityEvent.TYPE_VIEW_CLICKED); - break; - } - case "tooltip": { - AccessibilityEvent e = obtainAccessibilityEvent( - ROOT_NODE_ID, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - e.getText().add((String) data.get("message")); - sendAccessibilityEvent(e); - break; - } - } + /** + * Factory method that creates a {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} and sends + * the event to Android's accessibility system. + * + * The given {@code route} should be a {@link SemanticsNode} that represents a navigation route + * in the Flutter app. + */ + private void createAndSendWindowChangeEvent(@NonNull SemanticsNode route) { + AccessibilityEvent event = obtainAccessibilityEvent( + route.id, + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + ); + String routeName = route.getRouteName(); + event.getText().add(routeName); + sendAccessibilityEvent(event); } - private void createWindowChangeEvent(SemanticsObject route) { - AccessibilityEvent e = - obtainAccessibilityEvent(route.id, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - String routeName = route.getRouteName(); - e.getText().add(routeName); - sendAccessibilityEvent(e); + /** + * Factory method that creates a new {@link AccessibilityEvent} that is configured to represent + * the Flutter {@link SemanticsNode} represented by the given {@code virtualViewId}, categorized + * as the given {@code eventType}. + * + * This method should *only* be called for Flutter {@link SemanticsNode}s. It should *not* be + * invoked to create an {@link AccessibilityEvent} for the {@link #rootAccessibilityView}. + */ + private AccessibilityEvent obtainAccessibilityEvent(int virtualViewId, int eventType) { + assert virtualViewId != ROOT_NODE_ID; + AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + event.setPackageName(rootAccessibilityView.getContext().getPackageName()); + event.setSource(rootAccessibilityView, virtualViewId); + return event; } - private void willRemoveSemanticsObject(SemanticsObject object) { - assert mObjects.containsKey(object.id); - assert mObjects.get(object.id) == object; - object.parent = null; - if (mA11yFocusedObject == object) { - sendAccessibilityEvent(mA11yFocusedObject.id, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); - mA11yFocusedObject = null; + /** + * Hook called just before a {@link SemanticsNode} is removed from the Android cache of Flutter's + * semantics tree. + */ + private void willRemoveSemanticsNode(SemanticsNode semanticsNodeToBeRemoved) { + assert flutterSemanticsTree.containsKey(semanticsNodeToBeRemoved.id); + assert flutterSemanticsTree.get(semanticsNodeToBeRemoved.id) == semanticsNodeToBeRemoved; + // TODO(mattcarroll): should parent be set to "null" here? Changing the parent seems like the + // behavior of a method called "removeSemanticsNode()". The same is true + // for null'ing accessibilityFocusedSemanticsNode, inputFocusedSemanticsNode, + // and hoveredObject. Is this a hook method or a command? + semanticsNodeToBeRemoved.parent = null; + if (accessibilityFocusedSemanticsNode == semanticsNodeToBeRemoved) { + sendAccessibilityEvent( + accessibilityFocusedSemanticsNode.id, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + ); + accessibilityFocusedSemanticsNode = null; + } + if (inputFocusedSemanticsNode == semanticsNodeToBeRemoved) { + inputFocusedSemanticsNode = null; + } + if (hoveredObject == semanticsNodeToBeRemoved) { + hoveredObject = null; } - if (mInputFocusedObject == object) { - mInputFocusedObject = null; + } + + /** + * Resets the {@code AccessibilityBridge}: + *
      + *
    • Clears {@link #flutterSemanticsTree}, the Android cache of Flutter's semantics tree
    • + *
    • Releases focus on any active {@link #accessibilityFocusedSemanticsNode}
    • + *
    • Clears any hovered {@code SemanticsNode}
    • + *
    • Sends a {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event
    • + *
    + */ + // TODO(mattcarroll): under what conditions is this method expected to be invoked? + public void reset() { + flutterSemanticsTree.clear(); + if (accessibilityFocusedSemanticsNode != null) { + sendAccessibilityEvent( + accessibilityFocusedSemanticsNode.id, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED + ); + } + accessibilityFocusedSemanticsNode = null; + hoveredObject = null; + sendAccessibilityEvent(0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + } + + /** + * Listener that can be set on a {@link AccessibilityBridge}, which is invoked any time + * accessibility is turned on/off, or touch exploration is turned on/off. + */ + public interface OnAccessibilityChangeListener { + void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled); + } + + // Must match SemanticsActions in semantics.dart + // https://github.com/flutter/engine/blob/master/lib/ui/semantics.dart + public enum Action { + TAP(1 << 0), + LONG_PRESS(1 << 1), + SCROLL_LEFT(1 << 2), + SCROLL_RIGHT(1 << 3), + SCROLL_UP(1 << 4), + SCROLL_DOWN(1 << 5), + INCREASE(1 << 6), + DECREASE(1 << 7), + SHOW_ON_SCREEN(1 << 8), + MOVE_CURSOR_FORWARD_BY_CHARACTER(1 << 9), + MOVE_CURSOR_BACKWARD_BY_CHARACTER(1 << 10), + SET_SELECTION(1 << 11), + COPY(1 << 12), + CUT(1 << 13), + PASTE(1 << 14), + DID_GAIN_ACCESSIBILITY_FOCUS(1 << 15), + DID_LOSE_ACCESSIBILITY_FOCUS(1 << 16), + CUSTOM_ACTION(1 << 17), + DISMISS(1 << 18), + MOVE_CURSOR_FORWARD_BY_WORD(1 << 19), + MOVE_CURSOR_BACKWARD_BY_WORD(1 << 20); + + public final int value; + + Action(int value) { + this.value = value; } - if (mHoveredObject == object) { - mHoveredObject = null; + } + + // Must match SemanticsFlag in semantics.dart + // https://github.com/flutter/engine/blob/master/lib/ui/semantics.dart + private enum Flag { + HAS_CHECKED_STATE(1 << 0), + IS_CHECKED(1 << 1), + IS_SELECTED(1 << 2), + IS_BUTTON(1 << 3), + IS_TEXT_FIELD(1 << 4), + IS_FOCUSED(1 << 5), + HAS_ENABLED_STATE(1 << 6), + IS_ENABLED(1 << 7), + IS_IN_MUTUALLY_EXCLUSIVE_GROUP(1 << 8), + IS_HEADER(1 << 9), + IS_OBSCURED(1 << 10), + SCOPES_ROUTE(1 << 11), + NAMES_ROUTE(1 << 12), + IS_HIDDEN(1 << 13), + IS_IMAGE(1 << 14), + IS_LIVE_REGION(1 << 15), + HAS_TOGGLED_STATE(1 << 16), + IS_TOGGLED(1 << 17), + HAS_IMPLICIT_SCROLLING(1 << 18); + + final int value; + + Flag(int value) { + this.value = value; } } - void reset() { - mObjects.clear(); - if (mA11yFocusedObject != null) - sendAccessibilityEvent(mA11yFocusedObject.id, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); - mA11yFocusedObject = null; - mHoveredObject = null; - sendAccessibilityEvent(0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + // Must match the enum defined in window.dart. + private enum AccessibilityFeature { + ACCESSIBLE_NAVIGATION(1 << 0), + INVERT_COLORS(1 << 1), // NOT SUPPORTED + DISABLE_ANIMATIONS(1 << 2); + + final int value; + + AccessibilityFeature(int value) { + this.value = value; + } } private enum TextDirection { @@ -933,59 +1626,96 @@ public static TextDirection fromInt(int value) { } } - private class CustomAccessibilityAction { + /** + * Accessibility action that is defined within a given Flutter application, as opposed to the + * standard accessibility actions that are available in the Flutter framework. + * + * Flutter and Android support a number of built-in accessibility actions. However, these + * predefined actions are not always sufficient for a desired interaction. Android facilitates + * custom accessibility actions, https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction. + * Flutter supports custom accessibility actions via {@code customSemanticsActions} within + * a {@code Semantics} widget, https://docs.flutter.io/flutter/widgets/Semantics-class.html. + * + * See the Android documentation for custom accessibility actions: + * https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.AccessibilityAction + * + * See the Flutter documentation for the Semantics widget: + * https://docs.flutter.io/flutter/widgets/Semantics-class.html + */ + private static class CustomAccessibilityAction { CustomAccessibilityAction() {} - /// Resource id is the id of the custom action plus a minimum value so that the identifier - /// does not collide with existing Android accessibility actions. - int resourceId = -1; - int id = -1; - int overrideId = -1; + // The ID of the custom action plus a minimum value so that the identifier + // does not collide with existing Android accessibility actions. This ID + // represents and Android resource ID, not a Flutter ID. + private int resourceId = -1; - /// The label is the user presented value which is displayed in the local context menu. - String label; + // The Flutter ID of this custom accessibility action. See Flutter's Semantics widget for + // custom accessibility action definitions: https://docs.flutter.io/flutter/widgets/Semantics-class.html + private int id = -1; - /// The hint is the text used in overriden standard actions. - String hint; + // The ID of the standard Flutter accessibility action that this {@code CustomAccessibilityAction} + // overrides with a custom {@code label} and/or {@code hint}. + private int overrideId = -1; - boolean isStandardAction() { - return overrideId != -1; - } + // The user presented value which is displayed in the local context menu. + private String label; + + // The text used in overridden standard actions. + private String hint; } - /// Value is derived from ACTION_TYPE_MASK in AccessibilityNodeInfo.java - static int firstResourceId = 267386881; - - private class SemanticsObject { - SemanticsObject() {} - - int id = -1; - - int flags; - int actions; - int textSelectionBase; - int textSelectionExtent; - int scrollChildren; - int scrollIndex; - float scrollPosition; - float scrollExtentMax; - float scrollExtentMin; - String label; - String value; - String increasedValue; - String decreasedValue; - String hint; - TextDirection textDirection; - - boolean hadPreviousConfig = false; - int previousFlags; - int previousActions; - int previousTextSelectionBase; - int previousTextSelectionExtent; - float previousScrollPosition; - float previousScrollExtentMax; - float previousScrollExtentMin; - String previousValue; - String previousLabel; + + /** + * Flutter {@code SemanticsNode} represented in Java/Android. + * + * Flutter maintains a semantics tree that is controlled by, but is independent of Flutter's + * element tree, i.e., widgets/elements/render objects. Flutter's semantics tree must be cached + * on the Android side so that Android can query any {@code SemanticsNode} at any time. This + * class represents a single node in the semantics tree, and it is a Java representation of the + * analogous concept within Flutter. + * + * To see how this {@code SemanticsNode}'s fields correspond to Flutter's semantics system, see + * semantics.dart: https://github.com/flutter/engine/blob/master/lib/ui/semantics.dart + */ + private static class SemanticsNode { + private static boolean nullableHasAncestor(SemanticsNode target, Predicate tester) { + return target != null && target.getAncestor(tester) != null; + } + + final AccessibilityBridge accessibilityBridge; + + // Flutter ID of this {@code SemanticsNode}. + private int id = -1; + + private int flags; + private int actions; + private int textSelectionBase; + private int textSelectionExtent; + private int platformViewId; + private int scrollChildren; + private int scrollIndex; + private float scrollPosition; + private float scrollExtentMax; + private float scrollExtentMin; + private String label; + private String value; + private String increasedValue; + private String decreasedValue; + private String hint; + + // See Flutter's {@code SemanticsNode#textDirection}. + private TextDirection textDirection; + + private boolean hadPreviousConfig = false; + private int previousFlags; + private int previousActions; + private int previousTextSelectionBase; + private int previousTextSelectionExtent; + private float previousScrollPosition; + private float previousScrollExtentMax; + private float previousScrollExtentMin; + private String previousValue; + private String previousLabel; private float left; private float top; @@ -993,12 +1723,12 @@ private class SemanticsObject { private float bottom; private float[] transform; - SemanticsObject parent; - List childrenInTraversalOrder; - List childrenInHitTestOrder; - List customAccessibilityActions; - CustomAccessibilityAction onTapOverride; - CustomAccessibilityAction onLongPressOverride; + private SemanticsNode parent; + private List childrenInTraversalOrder; + private List childrenInHitTestOrder; + private List customAccessibilityActions; + private CustomAccessibilityAction onTapOverride; + private CustomAccessibilityAction onLongPressOverride; private boolean inverseTransformDirty = true; private float[] inverseTransform; @@ -1007,51 +1737,80 @@ private class SemanticsObject { private float[] globalTransform; private Rect globalRect; - boolean hasAction(Action action) { + SemanticsNode(@NonNull AccessibilityBridge accessibilityBridge) { + this.accessibilityBridge = accessibilityBridge; + } + + /** + * Returns the ancestor of this {@code SemanticsNode} for which {@link Predicate#test(Object)} + * returns true, or null if no such ancestor exists. + */ + private SemanticsNode getAncestor(Predicate tester) { + SemanticsNode nextAncestor = parent; + while (nextAncestor != null) { + if (tester.test(nextAncestor)) { + return nextAncestor; + } + nextAncestor = nextAncestor.parent; + } + return null; + } + + /** + * Returns true if the given {@code action} is supported by this {@code SemanticsNode}. + * + * This method only applies to this {@code SemanticsNode} and does not implicitly search + * its children. + */ + private boolean hasAction(@NonNull Action action) { return (actions & action.value) != 0; } - boolean hadAction(Action action) { + /** + * Returns true if the given {@code action} was supported by the immediately previous + * version of this {@code SemanticsNode}. + */ + private boolean hadAction(@NonNull Action action) { return (previousActions & action.value) != 0; } - boolean hasFlag(Flag flag) { + private boolean hasFlag(@NonNull Flag flag) { return (flags & flag.value) != 0; } - boolean hadFlag(Flag flag) { + private boolean hadFlag(@NonNull Flag flag) { assert hadPreviousConfig; return (previousFlags & flag.value) != 0; } - boolean didScroll() { + private boolean didScroll() { return !Float.isNaN(scrollPosition) && !Float.isNaN(previousScrollPosition) && previousScrollPosition != scrollPosition; } - boolean didChangeLabel() { + private boolean didChangeLabel() { if (label == null && previousLabel == null) { return false; } return label == null || previousLabel == null || !label.equals(previousLabel); } - void log(String indent, boolean recursive) { + private void log(@NonNull String indent, boolean recursive) { Log.i(TAG, - indent + "SemanticsObject id=" + id + " label=" + label + " actions=" + actions + indent + "SemanticsNode id=" + id + " label=" + label + " actions=" + actions + " flags=" + flags + "\n" + indent + " +-- textDirection=" + textDirection + "\n" + indent + " +-- rect.ltrb=(" + left + ", " + top + ", " + right + ", " + bottom + ")\n" + indent + " +-- transform=" + Arrays.toString(transform) + "\n"); if (childrenInTraversalOrder != null && recursive) { String childIndent = indent + " "; - for (SemanticsObject child : childrenInTraversalOrder) { + for (SemanticsNode child : childrenInTraversalOrder) { child.log(childIndent, recursive); } } } - void updateWith(ByteBuffer buffer, String[] strings) { + private void updateWith(@NonNull ByteBuffer buffer, @NonNull String[] strings) { hadPreviousConfig = true; previousValue = value; previousLabel = label; @@ -1067,6 +1826,7 @@ void updateWith(ByteBuffer buffer, String[] strings) { actions = buffer.getInt(); textSelectionBase = buffer.getInt(); textSelectionExtent = buffer.getInt(); + platformViewId = buffer.getInt(); scrollChildren = buffer.getInt(); scrollIndex = buffer.getInt(); scrollPosition = buffer.getFloat(); @@ -1110,23 +1870,23 @@ void updateWith(ByteBuffer buffer, String[] strings) { childrenInHitTestOrder = null; } else { if (childrenInTraversalOrder == null) - childrenInTraversalOrder = new ArrayList(childCount); + childrenInTraversalOrder = new ArrayList<>(childCount); else childrenInTraversalOrder.clear(); for (int i = 0; i < childCount; ++i) { - SemanticsObject child = getOrCreateObject(buffer.getInt()); + SemanticsNode child = accessibilityBridge.getOrCreateSemanticsNode(buffer.getInt()); child.parent = this; childrenInTraversalOrder.add(child); } if (childrenInHitTestOrder == null) - childrenInHitTestOrder = new ArrayList(childCount); + childrenInHitTestOrder = new ArrayList<>(childCount); else childrenInHitTestOrder.clear(); for (int i = 0; i < childCount; ++i) { - SemanticsObject child = getOrCreateObject(buffer.getInt()); + SemanticsNode child = accessibilityBridge.getOrCreateSemanticsNode(buffer.getInt()); child.parent = this; childrenInHitTestOrder.add(child); } @@ -1136,19 +1896,18 @@ void updateWith(ByteBuffer buffer, String[] strings) { customAccessibilityActions = null; } else { if (customAccessibilityActions == null) - customAccessibilityActions = - new ArrayList(actionCount); + customAccessibilityActions = new ArrayList<>(actionCount); else customAccessibilityActions.clear(); for (int i = 0; i < actionCount; i++) { - CustomAccessibilityAction action = getOrCreateAction(buffer.getInt()); + CustomAccessibilityAction action = accessibilityBridge.getOrCreateAccessibilityAction(buffer.getInt()); if (action.overrideId == Action.TAP.value) { onTapOverride = action; } else if (action.overrideId == Action.LONG_PRESS.value) { onLongPressOverride = action; } else { - // If we recieve a different overrideId it means that we were passed + // If we receive a different overrideId it means that we were passed // a standard action to override that we don't yet support. assert action.overrideId == -1; customAccessibilityActions.add(action); @@ -1171,12 +1930,12 @@ private void ensureInverseTransform() { } } - Rect getGlobalRect() { + private Rect getGlobalRect() { assert !globalGeometryDirty; return globalRect; } - SemanticsObject hitTest(float[] point) { + private SemanticsNode hitTest(float[] point) { final float w = point[3]; final float x = point[0] / w; final float y = point[1] / w; @@ -1184,13 +1943,13 @@ SemanticsObject hitTest(float[] point) { if (childrenInHitTestOrder != null) { final float[] transformedPoint = new float[4]; for (int i = 0; i < childrenInHitTestOrder.size(); i += 1) { - final SemanticsObject child = childrenInHitTestOrder.get(i); + final SemanticsNode child = childrenInHitTestOrder.get(i); if (child.hasFlag(Flag.IS_HIDDEN)) { continue; } child.ensureInverseTransform(); Matrix.multiplyMV(transformedPoint, 0, child.inverseTransform, 0, point, 0); - final SemanticsObject result = child.hitTest(transformedPoint); + final SemanticsNode result = child.hitTest(transformedPoint); if (result != null) { return result; } @@ -1201,7 +1960,7 @@ SemanticsObject hitTest(float[] point) { // TODO(goderbauer): This should be decided by the framework once we have more information // about focusability there. - boolean isFocusable() { + private boolean isFocusable() { // We enforce in the framework that no other useful semantics are merged with these // nodes. if (hasFlag(Flag.SCOPES_ROUTE)) { @@ -1214,7 +1973,7 @@ boolean isFocusable() { || (hint != null && !hint.isEmpty()); } - void collectRoutes(List edges) { + private void collectRoutes(List edges) { if (hasFlag(Flag.SCOPES_ROUTE)) { edges.add(this); } @@ -1225,7 +1984,7 @@ void collectRoutes(List edges) { } } - String getRouteName() { + private String getRouteName() { // Returns the first non-null and non-empty semantic label of a child // with an NamesRoute flag. Otherwise returns null. if (hasFlag(Flag.NAMES_ROUTE)) { @@ -1244,7 +2003,7 @@ String getRouteName() { return null; } - void updateRecursively(float[] ancestorTransform, Set visitedObjects, + private void updateRecursively(float[] ancestorTransform, Set visitedObjects, boolean forceUpdate) { visitedObjects.add(this); @@ -1333,4 +2092,46 @@ private String getValueLabelHint() { return sb.length() > 0 ? sb.toString() : null; } } + + /** + * Delegates handling of {@link android.view.ViewParent#requestSendAccessibilityEvent} to the accessibility bridge. + * + * This is used by embedded platform views to propagate accessibility events from their view hierarchy to the + * accessibility bridge. + * + * As the embedded view doesn't have to be the only View in the embedded hierarchy (it can have child views) and the + * event might have been originated from any view in this hierarchy, this method gets both a reference to the + * embedded platform view, and a reference to the view from its hierarchy that sent the event. + * + * @param embeddedView the embedded platform view for which the event is delegated + * @param eventOrigin the view in the embedded view's hierarchy that sent the event. + * @return True if the event was sent. + */ + public boolean externalViewRequestSendAccessibilityEvent(View embeddedView, View eventOrigin, AccessibilityEvent event) { + if (!accessibilityViewEmbedder.requestSendAccessibilityEvent(embeddedView, eventOrigin, event)){ + return false; + } + Integer virtualNodeId = accessibilityViewEmbedder.getRecordFlutterId(embeddedView, event); + if (virtualNodeId == null) { + return false; + } + switch(event.getEventType()) { + case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: + hoveredObject = null; + break; + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: + embeddedAccessibilityFocusedNodeId = virtualNodeId; + accessibilityFocusedSemanticsNode = null; + break; + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: + embeddedInputFocusedNodeId = null; + embeddedAccessibilityFocusedNodeId = null; + break; + case AccessibilityEvent.TYPE_VIEW_FOCUSED: + embeddedInputFocusedNodeId = virtualNodeId; + inputFocusedSemanticsNode = null; + break; + } + return true; + } } diff --git a/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java b/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java new file mode 100644 index 0000000000000..fbc2260d260e0 --- /dev/null +++ b/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java @@ -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. + +package io.flutter.view; + +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.accessibility.AccessibilityRecord; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * Facilitates embedding of platform views in the accessibility tree generated by the accessibility bridge. + * + * Embedding is done by mirroring the accessibility tree of the platform view as a subtree of the flutter + * accessibility tree. + * + * This class relies on hidden system APIs to extract the accessibility information and does not work starting + * Android P; If the reflection accessors are not available we fail silently by embedding a null node, the app + * continues working but the accessibility information for the platform view will not be embedded. + * + * We use the term `flutterId` for virtual accessibility node IDs in the FlutterView tree, and the term `originId` + * for the virtual accessibility node IDs in the platform view's tree. Internally this class maintains a bidirectional + * mapping between `flutterId`s and the corresponding platform view and `originId`. + */ +class AccessibilityViewEmbedder { + private static final String TAG = "AccessibilityBridge"; + + private final ReflectionAccessors reflectionAccessors; + + // The view to which the platform view is embedded, this is typically FlutterView. + private final View rootAccessibilityView; + + // Maps a flutterId to the corresponding platform view and originId. + private final SparseArray flutterIdToOrigin; + + // Maps a platform view and originId to a corresponding flutterID. + private final Map originToFlutterId; + + // Maps an embedded view to it's screen bounds. + // This is used to translate the coordinates of the accessibility node subtree to the main display's coordinate + // system. + private final Map embeddedViewToDisplayBounds; + + private int nextFlutterId; + + AccessibilityViewEmbedder(@NonNull View rootAccessibiiltyView, int firstVirtualNodeId) { + reflectionAccessors = new ReflectionAccessors(); + flutterIdToOrigin = new SparseArray<>(); + this.rootAccessibilityView = rootAccessibiiltyView; + nextFlutterId = firstVirtualNodeId; + originToFlutterId = new HashMap<>(); + embeddedViewToDisplayBounds = new HashMap<>(); + } + + /** + * Returns the root accessibility node for an embedded platform view. + * + * @param flutterId the virtual accessibility ID for the node in flutter accessibility tree + * @param displayBounds the display bounds for the node in screen coordinates + */ + public AccessibilityNodeInfo getRootNode(@NonNull View embeddedView, int flutterId, @NonNull Rect displayBounds) { + AccessibilityNodeInfo originNode = embeddedView.createAccessibilityNodeInfo(); + Long originPackedId = reflectionAccessors.getSourceNodeId(originNode); + if (originPackedId == null) { + return null; + } + embeddedViewToDisplayBounds.put(embeddedView, displayBounds); + int originId = ReflectionAccessors.getVirtualNodeId(originPackedId); + cacheVirtualIdMappings(embeddedView, originId, flutterId); + return convertToFlutterNode(originNode, flutterId, embeddedView); + } + + /** + * Creates the accessibility node info for the node identified with `flutterId`. + */ + @Nullable + public AccessibilityNodeInfo createAccessibilityNodeInfo(int flutterId) { + ViewAndId origin = flutterIdToOrigin.get(flutterId); + if (origin == null) { + return null; + } + if (!embeddedViewToDisplayBounds.containsKey(origin.view)) { + // This might happen if the embedded view is sending accessibility event before the first Flutter semantics + // tree was sent to the accessibility bridge. In this case we don't return a node as we do not know the + // bounds yet. + // https://github.com/flutter/flutter/issues/30068 + return null; + } + AccessibilityNodeProvider provider = origin.view.getAccessibilityNodeProvider(); + if (provider == null) { + // The provider is null for views that don't have a virtual accessibility tree. + // We currently only support embedding virtual hierarchies in the Flutter tree. + // TODO(amirh): support embedding non virtual hierarchies. + // https://github.com/flutter/flutter/issues/29717 + return null; + } + AccessibilityNodeInfo originNode = + origin.view.getAccessibilityNodeProvider().createAccessibilityNodeInfo(origin.id); + if (originNode == null) { + return null; + } + return convertToFlutterNode(originNode, flutterId, origin.view); + } + + /* + * Creates an AccessibilityNodeInfo that can be attached to the Flutter accessibility tree and is equivalent to + * originNode(which belongs to embeddedView). The virtual ID for the created node will be flutterId. + */ + @NonNull + private AccessibilityNodeInfo convertToFlutterNode( + @NonNull AccessibilityNodeInfo originNode, + int flutterId, + @NonNull View embeddedView + ) { + AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(rootAccessibilityView, flutterId); + result.setPackageName(rootAccessibilityView.getContext().getPackageName()); + result.setSource(rootAccessibilityView, flutterId); + result.setClassName(originNode.getClassName()); + + Rect displayBounds = embeddedViewToDisplayBounds.get(embeddedView); + + copyAccessibilityFields(originNode, result); + setFlutterNodesTranslateBounds(originNode, displayBounds, result); + addChildrenToFlutterNode(originNode, embeddedView, displayBounds, result); + setFlutterNodeParent(originNode, embeddedView, result); + + return result; + } + + private void setFlutterNodeParent( + @NonNull AccessibilityNodeInfo originNode, + @NonNull View embeddedView, + @NonNull AccessibilityNodeInfo result + ) { + Long parentOriginPackedId = reflectionAccessors.getParentNodeId(originNode); + if (parentOriginPackedId == null) { + return; + } + int parentOriginId = ReflectionAccessors.getVirtualNodeId(parentOriginPackedId); + Integer parentFlutterId = originToFlutterId.get(new ViewAndId(embeddedView, parentOriginId)); + if (parentFlutterId != null) { + result.setParent(rootAccessibilityView, parentFlutterId); + } + } + + + private void addChildrenToFlutterNode( + @NonNull AccessibilityNodeInfo originNode, + @NonNull View embeddedView, + @NonNull Rect displayBounds, + @NonNull AccessibilityNodeInfo resultNode + ) { + for (int i = 0; i < originNode.getChildCount(); i++) { + Long originPackedId = reflectionAccessors.getChildId(originNode, i); + if (originPackedId == null) { + continue; + } + int originId = ReflectionAccessors.getVirtualNodeId(originPackedId); + ViewAndId origin = new ViewAndId(embeddedView, originId); + int childFlutterId; + if (originToFlutterId.containsKey(origin)) { + childFlutterId = originToFlutterId.get(origin); + } else { + childFlutterId = nextFlutterId++; + cacheVirtualIdMappings(embeddedView, originId, childFlutterId); + } + resultNode.addChild(rootAccessibilityView, childFlutterId); + } + } + + // Caches a bidirectional mapping of (embeddedView, originId)<-->flutterId. + // Where originId is a virtual node ID in the embeddedView's tree, and flutterId is the ID + // of the corresponding node in the Flutter virtual accessibility nodes tree. + private void cacheVirtualIdMappings(@NonNull View embeddedView, int originId, int flutterId) { + ViewAndId origin = new ViewAndId(embeddedView, originId); + originToFlutterId.put(origin, flutterId); + flutterIdToOrigin.put(flutterId, origin); + } + + private void setFlutterNodesTranslateBounds( + @NonNull AccessibilityNodeInfo originNode, + @NonNull Rect displayBounds, + @NonNull AccessibilityNodeInfo resultNode + ) { + Rect boundsInParent = new Rect(); + originNode.getBoundsInParent(boundsInParent); + resultNode.setBoundsInParent(boundsInParent); + + Rect boundsInScreen = new Rect(); + originNode.getBoundsInScreen(boundsInScreen); + boundsInScreen.offset(displayBounds.left, displayBounds.top); + resultNode.setBoundsInScreen(boundsInScreen); + } + + private void copyAccessibilityFields(@NonNull AccessibilityNodeInfo input, @NonNull AccessibilityNodeInfo output) { + output.setAccessibilityFocused(input.isAccessibilityFocused()); + output.setCheckable(input.isCheckable()); + output.setChecked(input.isChecked()); + output.setContentDescription(input.getContentDescription()); + output.setEnabled(input.isEnabled()); + output.setClickable(input.isClickable()); + output.setFocusable(input.isFocusable()); + output.setFocused(input.isFocused()); + output.setLongClickable(input.isLongClickable()); + output.setMovementGranularities(input.getMovementGranularities()); + output.setPassword(input.isPassword()); + output.setScrollable(input.isScrollable()); + output.setSelected(input.isSelected()); + output.setText(input.getText()); + output.setVisibleToUser(input.isVisibleToUser()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + output.setEditable(input.isEditable()); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + output.setCanOpenPopup(input.canOpenPopup()); + output.setCollectionInfo(input.getCollectionInfo()); + output.setCollectionItemInfo(input.getCollectionItemInfo()); + output.setContentInvalid(input.isContentInvalid()); + output.setDismissable(input.isDismissable()); + output.setInputType(input.getInputType()); + output.setLiveRegion(input.getLiveRegion()); + output.setMultiLine(input.isMultiLine()); + output.setRangeInfo(input.getRangeInfo()); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + output.setError(input.getError()); + output.setMaxTextLength(input.getMaxTextLength()); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + output.setContextClickable(input.isContextClickable()); + // TODO(amirh): copy traversal before and after. + // https://github.com/flutter/flutter/issues/29718 + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + output.setDrawingOrder(input.getDrawingOrder()); + output.setImportantForAccessibility(input.isImportantForAccessibility()); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + output.setAvailableExtraData(input.getAvailableExtraData()); + output.setHintText(input.getHintText()); + output.setShowingHintText(input.isShowingHintText()); + } + } + + /** + * Delegates an AccessibilityNodeProvider#requestSendAccessibilityEvent from the AccessibilityBridge to the embedded + * view. + * + * @return True if the event was sent. + */ + public boolean requestSendAccessibilityEvent( + @NonNull View embeddedView, + @NonNull View eventOrigin, + @NonNull AccessibilityEvent event + ) { + AccessibilityEvent translatedEvent = AccessibilityEvent.obtain(event); + Long originPackedId = reflectionAccessors.getRecordSourceNodeId(event); + if (originPackedId == null) { + return false; + } + int originVirtualId = ReflectionAccessors.getVirtualNodeId(originPackedId); + Integer flutterId = originToFlutterId.get(new ViewAndId(embeddedView, originVirtualId)); + if (flutterId == null) { + flutterId = nextFlutterId++; + cacheVirtualIdMappings(embeddedView, originVirtualId, flutterId); + } + translatedEvent.setSource(rootAccessibilityView, flutterId); + translatedEvent.setClassName(event.getClassName()); + translatedEvent.setPackageName(event.getPackageName()); + + for (int i = 0; i < translatedEvent.getRecordCount(); i++) { + AccessibilityRecord record = translatedEvent.getRecord(i); + Long recordOriginPackedId = reflectionAccessors.getRecordSourceNodeId(record); + if (recordOriginPackedId == null) { + return false; + } + int recordOriginVirtualID = ReflectionAccessors.getVirtualNodeId(recordOriginPackedId); + ViewAndId originViewAndId = new ViewAndId(embeddedView, recordOriginVirtualID); + if (!originToFlutterId.containsKey(originViewAndId)) { + return false; + } + int recordFlutterId = originToFlutterId.get(originViewAndId); + record.setSource(rootAccessibilityView, recordFlutterId); + } + + return rootAccessibilityView.getParent().requestSendAccessibilityEvent(eventOrigin, translatedEvent); + } + + /** + * Delegates an @{link AccessibilityNodeProvider#performAction} from the AccessibilityBridge to the embedded view's + * accessibility node provider. + * + * @return True if the action was performed. + */ + public boolean performAction(int flutterId, int accessibilityAction, @Nullable Bundle arguments) { + ViewAndId origin = flutterIdToOrigin.get(flutterId); + if (origin == null) { + return false; + } + View embeddedView = origin.view; + AccessibilityNodeProvider provider = embeddedView.getAccessibilityNodeProvider(); + if (provider == null) { + return false; + } + return provider.performAction(origin.id, accessibilityAction, arguments); + } + + /** + * Returns a flutterID for an accessibility record, or null if no mapping exists. + * + * @param embeddedView the embedded view that the record is associated with. + */ + @Nullable + public Integer getRecordFlutterId(@NonNull View embeddedView, @NonNull AccessibilityRecord record) { + Long originPackedId = reflectionAccessors.getRecordSourceNodeId(record); + if (originPackedId == null) { + return null; + } + int originVirtualId = ReflectionAccessors.getVirtualNodeId(originPackedId); + return originToFlutterId.get(new ViewAndId(embeddedView, originVirtualId)); + } + + /** + * Delegates a View#onHoverEvent event from the AccessibilityBridge to an embedded view. + * + * The pointer coordinates are translated to the embedded view's coordinate system. + */ + public boolean onAccessibilityHoverEvent(int rootFlutterId, @NonNull MotionEvent event) { + ViewAndId origin = flutterIdToOrigin.get(rootFlutterId); + if (origin == null) { + return false; + } + Rect displayBounds = embeddedViewToDisplayBounds.get(origin.view); + int pointerCount = event.getPointerCount(); + MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[pointerCount]; + MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount]; + for(int i = 0; i < event.getPointerCount(); i++) { + pointerProperties[i] = new MotionEvent.PointerProperties(); + event.getPointerProperties(i, pointerProperties[i]); + + MotionEvent.PointerCoords originCoords = new MotionEvent.PointerCoords(); + event.getPointerCoords(i, originCoords); + + pointerCoords[i] = new MotionEvent.PointerCoords((originCoords)); + pointerCoords[i].x -= displayBounds.left; + pointerCoords[i].y -= displayBounds.top; + + } + MotionEvent translatedEvent = MotionEvent.obtain( + event.getDownTime(), + event.getEventTime(), + event.getAction(), + event.getPointerCount(), + pointerProperties, + pointerCoords, + event.getMetaState(), + event.getButtonState(), + event.getXPrecision(), + event.getYPrecision(), + event.getDeviceId(), + event.getEdgeFlags(), + event.getSource(), + event.getFlags() + ); + return origin.view.dispatchGenericMotionEvent(translatedEvent); + } + + private static class ViewAndId { + final View view; + final int id; + + private ViewAndId(View view, int id) { + this.view = view; + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ViewAndId viewAndId = (ViewAndId) o; + return id == viewAndId.id && + view.equals(viewAndId.view); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + view.hashCode(); + result = prime * result + id; + return result; + } + } + + private static class ReflectionAccessors { + private final Method getSourceNodeId; + private final Method getParentNodeId; + private final Method getRecordSourceNodeId; + private final Method getChildId; + + private ReflectionAccessors() { + Method getSourceNodeId = null; + Method getParentNodeId = null; + Method getRecordSourceNodeId = null; + Method getChildId = null; + try { + getSourceNodeId = AccessibilityNodeInfo.class.getMethod("getSourceNodeId"); + } catch (NoSuchMethodException e) { + Log.w(TAG, "can't invoke AccessibilityNodeInfo#getSourceNodeId with reflection"); + } + try { + getParentNodeId = AccessibilityNodeInfo.class.getMethod("getParentNodeId"); + } catch (NoSuchMethodException e) { + Log.w(TAG, "can't invoke getParentNodeId with reflection"); + } + try { + getRecordSourceNodeId = AccessibilityRecord.class.getMethod("getSourceNodeId"); + } catch (NoSuchMethodException e) { + Log.w(TAG, "can't invoke AccessibiiltyRecord#getSourceNodeId with reflection"); + } + try { + getChildId = AccessibilityNodeInfo.class.getMethod("getChildId", int.class); + } catch (NoSuchMethodException e) { + Log.w(TAG, "can't invoke getChildId with reflection"); + } + this.getSourceNodeId = getSourceNodeId; + this.getParentNodeId = getParentNodeId; + this.getRecordSourceNodeId = getRecordSourceNodeId; + this.getChildId = getChildId; + } + + /** Returns virtual node ID given packed node ID used internally in accessibility API. */ + private static int getVirtualNodeId(long nodeId) { + return (int) (nodeId >> 32); + } + + @Nullable + private Long getSourceNodeId(@NonNull AccessibilityNodeInfo node) { + if (getSourceNodeId == null) { + return null; + } + try { + return (Long) getSourceNodeId.invoke(node); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } catch (InvocationTargetException e) { + Log.w(TAG, e); + } + return null; + } + + @Nullable + private Long getChildId(@NonNull AccessibilityNodeInfo node, int child) { + if (getChildId == null) { + return null; + } + try { + return (Long) getChildId.invoke(node, child); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } catch (InvocationTargetException e) { + Log.w(TAG, e); + } + return null; + } + + @Nullable + private Long getParentNodeId(@NonNull AccessibilityNodeInfo node) { + if (getParentNodeId == null) { + return null; + } + try { + return (long) getParentNodeId.invoke(node); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } catch (InvocationTargetException e) { + Log.w(TAG, e); + } + return null; + } + + @Nullable + private Long getRecordSourceNodeId(@NonNull AccessibilityRecord node) { + if (getRecordSourceNodeId == null) { + return null; + } + try { + return (Long) getRecordSourceNodeId.invoke(node); + } catch (IllegalAccessException e) { + Log.w(TAG, e); + } catch (InvocationTargetException e) { + Log.w(TAG, e); + } + return null; + } + } +} diff --git a/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java b/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java index 20006dd60547c..f6157a0f31d1a 100644 --- a/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java +++ b/shell/platform/android/io/flutter/view/FlutterCallbackInformation.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/view/FlutterMain.java b/shell/platform/android/io/flutter/view/FlutterMain.java index cb4bd64e485bf..c632dca387ced 100644 --- a/shell/platform/android/io/flutter/view/FlutterMain.java +++ b/shell/platform/android/io/flutter/view/FlutterMain.java @@ -1,12 +1,16 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.view; import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.os.Bundle; import android.os.Looper; import android.os.SystemClock; @@ -56,13 +60,10 @@ public class FlutterMain { private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_DATA = "isolate_snapshot_data"; private static final String DEFAULT_AOT_ISOLATE_SNAPSHOT_INSTR = "isolate_snapshot_instr"; private static final String DEFAULT_FLX = "app.flx"; + private static final String DEFAULT_LIBRARY = "libflutter.so"; private static final String DEFAULT_KERNEL_BLOB = "kernel_blob.bin"; private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets"; - // Assets that are shared among all Flutter apps within an APK. - private static final String SHARED_ASSET_DIR = "flutter_shared"; - private static final String SHARED_ASSET_ICU_DATA = "icudtl.dat"; - private static String fromFlutterAssets(String filePath) { return sFlutterAssetsDir + File.separator + filePath; } @@ -77,11 +78,11 @@ private static String fromFlutterAssets(String filePath) { private static String sFlutterAssetsDir = DEFAULT_FLUTTER_ASSETS_DIR; private static boolean sInitialized = false; + private static ResourceUpdater sResourceUpdater; private static ResourceExtractor sResourceExtractor; private static boolean sIsPrecompiledAsBlobs; private static boolean sIsPrecompiledAsSharedLibrary; private static Settings sSettings; - private static String sIcuDataPath; private static final class ImmutableSetBuilder { static ImmutableSetBuilder newInstance() { @@ -154,7 +155,18 @@ public static void startInitialization(Context applicationContext, Settings sett initConfig(applicationContext); initAot(applicationContext); initResources(applicationContext); - System.loadLibrary("flutter"); + + if (sResourceUpdater == null) { + System.loadLibrary("flutter"); + } else { + sResourceExtractor.waitForCompletion(); + File lib = new File(PathUtils.getDataDirectory(applicationContext), DEFAULT_LIBRARY); + if (lib.exists()) { + System.load(lib.getAbsolutePath()); + } else { + System.loadLibrary("flutter"); + } + } // We record the initialization time using SystemClock because at the start of the // initialization we have not yet loaded the native library to call into dart_tools_api.h. @@ -184,7 +196,12 @@ public static void ensureInitializationComplete(Context applicationContext, Stri sResourceExtractor.waitForCompletion(); List shellArgs = new ArrayList<>(); - shellArgs.add("--icu-data-file-path=" + sIcuDataPath); + + shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat"); + ApplicationInfo applicationInfo = applicationContext.getPackageManager().getApplicationInfo( + applicationContext.getPackageName(), PackageManager.GET_META_DATA); + shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY); + if (args != null) { Collections.addAll(shellArgs, args); } @@ -254,11 +271,31 @@ private static void initResources(Context applicationContext) { Context context = applicationContext; new ResourceCleaner(context).start(); - sResourceExtractor = new ResourceExtractor(context); + Bundle metaData = null; + try { + metaData = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA).metaData; + + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to read application info", e); + } + + if (metaData != null && metaData.getBoolean("DynamicPatching")) { + sResourceUpdater = new ResourceUpdater(context); + // Also checking for ON_RESUME here since it's more efficient than waiting for actual + // onResume. Even though actual onResume is imminent when the app has just restarted, + // it's better to start downloading now, in parallel with the rest of initialization, + // and avoid a second application restart a bit later when actual onResume happens. + if (sResourceUpdater.getDownloadMode() == ResourceUpdater.DownloadMode.ON_RESTART || + sResourceUpdater.getDownloadMode() == ResourceUpdater.DownloadMode.ON_RESUME) { + sResourceUpdater.startUpdateDownloadOnce(); + if (sResourceUpdater.getInstallMode() == ResourceUpdater.InstallMode.IMMEDIATE) { + sResourceUpdater.waitForDownloadCompletion(); + } + } + } - String icuAssetPath = SHARED_ASSET_DIR + File.separator + SHARED_ASSET_ICU_DATA; - sResourceExtractor.addResource(icuAssetPath); - sIcuDataPath = PathUtils.getDataDirectory(applicationContext) + File.separator + icuAssetPath; + sResourceExtractor = new ResourceExtractor(context); sResourceExtractor .addResource(fromFlutterAssets(sFlx)) @@ -267,9 +304,11 @@ private static void initResources(Context applicationContext) { .addResource(fromFlutterAssets(sAotIsolateSnapshotData)) .addResource(fromFlutterAssets(sAotIsolateSnapshotInstr)) .addResource(fromFlutterAssets(DEFAULT_KERNEL_BLOB)); + if (sIsPrecompiledAsSharedLibrary) { sResourceExtractor .addResource(sAotSharedLibraryPath); + } else { sResourceExtractor .addResource(sAotVmSnapshotData) @@ -277,9 +316,23 @@ private static void initResources(Context applicationContext) { .addResource(sAotIsolateSnapshotData) .addResource(sAotIsolateSnapshotInstr); } + + if (sResourceUpdater != null) { + sResourceExtractor + .addResource(DEFAULT_LIBRARY); + } + sResourceExtractor.start(); } + public static void onResume(Context context) { + if (sResourceUpdater != null) { + if (sResourceUpdater.getDownloadMode() == ResourceUpdater.DownloadMode.ON_RESUME) { + sResourceUpdater.startUpdateDownloadOnce(); + } + } + } + /** * Returns a list of the file names at the root of the application's asset * path. @@ -321,13 +374,22 @@ public static String findAppBundlePath(Context applicationContext) { return appBundle.exists() ? appBundle.getPath() : null; } + /** + * Returns the main internal interface for the dynamic patching subsystem. + * + * If this is null, it means that dynamic patching is disabled in this app. + */ + public static ResourceUpdater getResourceUpdater() { + return sResourceUpdater; + } + /** * Returns the file name for the given asset. * The returned file name can be used to access the asset in the APK - * through the {@link AssetManager} API. + * through the {@link android.content.res.AssetManager} API. * * @param asset the name of the asset. The name can be hierarchical - * @return the filename to be used with {@link AssetManager} + * @return the filename to be used with {@link android.content.res.AssetManager} */ public static String getLookupKeyForAsset(String asset) { return fromFlutterAssets(asset); @@ -336,11 +398,11 @@ public static String getLookupKeyForAsset(String asset) { /** * Returns the file name for the given asset which originates from the * specified packageName. The returned file name can be used to access - * the asset in the APK through the {@link AssetManager} API. + * the asset in the APK through the {@link android.content.res.AssetManager} API. * * @param asset the name of the asset. The name can be hierarchical * @param packageName the name of the package from which the asset originates - * @return the file name to be used with {@link AssetManager} + * @return the file name to be used with {@link android.content.res.AssetManager} */ public static String getLookupKeyForAsset(String asset, String packageName) { return getLookupKeyForAsset( diff --git a/shell/platform/android/io/flutter/view/FlutterNativeView.java b/shell/platform/android/io/flutter/view/FlutterNativeView.java index 3a9ab500bc6a8..2c2b57a5f174a 100644 --- a/shell/platform/android/io/flutter/view/FlutterNativeView.java +++ b/shell/platform/android/io/flutter/view/FlutterNativeView.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,54 +6,66 @@ import android.app.Activity; import android.content.Context; +import android.support.annotation.NonNull; import android.util.Log; import io.flutter.app.FlutterPluginRegistry; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface; import io.flutter.plugin.common.*; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; import java.util.HashMap; import java.util.Map; -import android.content.res.AssetManager; + +import io.flutter.embedding.engine.dart.PlatformMessageHandler; public class FlutterNativeView implements BinaryMessenger { private static final String TAG = "FlutterNativeView"; - private final Map mMessageHandlers; - private int mNextReplyId = 1; - private final Map mPendingReplies = new HashMap<>(); - private final FlutterPluginRegistry mPluginRegistry; - private long mNativePlatformView; + private final DartExecutor dartExecutor; private FlutterView mFlutterView; + private final FlutterJNI mFlutterJNI; private final Context mContext; private boolean applicationIsRunning; - public FlutterNativeView(Context context) { + public FlutterNativeView(@NonNull Context context) { this(context, false); } - public FlutterNativeView(Context context, boolean isBackgroundView) { + public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) { mContext = context; mPluginRegistry = new FlutterPluginRegistry(this, context); + mFlutterJNI = new FlutterJNI(); + mFlutterJNI.setRenderSurface(new RenderSurfaceImpl()); + this.dartExecutor = new DartExecutor(mFlutterJNI); + mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl()); attach(this, isBackgroundView); assertAttached(); - mMessageHandlers = new HashMap<>(); } - public void detach() { + public void detachFromFlutterView() { mPluginRegistry.detach(); mFlutterView = null; - nativeDetach(mNativePlatformView); } public void destroy() { mPluginRegistry.destroy(); + dartExecutor.onDetachedFromJNI(); mFlutterView = null; - nativeDestroy(mNativePlatformView); - mNativePlatformView = 0; + mFlutterJNI.detachFromNativeAndReleaseResources(); applicationIsRunning = false; } + @NonNull + public DartExecutor getDartExecutor() { + return dartExecutor; + } + + @NonNull public FlutterPluginRegistry getPluginRegistry() { return mPluginRegistry; } @@ -64,11 +76,7 @@ public void attachViewAndActivity(FlutterView flutterView, Activity activity) { } public boolean isAttached() { - return mNativePlatformView != 0; - } - - public long get() { - return mNativePlatformView; + return mFlutterJNI.isAttached(); } public void assertAttached() { @@ -76,12 +84,21 @@ public void assertAttached() { } public void runFromBundle(FlutterRunArguments args) { - if (args.bundlePath == null) { - throw new AssertionError("A bundlePath must be specified"); + boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0; + if (args.bundlePath == null && !hasBundlePaths) { + throw new AssertionError("Either bundlePath or bundlePaths must be specified"); + } else if ((args.bundlePath != null || args.defaultPath != null) && + hasBundlePaths) { + throw new AssertionError("Can't specify both bundlePath and bundlePaths"); } else if (args.entrypoint == null) { - throw new AssertionError("An entrypoint must be specified"); + throw new AssertionError("An entrypoint must be specified"); + } + if (hasBundlePaths) { + runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath); + } else { + runFromBundleInternal(new String[] {args.bundlePath, args.defaultPath}, + args.entrypoint, args.libraryPath); } - runFromBundleInternal(args.bundlePath, args.entrypoint, args.libraryPath, args.defaultPath); } /** @@ -92,17 +109,21 @@ public void runFromBundle(FlutterRunArguments args) { @Deprecated public void runFromBundle(String bundlePath, String defaultPath, String entrypoint, boolean reuseRuntimeController) { - runFromBundleInternal(bundlePath, entrypoint, null, defaultPath); + runFromBundleInternal(new String[] {bundlePath, defaultPath}, entrypoint, null); } - private void runFromBundleInternal(String bundlePath, String entrypoint, - String libraryPath, String defaultPath) { + private void runFromBundleInternal(String[] bundlePaths, String entrypoint, + String libraryPath) { assertAttached(); if (applicationIsRunning) throw new AssertionError( "This Flutter engine instance is already running an application"); - nativeRunBundleAndSnapshotFromLibrary(mNativePlatformView, bundlePath, - defaultPath, entrypoint, libraryPath, mContext.getResources().getAssets()); + mFlutterJNI.runBundleAndSnapshotFromLibrary( + bundlePaths, + entrypoint, + libraryPath, + mContext.getResources().getAssets() + ); applicationIsRunning = true; } @@ -112,12 +133,12 @@ public boolean isApplicationRunning() { } public static String getObservatoryUri() { - return nativeGetObservatoryUri(); + return FlutterJNI.nativeGetObservatoryUri(); } @Override public void send(String channel, ByteBuffer message) { - send(channel, message, null); + dartExecutor.send(channel, message); } @Override @@ -127,133 +148,70 @@ public void send(String channel, ByteBuffer message, BinaryReply callback) { return; } - int replyId = 0; - if (callback != null) { - replyId = mNextReplyId++; - mPendingReplies.put(replyId, callback); - } - if (message == null) { - nativeDispatchEmptyPlatformMessage(mNativePlatformView, channel, replyId); - } else { - nativeDispatchPlatformMessage( - mNativePlatformView, channel, message, message.position(), replyId); - } + dartExecutor.send(channel, message, callback); } @Override public void setMessageHandler(String channel, BinaryMessageHandler handler) { - if (handler == null) { - mMessageHandlers.remove(channel); - } else { - mMessageHandlers.put(channel, handler); - } + dartExecutor.setMessageHandler(channel, handler); + } + + /*package*/ FlutterJNI getFlutterJNI() { + return mFlutterJNI; } private void attach(FlutterNativeView view, boolean isBackgroundView) { - mNativePlatformView = nativeAttach(view, isBackgroundView); + mFlutterJNI.attachToNative(isBackgroundView); + dartExecutor.onAttachedToJNI(); } - // Called by native to send us a platform message. - private void handlePlatformMessage(final String channel, byte[] message, final int replyId) { - assertAttached(); - BinaryMessageHandler handler = mMessageHandlers.get(channel); - if (handler != null) { - try { - final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message)); - handler.onMessage(buffer, new BinaryReply() { - private final AtomicBoolean done = new AtomicBoolean(false); - @Override - public void reply(ByteBuffer reply) { - if (!isAttached()) { - Log.d(TAG, - "handlePlatformMessage replying to a detached view, channel=" - + channel); - return; - } - if (done.getAndSet(true)) { - throw new IllegalStateException("Reply already submitted"); - } - if (reply == null) { - nativeInvokePlatformMessageEmptyResponseCallback( - mNativePlatformView, replyId); - } else { - nativeInvokePlatformMessageResponseCallback( - mNativePlatformView, replyId, reply, reply.position()); - } - } - }); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message listener", ex); - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); - } - return; + private final class RenderSurfaceImpl implements RenderSurface { + @Override + public void attachToRenderer(@NonNull FlutterRenderer renderer) { + // Not relevant for v1 embedding. } - nativeInvokePlatformMessageEmptyResponseCallback(mNativePlatformView, replyId); - } - // Called by native to respond to a platform message that we sent. - private void handlePlatformMessageResponse(int replyId, byte[] reply) { - BinaryReply callback = mPendingReplies.remove(replyId); - if (callback != null) { - try { - callback.reply(reply == null ? null : ByteBuffer.wrap(reply)); - } catch (Exception ex) { - Log.e(TAG, "Uncaught exception in binary message reply handler", ex); - } + @Override + public void detachFromRenderer() { + // Not relevant for v1 embedding. } - } - // Called by native to update the semantics/accessibility tree. - private void updateSemantics(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) return; - mFlutterView.updateSemantics(buffer, strings); - } + // Called by native to update the semantics/accessibility tree. + public void updateSemantics(ByteBuffer buffer, String[] strings) { + if (mFlutterView == null) { + return; + } + mFlutterView.updateSemantics(buffer, strings); + } - // Called by native to update the custom accessibility actions. - private void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { - if (mFlutterView == null) - return; - mFlutterView.updateCustomAccessibilityActions(buffer, strings); - } + // Called by native to update the custom accessibility actions. + public void updateCustomAccessibilityActions(ByteBuffer buffer, String[] strings) { + if (mFlutterView == null) { + return; + } + mFlutterView.updateCustomAccessibilityActions(buffer, strings); + } - // Called by native to notify first Flutter frame rendered. - private void onFirstFrame() { - if (mFlutterView == null) return; - mFlutterView.onFirstFrame(); + // Called by native to notify first Flutter frame rendered. + public void onFirstFrameRendered() { + if (mFlutterView == null) { + return; + } + mFlutterView.onFirstFrame(); + } } - // Called by native to notify when the engine is restarted (cold reload). - @SuppressWarnings("unused") - private void onPreEngineRestart() { - if (mPluginRegistry == null) - return; - mPluginRegistry.onPreEngineRestart(); + private final class EngineLifecycleListenerImpl implements EngineLifecycleListener { + // Called by native to notify when the engine is restarted (cold reload). + @SuppressWarnings("unused") + public void onPreEngineRestart() { + if (mFlutterView != null) { + mFlutterView.resetAccessibilityTree(); + } + if (mPluginRegistry == null) { + return; + } + mPluginRegistry.onPreEngineRestart(); + } } - - private static native long nativeAttach(FlutterNativeView view, boolean isBackgroundView); - private static native void nativeDestroy(long nativePlatformViewAndroid); - private static native void nativeDetach(long nativePlatformViewAndroid); - - private static native void nativeRunBundleAndSnapshotFromLibrary( - long nativePlatformViewAndroid, String bundlePath, - String defaultPath, String entrypoint, String libraryUrl, - AssetManager manager); - - private static native String nativeGetObservatoryUri(); - - // Send an empty platform message to Dart. - private static native void nativeDispatchEmptyPlatformMessage( - long nativePlatformViewAndroid, String channel, int responseId); - - // Send a data-carrying platform message to Dart. - private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid, - String channel, ByteBuffer message, int position, int responseId); - - // Send an empty response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageEmptyResponseCallback( - long nativePlatformViewAndroid, int responseId); - - // Send a data-carrying response to a platform message received from Dart. - private static native void nativeInvokePlatformMessageResponseCallback( - long nativePlatformViewAndroid, int responseId, ByteBuffer message, int position); } diff --git a/shell/platform/android/io/flutter/view/FlutterRunArguments.java b/shell/platform/android/io/flutter/view/FlutterRunArguments.java index f322cb8d24db5..9440933031893 100644 --- a/shell/platform/android/io/flutter/view/FlutterRunArguments.java +++ b/shell/platform/android/io/flutter/view/FlutterRunArguments.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +9,7 @@ * the first time. */ public class FlutterRunArguments { + public String[] bundlePaths; public String bundlePath; public String entrypoint; public String libraryPath; diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 8778877486875..7ba52567572ca 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -1,29 +1,24 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.view; +import android.annotation.TargetApi; import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; import android.content.res.Configuration; -import android.database.ContentObserver; -import android.graphics.PixelFormat; -import android.provider.Settings; -import android.net.Uri; -import android.os.Handler; import android.graphics.Bitmap; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Build; +import android.os.Handler; +import android.os.LocaleList; +import android.support.annotation.RequiresApi; import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; -import android.util.TypedValue; import android.view.*; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; @@ -31,13 +26,24 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import io.flutter.app.FlutterPluginRegistry; +import io.flutter.embedding.engine.FlutterJNI; +import io.flutter.embedding.android.AndroidKeyProcessor; +import io.flutter.embedding.android.AndroidTouchProcessor; +import io.flutter.embedding.engine.dart.DartExecutor; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; +import io.flutter.embedding.engine.systemchannels.KeyEventChannel; +import io.flutter.embedding.engine.systemchannels.LifecycleChannel; +import io.flutter.embedding.engine.systemchannels.LocalizationChannel; +import io.flutter.embedding.engine.systemchannels.NavigationChannel; +import io.flutter.embedding.engine.systemchannels.PlatformChannel; +import io.flutter.embedding.engine.systemchannels.SettingsChannel; +import io.flutter.embedding.engine.systemchannels.SystemChannel; import io.flutter.plugin.common.*; import io.flutter.plugin.editing.TextInputPlugin; import io.flutter.plugin.platform.PlatformPlugin; -import org.json.JSONException; -import org.json.JSONObject; +import io.flutter.plugin.platform.PlatformViewsController; -import java.net.URI; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.*; @@ -46,8 +52,7 @@ /** * An Android view containing a Flutter app. */ -public class FlutterView extends SurfaceView - implements BinaryMessenger, TextureRegistry, AccessibilityManager.AccessibilityStateChangeListener { +public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry { /** * Interface for those objects that maintain and expose a reference to a * {@code FlutterView} (such as a full-screen Flutter activity). @@ -84,24 +89,34 @@ static final class ViewportMetrics { int physicalViewInsetLeft = 0; } + private final DartExecutor dartExecutor; + private final FlutterRenderer flutterRenderer; + private final NavigationChannel navigationChannel; + private final KeyEventChannel keyEventChannel; + private final LifecycleChannel lifecycleChannel; + private final LocalizationChannel localizationChannel; + private final PlatformChannel platformChannel; + private final SettingsChannel settingsChannel; + private final SystemChannel systemChannel; private final InputMethodManager mImm; private final TextInputPlugin mTextInputPlugin; + private final AndroidKeyProcessor androidKeyProcessor; + private final AndroidTouchProcessor androidTouchProcessor; + private AccessibilityBridge mAccessibilityNodeProvider; private final SurfaceHolder.Callback mSurfaceCallback; private final ViewportMetrics mMetrics; - private final AccessibilityManager mAccessibilityManager; - private final MethodChannel mFlutterLocalizationChannel; - private final MethodChannel mFlutterNavigationChannel; - private final BasicMessageChannel mFlutterKeyEventChannel; - private final BasicMessageChannel mFlutterLifecycleChannel; - private final BasicMessageChannel mFlutterSystemChannel; - private final BasicMessageChannel mFlutterSettingsChannel; private final List mActivityLifecycleListeners; private final List mFirstFrameListeners; private final AtomicLong nextTextureId = new AtomicLong(0L); private FlutterNativeView mNativeView; - private final AnimationScaleObserver mAnimationScaleObserver; private boolean mIsSoftwareRenderingEnabled = false; // using the software renderer or not - private InputConnection mLastInputConnection; + + private final AccessibilityBridge.OnAccessibilityChangeListener onAccessibilityChangeListener = new AccessibilityBridge.OnAccessibilityChangeListener() { + @Override + public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { + resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled); + } + }; public FlutterView(Context context) { this(context, null); @@ -114,72 +129,67 @@ public FlutterView(Context context, AttributeSet attrs) { public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) { super(context, attrs); - mIsSoftwareRenderingEnabled = nativeGetIsSoftwareRenderingEnabled(); - mAnimationScaleObserver = new AnimationScaleObserver(new Handler()); - mMetrics = new ViewportMetrics(); - mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; - setFocusable(true); - setFocusableInTouchMode(true); - Activity activity = (Activity) getContext(); if (nativeView == null) { mNativeView = new FlutterNativeView(activity.getApplicationContext()); } else { mNativeView = nativeView; } + + dartExecutor = mNativeView.getDartExecutor(); + flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI()); + mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled(); + mMetrics = new ViewportMetrics(); + mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density; + setFocusable(true); + setFocusableInTouchMode(true); + mNativeView.attachViewAndActivity(this, activity); mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { assertAttached(); - nativeSurfaceCreated(mNativeView.get(), holder.getSurface()); + mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface()); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { assertAttached(); - nativeSurfaceChanged(mNativeView.get(), width, height); + mNativeView.getFlutterJNI().onSurfaceChanged(width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { assertAttached(); - nativeSurfaceDestroyed(mNativeView.get()); + mNativeView.getFlutterJNI().onSurfaceDestroyed(); } }; getHolder().addCallback(mSurfaceCallback); - mAccessibilityManager = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); - mActivityLifecycleListeners = new ArrayList<>(); mFirstFrameListeners = new ArrayList<>(); - // Configure the platform plugins and flutter channels. - mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE); - mFlutterNavigationChannel = new MethodChannel(this, "flutter/navigation", JSONMethodCodec.INSTANCE); - mFlutterKeyEventChannel = new BasicMessageChannel<>(this, "flutter/keyevent", JSONMessageCodec.INSTANCE); - mFlutterLifecycleChannel = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE); - mFlutterSystemChannel = new BasicMessageChannel<>(this, "flutter/system", JSONMessageCodec.INSTANCE); - mFlutterSettingsChannel = new BasicMessageChannel<>(this, "flutter/settings", JSONMessageCodec.INSTANCE); - - PlatformPlugin platformPlugin = new PlatformPlugin(activity); - MethodChannel flutterPlatformChannel = new MethodChannel(this, "flutter/platform", JSONMethodCodec.INSTANCE); - flutterPlatformChannel.setMethodCallHandler(platformPlugin); + // Create all platform channels + navigationChannel = new NavigationChannel(dartExecutor); + keyEventChannel = new KeyEventChannel(dartExecutor); + lifecycleChannel = new LifecycleChannel(dartExecutor); + localizationChannel = new LocalizationChannel(dartExecutor); + platformChannel = new PlatformChannel(dartExecutor); + systemChannel = new SystemChannel(dartExecutor); + settingsChannel = new SettingsChannel(dartExecutor); + + // Create and setup plugins + PlatformPlugin platformPlugin = new PlatformPlugin(activity, platformChannel); addActivityLifecycleListener(platformPlugin); mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - mTextInputPlugin = new TextInputPlugin(this); - - setLocale(getResources().getConfiguration().locale); - setUserSettings(); - } + mTextInputPlugin = new TextInputPlugin(this, dartExecutor); + androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel, mTextInputPlugin); + androidTouchProcessor = new AndroidTouchProcessor(flutterRenderer); - private void encodeKeyEvent(KeyEvent event, Map message) { - message.put("flags", event.getFlags()); - message.put("codePoint", event.getUnicodeChar()); - message.put("keyCode", event.getKeyCode()); - message.put("scanCode", event.getScanCode()); - message.put("metaState", event.getMetaState()); + // Send initial platform information to Dart + sendLocalesToDart(getResources().getConfiguration()); + sendUserPlatformSettingsToDart(); } @Override @@ -187,12 +197,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { if (!isAttached()) { return super.onKeyUp(keyCode, event); } - - Map message = new HashMap<>(); - message.put("type", "keyup"); - message.put("keymap", "android"); - encodeKeyEvent(event, message); - mFlutterKeyEventChannel.send(message); + androidKeyProcessor.onKeyUp(event); return super.onKeyUp(keyCode, event); } @@ -201,18 +206,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { if (!isAttached()) { return super.onKeyDown(keyCode, event); } - - if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) { - if (mLastInputConnection != null && mImm.isAcceptingText()) { - mLastInputConnection.sendKeyEvent(event); - } - } - - Map message = new HashMap<>(); - message.put("type", "keydown"); - message.put("keymap", "android"); - encodeKeyEvent(event, message); - mFlutterKeyEventChannel.send(message); + androidKeyProcessor.onKeyDown(event); return super.onKeyDown(keyCode, event); } @@ -237,29 +231,26 @@ public void addActivityLifecycleListener(ActivityLifecycleListener listener) { } public void onStart() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); + lifecycleChannel.appIsInactive(); } public void onPause() { - mFlutterLifecycleChannel.send("AppLifecycleState.inactive"); + lifecycleChannel.appIsInactive(); } public void onPostResume() { - updateAccessibilityFeatures(); for (ActivityLifecycleListener listener : mActivityLifecycleListeners) { listener.onPostResume(); } - mFlutterLifecycleChannel.send("AppLifecycleState.resumed"); + lifecycleChannel.appIsResumed(); } public void onStop() { - mFlutterLifecycleChannel.send("AppLifecycleState.paused"); + lifecycleChannel.appIsPaused(); } public void onMemoryPressure() { - Map message = new HashMap<>(1); - message.put("type", "memoryPressure"); - mFlutterSystemChannel.send(message); + systemChannel.sendMemoryPressureWarning(); } /** @@ -298,33 +289,53 @@ public void disableTransparentBackground() { } public void setInitialRoute(String route) { - mFlutterNavigationChannel.invokeMethod("setInitialRoute", route); + navigationChannel.setInitialRoute(route); } public void pushRoute(String route) { - mFlutterNavigationChannel.invokeMethod("pushRoute", route); + navigationChannel.pushRoute(route); } public void popRoute() { - mFlutterNavigationChannel.invokeMethod("popRoute", null); + navigationChannel.popRoute(); } - private void setUserSettings() { - Map message = new HashMap<>(); - message.put("textScaleFactor", getResources().getConfiguration().fontScale); - message.put("alwaysUse24HourFormat", DateFormat.is24HourFormat(getContext())); - mFlutterSettingsChannel.send(message); + private void sendUserPlatformSettingsToDart() { + // Lookup the current brightness of the Android OS. + boolean isNightModeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + SettingsChannel.PlatformBrightness brightness = isNightModeOn + ? SettingsChannel.PlatformBrightness.dark + : SettingsChannel.PlatformBrightness.light; + + settingsChannel + .startMessage() + .setTextScaleFactor(getResources().getConfiguration().fontScale) + .setUse24HourFormat(DateFormat.is24HourFormat(getContext())) + .setPlatformBrightness(brightness) + .send(); } - private void setLocale(Locale locale) { - mFlutterLocalizationChannel.invokeMethod("setLocale", Arrays.asList(locale.getLanguage(), locale.getCountry(), locale.getScript(), locale.getVariant())); + @SuppressWarnings("deprecation") + private void sendLocalesToDart(Configuration config) { + List locales = new ArrayList<>(); + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + LocaleList localeList = config.getLocales(); + int localeCount = localeList.size(); + for (int index = 0; index < localeCount; ++index) { + Locale locale = localeList.get(index); + locales.add(locale); + } + } else { + locales.add(config.locale); + } + localizationChannel.sendLocales(locales); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - setLocale(newConfig.locale); - setUserSettings(); + sendLocalesToDart(newConfig); + sendUserPlatformSettingsToDart(); } float getDevicePixelRatio() { @@ -335,7 +346,7 @@ public FlutterNativeView detach() { if (!isAttached()) return null; getHolder().removeCallback(mSurfaceCallback); - mNativeView.detach(); + mNativeView.detachFromFlutterView(); FlutterNativeView view = mNativeView; mNativeView = null; @@ -354,132 +365,13 @@ public void destroy() { @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - try { - mLastInputConnection = mTextInputPlugin.createInputConnection(this, outAttrs); - return mLastInputConnection; - } catch (JSONException e) { - Log.e(TAG, "Failed to create input connection", e); - return null; - } - } - - // Must match the PointerChange enum in pointer.dart. - private static final int kPointerChangeCancel = 0; - private static final int kPointerChangeAdd = 1; - private static final int kPointerChangeRemove = 2; - private static final int kPointerChangeHover = 3; - private static final int kPointerChangeDown = 4; - private static final int kPointerChangeMove = 5; - private static final int kPointerChangeUp = 6; - - // Must match the PointerDeviceKind enum in pointer.dart. - private static final int kPointerDeviceKindTouch = 0; - private static final int kPointerDeviceKindMouse = 1; - private static final int kPointerDeviceKindStylus = 2; - private static final int kPointerDeviceKindInvertedStylus = 3; - private static final int kPointerDeviceKindUnknown = 4; - - private int getPointerChangeForAction(int maskedAction) { - // Primary pointer: - if (maskedAction == MotionEvent.ACTION_DOWN) { - return kPointerChangeDown; - } - if (maskedAction == MotionEvent.ACTION_UP) { - return kPointerChangeUp; - } - // Secondary pointer: - if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) { - return kPointerChangeDown; - } - if (maskedAction == MotionEvent.ACTION_POINTER_UP) { - return kPointerChangeUp; - } - // All pointers: - if (maskedAction == MotionEvent.ACTION_MOVE) { - return kPointerChangeMove; - } - if (maskedAction == MotionEvent.ACTION_CANCEL) { - return kPointerChangeCancel; - } - return -1; - } - - private int getPointerDeviceTypeForToolType(int toolType) { - switch (toolType) { - case MotionEvent.TOOL_TYPE_FINGER: - return kPointerDeviceKindTouch; - case MotionEvent.TOOL_TYPE_STYLUS: - return kPointerDeviceKindStylus; - case MotionEvent.TOOL_TYPE_MOUSE: - return kPointerDeviceKindMouse; - case MotionEvent.TOOL_TYPE_ERASER: - return kPointerDeviceKindInvertedStylus; - default: - // MotionEvent.TOOL_TYPE_UNKNOWN will reach here. - return kPointerDeviceKindUnknown; - } - } - - private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer packet) { - int pointerChange = getPointerChangeForAction(event.getActionMasked()); - if (pointerChange == -1) { - return; - } - - int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex)); - - long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. - - packet.putLong(timeStamp); // time_stamp - packet.putLong(pointerChange); // change - packet.putLong(pointerKind); // kind - packet.putLong(event.getPointerId(pointerIndex)); // device - packet.putDouble(event.getX(pointerIndex)); // physical_x - packet.putDouble(event.getY(pointerIndex)); // physical_y - - if (pointerKind == kPointerDeviceKindMouse) { - packet.putLong(event.getButtonState() & 0x1F); // buttons - } else if (pointerKind == kPointerDeviceKindStylus) { - packet.putLong((event.getButtonState() >> 4) & 0xF); // buttons - } else { - packet.putLong(0); // buttons - } - - packet.putLong(0); // obscured - - // TODO(eseidel): Could get the calibrated range if necessary: - // event.getDevice().getMotionRange(MotionEvent.AXIS_PRESSURE) - packet.putDouble(event.getPressure(pointerIndex)); // pressure - packet.putDouble(0.0); // pressure_min - packet.putDouble(1.0); // pressure_max - - if (pointerKind == kPointerDeviceKindStylus) { - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_DISTANCE, pointerIndex)); // distance - packet.putDouble(0.0); // distance_max - } else { - packet.putDouble(0.0); // distance - packet.putDouble(0.0); // distance_max - } - - packet.putDouble(event.getToolMajor(pointerIndex)); // radius_major - packet.putDouble(event.getToolMinor(pointerIndex)); // radius_minor - - packet.putDouble(0.0); // radius_min - packet.putDouble(0.0); // radius_max - - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation - - if (pointerKind == kPointerDeviceKindStylus) { - packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt - } else { - packet.putDouble(0.0); // tilt - } + return mTextInputPlugin.createInputConnection(this, outAttrs); } @Override public boolean onTouchEvent(MotionEvent event) { if (!isAttached()) { - return false; + return super.onTouchEvent(event); } // TODO(abarth): This version check might not be effective in some @@ -491,42 +383,16 @@ public boolean onTouchEvent(MotionEvent event) { requestUnbufferedDispatch(event); } - // These values must match the unpacking code in hooks.dart. - final int kPointerDataFieldCount = 19; - final int kBytePerField = 8; - - int pointerCount = event.getPointerCount(); - - ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField); - packet.order(ByteOrder.LITTLE_ENDIAN); - - int maskedAction = event.getActionMasked(); - // ACTION_UP, ACTION_POINTER_UP, ACTION_DOWN, and ACTION_POINTER_DOWN - // only apply to a single pointer, other events apply to all pointers. - if (maskedAction == MotionEvent.ACTION_UP || maskedAction == MotionEvent.ACTION_POINTER_UP - || maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN) { - addPointerForIndex(event, event.getActionIndex(), packet); - } else { - // ACTION_MOVE may not actually mean all pointers have moved - // but it's the responsibility of a later part of the system to - // ignore 0-deltas if desired. - for (int p = 0; p < pointerCount; p++) { - addPointerForIndex(event, p, packet); - } - } - - assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; - nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); - return true; + return androidTouchProcessor.onTouchEvent(event); } @Override public boolean onHoverEvent(MotionEvent event) { if (!isAttached()) { - return false; + return super.onHoverEvent(event); } - boolean handled = handleAccessibilityHoverEvent(event); + boolean handled = mAccessibilityNodeProvider.onAccessibilityHoverEvent(event); if (!handled) { // TODO(ianh): Expose hover events to the platform, // implementing ADD, REMOVE, etc. @@ -534,6 +400,19 @@ public boolean onHoverEvent(MotionEvent event) { return handled; } + /** + * Invoked by Android when a generic motion event occurs, e.g., joystick movement, mouse hover, + * track pad touches, scroll wheel movements, etc. + * + * Flutter handles all of its own gesture detection and processing, therefore this + * method forwards all {@link MotionEvent} data from Android to Flutter. + */ + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + boolean handled = isAttached() && androidTouchProcessor.onGenericMotionEvent(event); + return handled ? true : super.onGenericMotionEvent(event); + } + @Override protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { mMetrics.physicalWidth = width; @@ -583,6 +462,8 @@ else if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { // be padded. When the on-screen keyboard is detected, we want to include the full inset // but when the inset is just the hidden nav bar, we want to provide a zero inset so the space // can be used. + @TargetApi(20) + @RequiresApi(20) int calculateBottomKeyboardInset(WindowInsets insets) { int screenHeight = getRootView().getHeight(); // Magic number due to this being a heuristic. This should be replaced, but we have not @@ -601,6 +482,8 @@ int calculateBottomKeyboardInset(WindowInsets insets) { // This callback is not present in API < 20, which means lower API devices will see // the wider than expected padding when the status and navigation bars are hidden. @Override + @TargetApi(20) + @RequiresApi(20) public final WindowInsets onApplyWindowInsets(WindowInsets insets) { boolean statusBarHidden = (SYSTEM_UI_FLAG_FULLSCREEN & getWindowSystemUiVisibility()) != 0; @@ -669,6 +552,12 @@ private void preRun() { resetAccessibilityTree(); } + void resetAccessibilityTree() { + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.reset(); + } + } + private void postRun() { } @@ -718,45 +607,13 @@ public void runFromBundle(String bundlePath, String defaultPath, String entrypoi */ public Bitmap getBitmap() { assertAttached(); - return nativeGetBitmap(mNativeView.get()); + return mNativeView.getFlutterJNI().getBitmap(); } - private static native void nativeSurfaceCreated(long nativePlatformViewAndroid, Surface surface); - - private static native void nativeSurfaceChanged(long nativePlatformViewAndroid, int width, int height); - - private static native void nativeSurfaceDestroyed(long nativePlatformViewAndroid); - - private static native void nativeSetViewportMetrics(long nativePlatformViewAndroid, float devicePixelRatio, - int physicalWidth, int physicalHeight, int physicalPaddingTop, int physicalPaddingRight, - int physicalPaddingBottom, int physicalPaddingLeft, int physicalViewInsetTop, int physicalViewInsetRight, - int physicalViewInsetBottom, int physicalViewInsetLeft); - - private static native Bitmap nativeGetBitmap(long nativePlatformViewAndroid); - - private static native void nativeDispatchPointerDataPacket(long nativePlatformViewAndroid, ByteBuffer buffer, - int position); - - private static native void nativeDispatchSemanticsAction(long nativePlatformViewAndroid, int id, int action, - ByteBuffer args, int argsPosition); - - private static native void nativeSetSemanticsEnabled(long nativePlatformViewAndroid, boolean enabled); - - private static native void nativeSetAccessibilityFeatures(long nativePlatformViewAndroid, int flags); - - private static native boolean nativeGetIsSoftwareRenderingEnabled(); - - private static native void nativeRegisterTexture(long nativePlatformViewAndroid, long textureId, - SurfaceTexture surfaceTexture); - - private static native void nativeMarkTextureFrameAvailable(long nativePlatformViewAndroid, long textureId); - - private static native void nativeUnregisterTexture(long nativePlatformViewAndroid, long textureId); - private void updateViewportMetrics() { if (!isAttached()) return; - nativeSetViewportMetrics(mNativeView.get(), mMetrics.devicePixelRatio, mMetrics.physicalWidth, + mNativeView.getFlutterJNI().setViewportMetrics(mMetrics.devicePixelRatio, mMetrics.physicalWidth, mMetrics.physicalHeight, mMetrics.physicalPaddingTop, mMetrics.physicalPaddingRight, mMetrics.physicalPaddingBottom, mMetrics.physicalPaddingLeft, mMetrics.physicalViewInsetTop, mMetrics.physicalViewInsetRight, mMetrics.physicalViewInsetBottom, mMetrics.physicalViewInsetLeft); @@ -764,6 +621,7 @@ private void updateViewportMetrics() { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); float fps = wm.getDefaultDisplay().getRefreshRate(); VsyncWaiter.refreshPeriodNanos = (long) (1000000000.0 / fps); + VsyncWaiter.refreshRateFPS = fps; } // Called by native to update the semantics/accessibility tree. @@ -798,202 +656,53 @@ public void onFirstFrame() { } } - // ACCESSIBILITY - - private boolean mAccessibilityEnabled = false; - private boolean mTouchExplorationEnabled = false; - private int mAccessibilityFeatureFlags = 0; - private TouchExplorationListener mTouchExplorationListener; - - protected void dispatchSemanticsAction(int id, AccessibilityBridge.Action action) { - dispatchSemanticsAction(id, action, null); - } - - protected void dispatchSemanticsAction(int id, AccessibilityBridge.Action action, Object args) { - if (!isAttached()) - return; - ByteBuffer encodedArgs = null; - int position = 0; - if (args != null) { - encodedArgs = StandardMessageCodec.INSTANCE.encodeMessage(args); - position = encodedArgs.position(); - } - nativeDispatchSemanticsAction(mNativeView.get(), id, action.value, encodedArgs, position); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mAccessibilityEnabled = mAccessibilityManager.isEnabled(); - mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE); - getContext().getContentResolver().registerContentObserver(transitionUri, false, mAnimationScaleObserver); - } - if (mAccessibilityEnabled || mTouchExplorationEnabled) { - ensureAccessibilityEnabled(); - } - if (mTouchExplorationEnabled) { - mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - } - // Apply additional accessibility settings - updateAccessibilityFeatures(); - resetWillNotDraw(); - mAccessibilityManager.addAccessibilityStateChangeListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (mTouchExplorationListener == null) { - mTouchExplorationListener = new TouchExplorationListener(); - } - mAccessibilityManager.addTouchExplorationStateChangeListener(mTouchExplorationListener); - } - } + PlatformViewsController platformViewsController = getPluginRegistry().getPlatformViewsController(); + mAccessibilityNodeProvider = new AccessibilityBridge( + this, + new AccessibilityChannel(dartExecutor, getFlutterNativeView().getFlutterJNI()), + (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE), + getContext().getContentResolver(), + platformViewsController + ); + mAccessibilityNodeProvider.setOnAccessibilityChangeListener(onAccessibilityChangeListener); - private void updateAccessibilityFeatures() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - String transitionAnimationScale = Settings.Global.getString(getContext().getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE); - if (transitionAnimationScale != null && transitionAnimationScale.equals("0")) { - mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; - } else { - mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; - } - } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); + resetWillNotDraw( + mAccessibilityNodeProvider.isAccessibilityEnabled(), + mAccessibilityNodeProvider.isTouchExplorationEnabled() + ); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - getContext().getContentResolver().unregisterContentObserver(mAnimationScaleObserver); - mAccessibilityManager.removeAccessibilityStateChangeListener(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mAccessibilityManager.removeTouchExplorationStateChangeListener(mTouchExplorationListener); - } + + mAccessibilityNodeProvider.release(); + mAccessibilityNodeProvider = null; } - private void resetWillNotDraw() { + // TODO(mattcarroll): Confer with Ian as to why we need this method. Delete if possible, otherwise add comments. + private void resetWillNotDraw(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) { if (!mIsSoftwareRenderingEnabled) { - setWillNotDraw(!(mAccessibilityEnabled || mTouchExplorationEnabled)); + setWillNotDraw(!(isAccessibilityEnabled || isTouchExplorationEnabled)); } else { setWillNotDraw(false); } } - @Override - public void onAccessibilityStateChanged(boolean enabled) { - if (enabled) { - ensureAccessibilityEnabled(); - } else { - mAccessibilityEnabled = false; - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setAccessibilityEnabled(false); - } - nativeSetSemanticsEnabled(mNativeView.get(), false); - } - resetWillNotDraw(); - } - - /// Must match the enum defined in window.dart. - private enum AccessibilityFeature { - ACCESSIBLE_NAVIGATION(1 << 0), - INVERT_COLORS(1 << 1), // NOT SUPPORTED - DISABLE_ANIMATIONS(1 << 2); - - AccessibilityFeature(int value) { - this.value = value; - } - - final int value; - } - - // Listens to the global TRANSITION_ANIMATION_SCALE property and notifies us so - // that we can disable animations in Flutter. - private class AnimationScaleObserver extends ContentObserver { - public AnimationScaleObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - this.onChange(selfChange, null); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - String value = Settings.Global.getString(getContext().getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE); - if (value == "0") { - mAccessibilityFeatureFlags ^= AccessibilityFeature.DISABLE_ANIMATIONS.value; - } else { - mAccessibilityFeatureFlags &= ~AccessibilityFeature.DISABLE_ANIMATIONS.value; - } - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } - } - - class TouchExplorationListener implements AccessibilityManager.TouchExplorationStateChangeListener { - @Override - public void onTouchExplorationStateChanged(boolean enabled) { - if (enabled) { - mTouchExplorationEnabled = true; - ensureAccessibilityEnabled(); - mAccessibilityFeatureFlags ^= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } else { - mTouchExplorationEnabled = false; - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.handleTouchExplorationExit(); - } - mAccessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value; - nativeSetAccessibilityFeatures(mNativeView.get(), mAccessibilityFeatureFlags); - } - resetWillNotDraw(); - } - } - @Override public AccessibilityNodeProvider getAccessibilityNodeProvider() { - if (mAccessibilityEnabled) + if (mAccessibilityNodeProvider != null && mAccessibilityNodeProvider.isAccessibilityEnabled()) { return mAccessibilityNodeProvider; - // TODO(goderbauer): when a11y is off this should return a one-off snapshot of - // the a11y - // tree. - return null; - } - - private AccessibilityBridge mAccessibilityNodeProvider; - - void ensureAccessibilityEnabled() { - if (!isAttached()) - return; - mAccessibilityEnabled = true; - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = new AccessibilityBridge(this); - } - nativeSetSemanticsEnabled(mNativeView.get(), true); - mAccessibilityNodeProvider.setAccessibilityEnabled(true); - } - - void resetAccessibilityTree() { - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.reset(); - } - } - - private boolean handleAccessibilityHoverEvent(MotionEvent event) { - if (!mTouchExplorationEnabled) { - return false; - } - if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { - mAccessibilityNodeProvider.handleTouchExploration(event.getX(), event.getY()); - } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { - mAccessibilityNodeProvider.handleTouchExplorationExit(); } else { - Log.d("flutter", "unexpected accessibility hover event: " + event); - return false; + // TODO(goderbauer): when a11y is off this should return a one-off snapshot of + // the a11y + // tree. + return null; } - return true; } @Override @@ -1029,7 +738,7 @@ public TextureRegistry.SurfaceTextureEntry createSurfaceTexture() { surfaceTexture.detachFromGLContext(); final SurfaceTextureRegistryEntry entry = new SurfaceTextureRegistryEntry(nextTextureId.getAndIncrement(), surfaceTexture); - nativeRegisterTexture(mNativeView.get(), entry.id(), surfaceTexture); + mNativeView.getFlutterJNI().registerTexture(entry.id(), surfaceTexture); return entry; } @@ -1058,13 +767,13 @@ final class SurfaceTextureRegistryEntry implements TextureRegistry.SurfaceTextur private SurfaceTexture.OnFrameAvailableListener onFrameListener = new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture texture) { - if (released) { + if (released || mNativeView == null) { // Even though we make sure to unregister the callback before releasing, as of Android O // SurfaceTexture has a data race when accessing the callback, so the callback may // still be called by a stale reference after released==true and mNativeView==null. return; } - nativeMarkTextureFrameAvailable(mNativeView.get(), SurfaceTextureRegistryEntry.this.id); + mNativeView.getFlutterJNI().markTextureFrameAvailable(SurfaceTextureRegistryEntry.this.id); } }; @@ -1084,11 +793,16 @@ public void release() { return; } released = true; - nativeUnregisterTexture(mNativeView.get(), id); + + // The ordering of the next 3 calls is important: + // First we remove the frame listener, then we release the SurfaceTexture, and only after we unregister + // the texture which actually deletes the GL texture. + // Otherwise onFrameAvailableListener might be called after mNativeView==null // (https://github.com/flutter/flutter/issues/20951). See also the check in onFrameAvailable. surfaceTexture.setOnFrameAvailableListener(null); surfaceTexture.release(); + mNativeView.getFlutterJNI().unregisterTexture(id); } } } diff --git a/shell/platform/android/io/flutter/view/ResourceCleaner.java b/shell/platform/android/io/flutter/view/ResourceCleaner.java index 7378a04d2d719..babc53f6e0dfd 100644 --- a/shell/platform/android/io/flutter/view/ResourceCleaner.java +++ b/shell/platform/android/io/flutter/view/ResourceCleaner.java @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,7 +27,7 @@ private class CleanTask extends AsyncTask { } boolean hasFilesToDelete() { - return mFilesToDelete.length > 0; + return mFilesToDelete != null && mFilesToDelete.length > 0; } @Override diff --git a/shell/platform/android/io/flutter/view/ResourceExtractor.java b/shell/platform/android/io/flutter/view/ResourceExtractor.java index a83d0530c5a6c..2895db56b4cbb 100644 --- a/shell/platform/android/io/flutter/view/ResourceExtractor.java +++ b/shell/platform/android/io/flutter/view/ResourceExtractor.java @@ -1,123 +1,115 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package io.flutter.view; +import static java.util.Arrays.asList; + import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.os.AsyncTask; +import android.os.Build; import android.util.Log; +import io.flutter.util.BSDiff; import io.flutter.util.PathUtils; +import org.json.JSONObject; import java.io.*; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** - * A class to intialize the native code. + * A class to initialize the native code. **/ class ResourceExtractor { private static final String TAG = "ResourceExtractor"; private static final String TIMESTAMP_PREFIX = "res_timestamp-"; + private static final String[] SUPPORTED_ABIS = getSupportedAbis(); + + @SuppressWarnings("deprecation") + static long getVersionCode(PackageInfo packageInfo) { + // Linter needs P (28) hardcoded or else it will fail these lines. + if (Build.VERSION.SDK_INT >= 28) { + return packageInfo.getLongVersionCode(); + } else { + return packageInfo.versionCode; + } + } private class ExtractTask extends AsyncTask { - private static final int BUFFER_SIZE = 16 * 1024; - ExtractTask() { } - private void extractResources() { + @Override + protected Void doInBackground(Void... unused) { final File dataDir = new File(PathUtils.getDataDirectory(mContext)); - final String timestamp = checkTimestamp(dataDir); - if (timestamp != null) { - deleteFiles(); + ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater(); + if (resourceUpdater != null) { + // Protect patch file from being overwritten by downloader while + // it's being extracted since downloading happens asynchronously. + resourceUpdater.getInstallationLock().lock(); } - final AssetManager manager = mContext.getResources().getAssets(); - - byte[] buffer = null; - for (String asset : mResources) { - try { - final File output = new File(dataDir, asset); - - if (output.exists()) { - continue; - } - if (output.getParentFile() != null) { - output.getParentFile().mkdirs(); - } - - try (InputStream is = manager.open(asset)) { - try (OutputStream os = new FileOutputStream(output)) { - if (buffer == null) { - buffer = new byte[BUFFER_SIZE]; + try { + if (resourceUpdater != null) { + File updateFile = resourceUpdater.getDownloadedPatch(); + File activeFile = resourceUpdater.getInstalledPatch(); + + if (updateFile.exists()) { + JSONObject manifest = resourceUpdater.readManifest(updateFile); + if (resourceUpdater.validateManifest(manifest)) { + // Graduate patch file as active for asset manager. + if (activeFile.exists() && !activeFile.delete()) { + Log.w(TAG, "Could not delete file " + activeFile); + return null; } - - int count = 0; - while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { - os.write(buffer, 0, count); + if (!updateFile.renameTo(activeFile)) { + Log.w(TAG, "Could not create file " + activeFile); + return null; } - os.flush(); } } - } catch (FileNotFoundException fnfe) { - continue; - } catch (IOException ioe) { - Log.w(TAG, "Exception unpacking resources: " + ioe.getMessage()); - deleteFiles(); - return; } - } - if (timestamp != null) { - try { - new File(dataDir, timestamp).createNewFile(); - } catch (IOException e) { - Log.w(TAG, "Failed to write resource timestamp"); + final String timestamp = checkTimestamp(dataDir); + if (timestamp == null) { + return null; } - } - } - - private String checkTimestamp(File dataDir) { - PackageManager packageManager = mContext.getPackageManager(); - PackageInfo packageInfo = null; - try { - packageInfo = packageManager.getPackageInfo(mContext.getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - return TIMESTAMP_PREFIX; - } + deleteFiles(); - if (packageInfo == null) { - return TIMESTAMP_PREFIX; - } + if (!extractUpdate(dataDir)) { + return null; + } - String expectedTimestamp = - TIMESTAMP_PREFIX + packageInfo.versionCode + "-" + packageInfo.lastUpdateTime; + if (!extractAPK(dataDir)) { + return null; + } - final String[] existingTimestamps = getExistingTimestamps(dataDir); + if (timestamp != null) { + try { + new File(dataDir, timestamp).createNewFile(); + } catch (IOException e) { + Log.w(TAG, "Failed to write resource timestamp"); + } + } - if (existingTimestamps == null) { return null; - } - - if (existingTimestamps.length != 1 - || !expectedTimestamp.equals(existingTimestamps[0])) { - return expectedTimestamp; - } - return null; - } - - @Override - protected Void doInBackground(Void... unused) { - extractResources(); - return null; + } finally { + if (resourceUpdater != null) { + resourceUpdater.getInstallationLock().unlock(); + } + } } } @@ -127,7 +119,7 @@ protected Void doInBackground(Void... unused) { ResourceExtractor(Context context) { mContext = context; - mResources = new HashSet(); + mResources = new HashSet<>(); } ResourceExtractor addResource(String resource) { @@ -148,15 +140,13 @@ ResourceExtractor start() { } void waitForCompletion() { - assert mExtractTask != null; + if (mExtractTask == null) { + return; + } try { mExtractTask.get(); - } catch (CancellationException e) { - deleteFiles(); - } catch (ExecutionException e2) { - deleteFiles(); - } catch (InterruptedException e3) { + } catch (CancellationException | ExecutionException | InterruptedException e) { deleteFiles(); } } @@ -186,4 +176,247 @@ private void deleteFiles() { new File(dataDir, timestamp).delete(); } } + + /// Returns true if successfully unpacked APK resources, + /// otherwise deletes all resources and returns false. + private boolean extractAPK(File dataDir) { + final AssetManager manager = mContext.getResources().getAssets(); + + for (String asset : mResources) { + try { + final String resource = "assets/" + asset; + final File output = new File(dataDir, asset); + if (output.exists()) { + continue; + } + if (output.getParentFile() != null) { + output.getParentFile().mkdirs(); + } + + try (InputStream is = manager.open(asset); + OutputStream os = new FileOutputStream(output)) { + copy(is, os); + } + + Log.i(TAG, "Extracted baseline resource " + resource); + + } catch (FileNotFoundException fnfe) { + continue; + + } catch (IOException ioe) { + Log.w(TAG, "Exception unpacking resources: " + ioe.getMessage()); + deleteFiles(); + return false; + } + } + + return true; + } + + /// Returns true if successfully unpacked update resources or if there is no update, + /// otherwise deletes all resources and returns false. + private boolean extractUpdate(File dataDir) { + final AssetManager manager = mContext.getResources().getAssets(); + + ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater(); + if (resourceUpdater == null) { + return true; + } + + File updateFile = resourceUpdater.getInstalledPatch(); + if (!updateFile.exists()) { + return true; + } + + JSONObject manifest = resourceUpdater.readManifest(updateFile); + if (!resourceUpdater.validateManifest(manifest)) { + // Obsolete patch file, nothing to install. + return true; + } + + ZipFile zipFile; + try { + zipFile = new ZipFile(updateFile); + + } catch (IOException e) { + Log.w(TAG, "Exception unpacking resources: " + e.getMessage()); + deleteFiles(); + return false; + } + + for (String asset : mResources) { + String resource = null; + ZipEntry entry = null; + if (asset.endsWith(".so")) { + // Replicate library lookup logic. + for (String abi : SUPPORTED_ABIS) { + resource = "lib/" + abi + "/" + asset; + entry = zipFile.getEntry(resource); + if (entry == null) { + entry = zipFile.getEntry(resource + ".bzdiff40"); + if (entry == null) { + continue; + } + } + + // Stop after the first match. + break; + } + } + + if (entry == null) { + resource = "assets/" + asset; + entry = zipFile.getEntry(resource); + if (entry == null) { + entry = zipFile.getEntry(resource + ".bzdiff40"); + if (entry == null) { + continue; + } + } + } + + final File output = new File(dataDir, asset); + if (output.exists()) { + continue; + } + if (output.getParentFile() != null) { + output.getParentFile().mkdirs(); + } + + try { + if (entry.getName().endsWith(".bzdiff40")) { + ByteArrayOutputStream diff = new ByteArrayOutputStream(); + try (InputStream is = zipFile.getInputStream(entry)) { + copy(is, diff); + } + + ByteArrayOutputStream orig = new ByteArrayOutputStream(); + if (asset.endsWith(".so")) { + ZipFile apkFile = new ZipFile(getAPKPath()); + if (apkFile == null) { + throw new IOException("Could not find APK"); + } + + ZipEntry origEntry = apkFile.getEntry(resource); + if (origEntry == null) { + throw new IOException("Could not find APK resource " + resource); + } + + try (InputStream is = apkFile.getInputStream(origEntry)) { + copy(is, orig); + } + + } else { + try (InputStream is = manager.open(asset)) { + copy(is, orig); + } catch (FileNotFoundException e) { + throw new IOException("Could not find APK resource " + resource); + } + } + + try (OutputStream os = new FileOutputStream(output)) { + os.write(BSDiff.bspatch(orig.toByteArray(), diff.toByteArray())); + } + + } else { + try (InputStream is = zipFile.getInputStream(entry); + OutputStream os = new FileOutputStream(output)) { + copy(is, os); + } + } + + Log.i(TAG, "Extracted override resource " + entry.getName()); + + } catch (FileNotFoundException fnfe) { + continue; + + } catch (IOException ioe) { + Log.w(TAG, "Exception unpacking resources: " + ioe.getMessage()); + deleteFiles(); + return false; + } + } + + return true; + } + + // Returns null if extracted resources are found and match the current APK version + // and update version if any, otherwise returns the current APK and update version. + private String checkTimestamp(File dataDir) { + PackageManager packageManager = mContext.getPackageManager(); + PackageInfo packageInfo = null; + + try { + packageInfo = packageManager.getPackageInfo(mContext.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + return TIMESTAMP_PREFIX; + } + + if (packageInfo == null) { + return TIMESTAMP_PREFIX; + } + + String expectedTimestamp = + TIMESTAMP_PREFIX + getVersionCode(packageInfo) + "-" + packageInfo.lastUpdateTime; + + ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater(); + if (resourceUpdater != null) { + File patchFile = resourceUpdater.getInstalledPatch(); + JSONObject manifest = resourceUpdater.readManifest(patchFile); + if (resourceUpdater.validateManifest(manifest)) { + String patchNumber = manifest.optString("patchNumber", null); + if (patchNumber != null) { + expectedTimestamp += "-" + patchNumber + "-" + patchFile.lastModified(); + } else { + expectedTimestamp += "-" + patchFile.lastModified(); + } + } + } + + final String[] existingTimestamps = getExistingTimestamps(dataDir); + + if (existingTimestamps == null) { + Log.i(TAG, "No extracted resources found"); + return expectedTimestamp; + } + + if (existingTimestamps.length == 1) { + Log.i(TAG, "Found extracted resources " + existingTimestamps[0]); + } + + if (existingTimestamps.length != 1 + || !expectedTimestamp.equals(existingTimestamps[0])) { + Log.i(TAG, "Resource version mismatch " + expectedTimestamp); + return expectedTimestamp; + } + + return null; + } + + private static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[16 * 1024]; + for (int i; (i = in.read(buf)) >= 0; ) { + out.write(buf, 0, i); + } + } + + private String getAPKPath() { + try { + return mContext.getPackageManager().getApplicationInfo( + mContext.getPackageName(), 0).publicSourceDir; + } catch (Exception e) { + return null; + } + } + + @SuppressWarnings("deprecation") + private static String[] getSupportedAbis() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return Build.SUPPORTED_ABIS; + } else { + ArrayList cpuAbis = new ArrayList(asList(Build.CPU_ABI, Build.CPU_ABI2)); + cpuAbis.removeAll(asList(null, "")); + return cpuAbis.toArray(new String[0]); + } + } } diff --git a/shell/platform/android/io/flutter/view/ResourcePaths.java b/shell/platform/android/io/flutter/view/ResourcePaths.java index 44bfb4a0a0c43..2e2305e0945a6 100644 --- a/shell/platform/android/io/flutter/view/ResourcePaths.java +++ b/shell/platform/android/io/flutter/view/ResourcePaths.java @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/view/ResourceUpdater.java b/shell/platform/android/io/flutter/view/ResourceUpdater.java new file mode 100644 index 0000000000000..30db64e14b1a8 --- /dev/null +++ b/shell/platform/android/io/flutter/view/ResourceUpdater.java @@ -0,0 +1,374 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.view; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.AssetManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.Math; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Date; +import java.util.Scanner; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.json.JSONException; +import org.json.JSONObject; + +public final class ResourceUpdater { + private static final String TAG = "ResourceUpdater"; + + private static final int BUFFER_SIZE = 16 * 1024; + + // Controls when to check if a new patch is available for download, and start downloading. + // Note that by default the application will not block to wait for the download to finish. + // Patches are downloaded in the background, but the developer can also use [InstallMode] + // to control whether to block on download completion, in order to install patches sooner. + enum DownloadMode { + // Check for and download patch on application restart (but not necessarily apply it). + // This is the default setting which will also check for new patches least frequently. + ON_RESTART, + + // Check for and download patch on application resume (but not necessarily apply it). + // By definition, this setting will check for new patches both on restart and resume. + ON_RESUME + } + + // Controls when to check that a new patch has been downloaded and needs to be applied. + enum InstallMode { + // Wait for next application restart before applying downloaded patch. With this + // setting, the application will not block to wait for patch download to finish. + // The application can be restarted later either by the user, or by the system, + // for any reason, at which point the newly downloaded patch will get applied. + // This is the default setting, and is the least disruptive way to apply patches. + ON_NEXT_RESTART, + + // Apply patch as soon as it's downloaded. This will block to wait for new patch + // download to finish, and will immediately apply it. This setting increases the + // urgency with which patches are installed, but may also affect startup latency. + // For now, this setting is only effective when download happens during restart. + // Patches downloaded during resume will not get installed immediately as that + // requires force restarting the app (which might be implemented in the future). + IMMEDIATE + } + + /// Lock that prevents replacement of the install file by the downloader + /// while this file is being extracted, since these can happen in parallel. + Lock getInstallationLock() { + return installationLock; + } + + // Patch file that's fully installed and is ready to serve assets. + // This file represents the final stage in the installation process. + public File getInstalledPatch() { + return new File(context.getFilesDir().toString() + "/patch.zip"); + } + + // Patch file that's finished downloading and is ready to be installed. + // This is a separate file in order to prevent serving assets from patch + // that failed installing for any reason, such as mismatched APK version. + File getDownloadedPatch() { + return new File(getInstalledPatch().getPath() + ".install"); + } + + private class DownloadTask extends AsyncTask { + @Override + protected Void doInBackground(String... unused) { + try { + URL unresolvedURL = new URL(buildUpdateDownloadURL()); + + // Download to transient file to avoid extracting incomplete download. + File localFile = new File(getInstalledPatch().getPath() + ".download"); + + long startMillis = new Date().getTime(); + Log.i(TAG, "Checking for updates at " + unresolvedURL); + + HttpURLConnection connection = + (HttpURLConnection)unresolvedURL.openConnection(); + + long lastDownloadTime = Math.max( + getDownloadedPatch().lastModified(), + getInstalledPatch().lastModified()); + + if (lastDownloadTime != 0) { + Log.i(TAG, "Active update timestamp " + lastDownloadTime); + connection.setIfModifiedSince(lastDownloadTime); + } + + URL resolvedURL = connection.getURL(); + Log.i(TAG, "Resolved update URL " + resolvedURL); + + int responseCode = connection.getResponseCode(); + Log.i(TAG, "HTTP response code " + responseCode); + + if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) { + Log.i(TAG, "Latest update not found on server"); + return null; + } + + if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) { + Log.i(TAG, "Already have latest update"); + return null; + } + + try (InputStream input = connection.getInputStream()) { + Log.i(TAG, "Downloading update " + unresolvedURL); + try (OutputStream output = new FileOutputStream(localFile)) { + int count; + byte[] data = new byte[1024]; + while ((count = input.read(data)) != -1) { + output.write(data, 0, count); + } + + long totalMillis = new Date().getTime() - startMillis; + Log.i(TAG, "Update downloaded in " + totalMillis / 100 / 10. + "s"); + } + } + + // Wait renaming the file if extraction is in progress. + installationLock.lock(); + + try { + File updateFile = getDownloadedPatch(); + + // Graduate downloaded file as ready for installation. + if (updateFile.exists() && !updateFile.delete()) { + Log.w(TAG, "Could not delete file " + updateFile); + return null; + } + if (!localFile.renameTo(updateFile)) { + Log.w(TAG, "Could not create file " + updateFile); + return null; + } + + return null; + + } finally { + installationLock.unlock(); + } + + } catch (IOException e) { + Log.w(TAG, "Could not download update " + e.getMessage()); + return null; + } + } + } + + private final Context context; + private DownloadTask downloadTask; + private final Lock installationLock = new ReentrantLock(); + + public ResourceUpdater(Context context) { + this.context = context; + } + + private String getAPKVersion() { + try { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); + return packageInfo == null ? null : Long.toString(ResourceExtractor.getVersionCode(packageInfo)); + + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + private String buildUpdateDownloadURL() { + Bundle metaData; + try { + metaData = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA).metaData; + + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + + if (metaData == null || metaData.getString("PatchServerURL") == null) { + return null; + } + + URI uri; + try { + uri = new URI(metaData.getString("PatchServerURL") + "/" + getAPKVersion() + ".zip"); + + } catch (URISyntaxException e) { + Log.w(TAG, "Invalid AndroidManifest.xml PatchServerURL: " + e.getMessage()); + return null; + } + + return uri.normalize().toString(); + } + + DownloadMode getDownloadMode() { + Bundle metaData; + try { + metaData = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA).metaData; + + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + + if (metaData == null) { + return DownloadMode.ON_RESTART; + } + + String patchDownloadMode = metaData.getString("PatchDownloadMode"); + if (patchDownloadMode == null) { + return DownloadMode.ON_RESTART; + } + + try { + return DownloadMode.valueOf(patchDownloadMode); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid PatchDownloadMode " + patchDownloadMode); + return DownloadMode.ON_RESTART; + } + } + + InstallMode getInstallMode() { + Bundle metaData; + try { + metaData = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA).metaData; + + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + + if (metaData == null) { + return InstallMode.ON_NEXT_RESTART; + } + + String patchInstallMode = metaData.getString("PatchInstallMode"); + if (patchInstallMode == null) { + return InstallMode.ON_NEXT_RESTART; + } + + try { + return InstallMode.valueOf(patchInstallMode); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid PatchInstallMode " + patchInstallMode); + return InstallMode.ON_NEXT_RESTART; + } + } + + /// Returns manifest JSON from ZIP file, or null if not found. + public JSONObject readManifest(File updateFile) { + if (!updateFile.exists()) { + return null; + } + + try { + ZipFile zipFile = new ZipFile(updateFile); + ZipEntry entry = zipFile.getEntry("manifest.json"); + if (entry == null) { + Log.w(TAG, "Invalid update file: " + updateFile); + return null; + } + + // Read and parse the entire JSON file as single operation. + Scanner scanner = new Scanner(zipFile.getInputStream(entry)); + return new JSONObject(scanner.useDelimiter("\\A").next()); + + } catch (IOException | JSONException e) { + Log.w(TAG, "Invalid update file: " + e); + return null; + } + } + + /// Returns true if the patch file was indeed built for this APK. + public boolean validateManifest(JSONObject manifest) { + if (manifest == null) { + return false; + } + + String buildNumber = manifest.optString("buildNumber", null); + if (buildNumber == null) { + Log.w(TAG, "Invalid update manifest: missing buildNumber"); + return false; + } + + if (!buildNumber.equals(getAPKVersion())) { + Log.w(TAG, "Outdated update file for build " + getAPKVersion()); + return false; + } + + String baselineChecksum = manifest.optString("baselineChecksum", null); + if (baselineChecksum == null) { + Log.w(TAG, "Invalid update manifest: missing baselineChecksum"); + return false; + } + + CRC32 checksum = new CRC32(); + String[] checksumFiles = { + "isolate_snapshot_data", + "isolate_snapshot_instr", + "flutter_assets/isolate_snapshot_data", + }; + for (String fn : checksumFiles) { + AssetManager manager = context.getResources().getAssets(); + try (InputStream is = manager.open(fn)) { + int count = 0; + byte[] buffer = new byte[BUFFER_SIZE]; + while ((count = is.read(buffer, 0, BUFFER_SIZE)) != -1) { + checksum.update(buffer, 0, count); + } + } catch (IOException e) { + // Skip missing files. + } + } + + if (!baselineChecksum.equals(String.valueOf(checksum.getValue()))) { + Log.w(TAG, "Mismatched update file for APK"); + return false; + } + + return true; + } + + void startUpdateDownloadOnce() { + if (downloadTask != null) { + return; + } + downloadTask = new DownloadTask(); + downloadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + void waitForDownloadCompletion() { + if (downloadTask == null) { + return; + } + try { + downloadTask.get(); + downloadTask = null; + } catch (CancellationException e) { + Log.w(TAG, "Download cancelled: " + e.getMessage()); + return; + } catch (ExecutionException e) { + Log.w(TAG, "Download exception: " + e.getMessage()); + return; + } catch (InterruptedException e) { + Log.w(TAG, "Download interrupted: " + e.getMessage()); + return; + } + } +} diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index 992114dc07318..ed3134bb035dd 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/io/flutter/view/VsyncWaiter.java b/shell/platform/android/io/flutter/view/VsyncWaiter.java index c7de31f860119..cce4a96f868da 100644 --- a/shell/platform/android/io/flutter/view/VsyncWaiter.java +++ b/shell/platform/android/io/flutter/view/VsyncWaiter.java @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,6 +10,10 @@ public class VsyncWaiter { // This estimate will be updated by FlutterView when it is attached to a Display. public static long refreshPeriodNanos = 1000000000 / 60; + // This should also be updated by FlutterView when it is attached to a Display. + // The initial value of 0.0 indicates unkonwn refresh rate. + public static float refreshRateFPS = 0.0f; + public static void asyncWaitForVsync(final long cookie) { Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { @Override diff --git a/shell/platform/android/library_loader.cc b/shell/platform/android/library_loader.cc index 70a2b5af34da2..c625414470340 100644 --- a/shell/platform/android/library_loader.cc +++ b/shell/platform/android/library_loader.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/platform_message_response_android.cc b/shell/platform/android/platform_message_response_android.cc index 3610d8baa0d73..eb88a6a5840bb 100644 --- a/shell/platform/android/platform_message_response_android.cc +++ b/shell/platform/android/platform_message_response_android.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -17,6 +17,8 @@ PlatformMessageResponseAndroid::PlatformMessageResponseAndroid( weak_java_object_(weak_java_object), platform_task_runner_(std::move(platform_task_runner)) {} +PlatformMessageResponseAndroid::~PlatformMessageResponseAndroid() = default; + // |blink::PlatformMessageResponse| void PlatformMessageResponseAndroid::Complete( std::unique_ptr data) { @@ -27,7 +29,7 @@ void PlatformMessageResponseAndroid::Complete( ]() { // We are on the platform thread. Attempt to get the strong reference to // the Java object. - auto env = fml::jni::AttachCurrentThread(); + auto* env = fml::jni::AttachCurrentThread(); auto java_object = weak_java_object.get(env); if (java_object.is_null()) { @@ -57,7 +59,7 @@ void PlatformMessageResponseAndroid::CompleteEmpty() { ]() { // We are on the platform thread. Attempt to get the strong reference to // the Java object. - auto env = fml::jni::AttachCurrentThread(); + auto* env = fml::jni::AttachCurrentThread(); auto java_object = weak_java_object.get(env); if (java_object.is_null()) { diff --git a/shell/platform/android/platform_message_response_android.h b/shell/platform/android/platform_message_response_android.h index fb4c5ac640645..f3d79c1aa4faf 100644 --- a/shell/platform/android/platform_message_response_android.h +++ b/shell/platform/android/platform_message_response_android.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,6 +26,8 @@ class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { fml::jni::JavaObjectWeakGlobalRef weak_java_object, fml::RefPtr platform_task_runner); + ~PlatformMessageResponseAndroid() override; + int response_id_; fml::jni::JavaObjectWeakGlobalRef weak_java_object_; fml::RefPtr platform_task_runner_; diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index ee82b44e51604..4817133054071 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +9,7 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/shell/common/io_manager.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" #include "flutter/shell/platform/android/android_surface_gl.h" #include "flutter/shell/platform/android/platform_message_response_android.h" @@ -44,15 +45,33 @@ void PlatformViewAndroid::NotifyCreated( fml::RefPtr native_window) { if (android_surface_) { InstallFirstFrameCallback(); - android_surface_->SetNativeWindow(native_window); + + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), + [&latch, surface = android_surface_.get(), + native_window = std::move(native_window)]() { + surface->SetNativeWindow(native_window); + latch.Signal(); + }); + latch.Wait(); } + PlatformView::NotifyCreated(); } void PlatformViewAndroid::NotifyDestroyed() { PlatformView::NotifyDestroyed(); + if (android_surface_) { - android_surface_->TeardownOnScreenContext(); + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), + [&latch, surface = android_surface_.get()]() { + surface->TeardownOnScreenContext(); + latch.Signal(); + }); + latch.Wait(); } } @@ -207,7 +226,7 @@ void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, void PlatformViewAndroid::UpdateSemantics( blink::SemanticsNodeUpdates update, blink::CustomAccessibilityActionUpdates actions) { - constexpr size_t kBytesPerNode = 38 * sizeof(int32_t); + constexpr size_t kBytesPerNode = 39 * sizeof(int32_t); constexpr size_t kBytesPerChild = sizeof(int32_t); constexpr size_t kBytesPerAction = 4 * sizeof(int32_t); @@ -243,6 +262,7 @@ void PlatformViewAndroid::UpdateSemantics( buffer_int32[position++] = node.actions; buffer_int32[position++] = node.textSelectionBase; buffer_int32[position++] = node.textSelectionExtent; + buffer_int32[position++] = node.platformViewId; buffer_int32[position++] = node.scrollChildren; buffer_int32[position++] = node.scrollIndex; buffer_float32[position++] = (float)node.scrollPosition; @@ -379,7 +399,8 @@ sk_sp PlatformViewAndroid::CreateResourceContext() const { // the OpenGL surface will be able to make a resource context current. If // this changes, this assumption breaks. Handle the same. resource_context = IOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend); + GrBackend::kOpenGL_GrBackend, + GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } else { FML_DLOG(ERROR) << "Could not make the resource context current."; } diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 0a73fed04d5d6..c162ac5a2e8f3 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index e84e68737b281..b26f5939902f2 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -46,9 +46,9 @@ bool CheckException(JNIEnv* env) { static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; -static fml::jni::ScopedJavaGlobalRef* g_flutter_view_class = nullptr; -static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = - nullptr; + +static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; + static fml::jni::ScopedJavaGlobalRef* g_surface_texture_class = nullptr; // Called By Native @@ -146,11 +146,11 @@ void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { // Called By Java -static jlong Attach(JNIEnv* env, - jclass clazz, - jobject flutterView, - jboolean is_background_view) { - fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterView); +static jlong AttachJNI(JNIEnv* env, + jclass clazz, + jobject flutterJNI, + jboolean is_background_view) { + fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI); auto shell_holder = std::make_unique( FlutterMain::Get().GetSettings(), java_object, is_background_view); if (shell_holder->IsValid()) { @@ -160,11 +160,7 @@ static jlong Attach(JNIEnv* env, } } -static void Detach(JNIEnv* env, jobject jcaller, jlong shell_holder) { - // Nothing to do. -} - -static void Destroy(JNIEnv* env, jobject jcaller, jlong shell_holder) { +static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { delete ANDROID_SHELL_HOLDER; } @@ -236,49 +232,45 @@ std::unique_ptr CreateIsolateConfiguration( static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, jobject jcaller, jlong shell_holder, - jstring jbundlepath, - jstring jdefaultPath, + jobjectArray jbundlepaths, jstring jEntrypoint, jstring jLibraryUrl, jobject jAssetManager) { - auto asset_manager = fml::MakeRefCounted(); + auto asset_manager = std::make_shared(); + for (const auto& bundlepath : + fml::jni::StringArrayToVector(env, jbundlepaths)) { + if (bundlepath.empty()) { + continue; + } - const auto bundlepath = fml::jni::JavaStringToString(env, jbundlepath); - if (bundlepath.size() > 0) { // If we got a bundle path, attempt to use that as a directory asset // bundle or a zip asset bundle. const auto file_ext_index = bundlepath.rfind("."); if (bundlepath.substr(file_ext_index) == ".zip") { - asset_manager->PushBack( - std::make_unique(bundlepath)); + asset_manager->PushBack(std::make_unique( + bundlepath, "assets/flutter_assets")); + } else { asset_manager->PushBack( std::make_unique(fml::OpenDirectory( bundlepath.c_str(), false, fml::FilePermission::kRead))); - } - // Use the last path component of the bundle path to determine the - // directory in the APK assets. - const auto last_slash_index = bundlepath.rfind("/", bundlepath.size()); - if (last_slash_index != std::string::npos) { - auto apk_asset_dir = bundlepath.substr( - last_slash_index + 1, bundlepath.size() - last_slash_index); - - asset_manager->PushBack(std::make_unique( - env, // jni environment - jAssetManager, // asset manager - std::move(apk_asset_dir)) // apk asset dir - ); + // Use the last path component of the bundle path to determine the + // directory in the APK assets. + const auto last_slash_index = bundlepath.rfind("/", bundlepath.size()); + if (last_slash_index != std::string::npos) { + auto apk_asset_dir = bundlepath.substr( + last_slash_index + 1, bundlepath.size() - last_slash_index); + + asset_manager->PushBack(std::make_unique( + env, // jni environment + jAssetManager, // asset manager + std::move(apk_asset_dir)) // apk asset dir + ); + } } } - const auto defaultpath = fml::jni::JavaStringToString(env, jdefaultPath); - if (defaultpath.size() > 0) { - asset_manager->PushBack( - std::make_unique(fml::OpenDirectory( - defaultpath.c_str(), false, fml::FilePermission::kRead))); - } - auto isolate_configuration = CreateIsolateConfiguration(*asset_manager); if (!isolate_configuration) { FML_DLOG(ERROR) @@ -329,19 +321,18 @@ static void SetViewportMetrics(JNIEnv* env, jint physicalViewInsetRight, jint physicalViewInsetBottom, jint physicalViewInsetLeft) { - const blink::ViewportMetrics metrics = { - .device_pixel_ratio = static_cast(devicePixelRatio), - .physical_width = static_cast(physicalWidth), - .physical_height = static_cast(physicalHeight), - .physical_padding_top = static_cast(physicalPaddingTop), - .physical_padding_right = static_cast(physicalPaddingRight), - .physical_padding_bottom = static_cast(physicalPaddingBottom), - .physical_padding_left = static_cast(physicalPaddingLeft), - .physical_view_inset_top = static_cast(physicalViewInsetTop), - .physical_view_inset_right = static_cast(physicalViewInsetRight), - .physical_view_inset_bottom = - static_cast(physicalViewInsetBottom), - .physical_view_inset_left = static_cast(physicalViewInsetLeft), + const blink::ViewportMetrics metrics{ + static_cast(devicePixelRatio), + static_cast(physicalWidth), + static_cast(physicalHeight), + static_cast(physicalPaddingTop), + static_cast(physicalPaddingRight), + static_cast(physicalPaddingBottom), + static_cast(physicalPaddingLeft), + static_cast(physicalViewInsetTop), + static_cast(physicalViewInsetRight), + static_cast(physicalViewInsetBottom), + static_cast(physicalViewInsetLeft), }; ANDROID_SHELL_HOLDER->SetViewportMetrics(metrics); @@ -366,7 +357,7 @@ static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { return nullptr; } - auto pixels_src = static_cast(screenshot.data->data()); + auto* pixels_src = static_cast(screenshot.data->data()); // Our configuration of Skia does not support rendering to the // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. @@ -543,66 +534,26 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, ); } -bool PlatformViewAndroid::Register(JNIEnv* env) { - if (env == nullptr) { - return false; - } - - g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); - if (g_flutter_callback_info_class->is_null()) { - return false; - } - - g_flutter_callback_info_constructor = env->GetMethodID( - g_flutter_callback_info_class->obj(), "", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - if (g_flutter_callback_info_constructor == nullptr) { - return false; - } - - g_flutter_view_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterView")); - if (g_flutter_view_class->is_null()) { - return false; - } - - g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("io/flutter/view/FlutterNativeView")); - if (g_flutter_native_view_class->is_null()) { - return false; - } - - g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( - env, env->FindClass("android/graphics/SurfaceTexture")); - if (g_surface_texture_class->is_null()) { - return false; - } - - static const JNINativeMethod native_view_methods[] = { +bool RegisterApi(JNIEnv* env) { + static const JNINativeMethod flutter_jni_methods[] = { + // Start of methods from FlutterNativeView { .name = "nativeAttach", - .signature = "(Lio/flutter/view/FlutterNativeView;Z)J", - .fnPtr = reinterpret_cast(&shell::Attach), + .signature = "(Lio/flutter/embedding/engine/FlutterJNI;Z)J", + .fnPtr = reinterpret_cast(&shell::AttachJNI), }, { .name = "nativeDestroy", .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), + .fnPtr = reinterpret_cast(&shell::DestroyJNI), }, { .name = "nativeRunBundleAndSnapshotFromLibrary", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;Landroid/content/res/AssetManager;)V", + .signature = "(J[Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Landroid/content/res/AssetManager;)V", .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshotFromLibrary), }, - { - .name = "nativeDetach", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Detach), - }, { .name = "nativeGetObservatoryUri", .signature = "()Ljava/lang/String;", @@ -631,9 +582,13 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .fnPtr = reinterpret_cast( &shell::InvokePlatformMessageEmptyResponseCallback), }, - }; - static const JNINativeMethod view_methods[] = { + // Start of methods from FlutterView + { + .name = "nativeGetBitmap", + .signature = "(J)Landroid/graphics/Bitmap;", + .fnPtr = reinterpret_cast(&shell::GetBitmap), + }, { .name = "nativeSurfaceCreated", .signature = "(JLandroid/view/Surface;)V", @@ -654,11 +609,6 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .signature = "(JFIIIIIIIIII)V", .fnPtr = reinterpret_cast(&shell::SetViewportMetrics), }, - { - .name = "nativeGetBitmap", - .signature = "(J)Landroid/graphics/Bitmap;", - .fnPtr = reinterpret_cast(&shell::GetBitmap), - }, { .name = "nativeDispatchPointerDataPacket", .signature = "(JLjava/nio/ByteBuffer;I)V", @@ -701,74 +651,114 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { }, }; - static const JNINativeMethod callback_info_methods[] = { - { - .name = "nativeLookupCallbackInformation", - .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", - .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), - }, - }; - - if (env->RegisterNatives(g_flutter_native_view_class->obj(), - native_view_methods, - arraysize(native_view_methods)) != 0) { - return false; - } - - if (env->RegisterNatives(g_flutter_view_class->obj(), view_methods, - arraysize(view_methods)) != 0) { - return false; - } - - if (env->RegisterNatives(g_flutter_callback_info_class->obj(), - callback_info_methods, - arraysize(callback_info_methods)) != 0) { + if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, + arraysize(flutter_jni_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI"; return false; } g_handle_platform_message_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); + env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage", + "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; return false; } - g_handle_platform_message_response_method = - env->GetMethodID(g_flutter_native_view_class->obj(), - "handlePlatformMessageResponse", "(I[B)V"); + g_handle_platform_message_response_method = env->GetMethodID( + g_flutter_jni_class->obj(), "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { + FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; return false; } g_update_semantics_method = - env->GetMethodID(g_flutter_native_view_class->obj(), "updateSemantics", + env->GetMethodID(g_flutter_jni_class->obj(), "updateSemantics", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_semantics_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateSemantics method"; return false; } g_update_custom_accessibility_actions_method = env->GetMethodID( - g_flutter_native_view_class->obj(), "updateCustomAccessibilityActions", + g_flutter_jni_class->obj(), "updateCustomAccessibilityActions", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { + FML_LOG(ERROR) + << "Could not locate updateCustomAccessibilityActions method"; return false; } - g_on_first_frame_method = env->GetMethodID(g_flutter_native_view_class->obj(), - "onFirstFrame", "()V"); + g_on_first_frame_method = + env->GetMethodID(g_flutter_jni_class->obj(), "onFirstFrame", "()V"); if (g_on_first_frame_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onFirstFrame method"; return false; } - g_on_engine_restart_method = env->GetMethodID( - g_flutter_native_view_class->obj(), "onPreEngineRestart", "()V"); + g_on_engine_restart_method = + env->GetMethodID(g_flutter_jni_class->obj(), "onPreEngineRestart", "()V"); if (g_on_engine_restart_method == nullptr) { + FML_LOG(ERROR) << "Could not locate onEngineRestart method"; + return false; + } + + return true; +} + +bool PlatformViewAndroid::Register(JNIEnv* env) { + if (env == nullptr) { + FML_LOG(ERROR) << "No JNIEnv provided"; + return false; + } + + g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); + if (g_flutter_callback_info_class->is_null()) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation class"; + return false; + } + + g_flutter_callback_info_constructor = env->GetMethodID( + g_flutter_callback_info_class->obj(), "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + if (g_flutter_callback_info_constructor == nullptr) { + FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation constructor"; + return false; + } + + g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("io/flutter/embedding/engine/FlutterJNI")); + if (g_flutter_jni_class->is_null()) { + FML_LOG(ERROR) << "Failed to find FlutterJNI Class."; + return false; + } + + g_surface_texture_class = new fml::jni::ScopedJavaGlobalRef( + env, env->FindClass("android/graphics/SurfaceTexture")); + if (g_surface_texture_class->is_null()) { + FML_LOG(ERROR) << "Could not locate SurfaceTexture class"; + return false; + } + + static const JNINativeMethod callback_info_methods[] = { + { + .name = "nativeLookupCallbackInformation", + .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", + .fnPtr = reinterpret_cast(&shell::LookupCallbackInformation), + }, + }; + + if (env->RegisterNatives(g_flutter_callback_info_class->obj(), + callback_info_methods, + arraysize(callback_info_methods)) != 0) { + FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterCallbackInfo"; return false; } @@ -776,6 +766,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "attachToGLContext", "(I)V"); if (g_attach_to_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate attachToGlContext method"; return false; } @@ -783,6 +774,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { env->GetMethodID(g_surface_texture_class->obj(), "updateTexImage", "()V"); if (g_update_tex_image_method == nullptr) { + FML_LOG(ERROR) << "Could not locate updateTexImage method"; return false; } @@ -790,6 +782,7 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "getTransformMatrix", "([F)V"); if (g_get_transform_matrix_method == nullptr) { + FML_LOG(ERROR) << "Could not locate getTransformMatrix method"; return false; } @@ -797,10 +790,11 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { g_surface_texture_class->obj(), "detachFromGLContext", "()V"); if (g_detach_from_gl_context_method == nullptr) { + FML_LOG(ERROR) << "Could not locate detachFromGlContext method"; return false; } - return true; + return RegisterApi(env); } } // namespace shell diff --git a/shell/platform/android/platform_view_android_jni.h b/shell/platform/android/platform_view_android_jni.h index 9a4a411a9d303..3423c745fd584 100644 --- a/shell/platform/android/platform_view_android_jni.h +++ b/shell/platform/android/platform_view_android_jni.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index be04abcf1e61f..854d2f21eb062 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,10 +16,6 @@ namespace shell { -static void ConsumePendingCallback(jlong java_baton, - fml::TimePoint frame_start_time, - fml::TimePoint frame_target_time); - static fml::jni::ScopedJavaGlobalRef* g_vsync_waiter_class = nullptr; static jmethodID g_async_wait_for_vsync_method_ = nullptr; @@ -30,8 +26,7 @@ VsyncWaiterAndroid::~VsyncWaiterAndroid() = default; // |shell::VsyncWaiter| void VsyncWaiterAndroid::AwaitVSync() { - std::weak_ptr* weak_this = - new std::weak_ptr(shared_from_this()); + auto* weak_this = new std::weak_ptr(shared_from_this()); jlong java_baton = reinterpret_cast(weak_this); task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { @@ -43,11 +38,25 @@ void VsyncWaiterAndroid::AwaitVSync() { }); } -static void OnNativeVsync(JNIEnv* env, - jclass jcaller, - jlong frameTimeNanos, - jlong frameTargetTimeNanos, - jlong java_baton) { +float VsyncWaiterAndroid::GetDisplayRefreshRate() const { + JNIEnv* env = fml::jni::AttachCurrentThread(); + if (g_vsync_waiter_class == nullptr) { + return kUnknownRefreshRateFPS; + } + jclass clazz = g_vsync_waiter_class->obj(); + if (clazz == nullptr) { + return kUnknownRefreshRateFPS; + } + jfieldID fid = env->GetStaticFieldID(clazz, "refreshRateFPS", "F"); + return env->GetStaticFloatField(clazz, fid); +} + +// static +void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env, + jclass jcaller, + jlong frameTimeNanos, + jlong frameTargetTimeNanos, + jlong java_baton) { auto frame_time = fml::TimePoint::FromEpochDelta( fml::TimeDelta::FromNanoseconds(frameTimeNanos)); auto target_time = fml::TimePoint::FromEpochDelta( @@ -56,6 +65,21 @@ static void OnNativeVsync(JNIEnv* env, ConsumePendingCallback(java_baton, frame_time, target_time); } +// static +void VsyncWaiterAndroid::ConsumePendingCallback( + jlong java_baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time) { + auto* weak_this = reinterpret_cast*>(java_baton); + auto shared_this = weak_this->lock(); + delete weak_this; + + if (shared_this) { + shared_this->FireCallback(frame_start_time, frame_target_time); + } +} + +// static bool VsyncWaiterAndroid::Register(JNIEnv* env) { static const JNINativeMethod methods[] = {{ .name = "nativeOnVsync", @@ -81,16 +105,4 @@ bool VsyncWaiterAndroid::Register(JNIEnv* env) { return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0; } -static void ConsumePendingCallback(jlong java_baton, - fml::TimePoint frame_start_time, - fml::TimePoint frame_target_time) { - auto weak_this = reinterpret_cast*>(java_baton); - auto shared_this = weak_this->lock(); - delete weak_this; - - if (shared_this) { - shared_this->FireCallback(frame_start_time, frame_target_time); - } -} - } // namespace shell diff --git a/shell/platform/android/vsync_waiter_android.h b/shell/platform/android/vsync_waiter_android.h index 6c664059ded28..7f6f926418374 100644 --- a/shell/platform/android/vsync_waiter_android.h +++ b/shell/platform/android/vsync_waiter_android.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,9 +6,10 @@ #define SHELL_PLATFORM_ANDROID_VSYNC_WAITER_ANDROID_H_ #include + #include + #include "flutter/fml/macros.h" -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/vsync_waiter.h" namespace shell { @@ -21,10 +22,22 @@ class VsyncWaiterAndroid final : public VsyncWaiter { ~VsyncWaiterAndroid() override; + float GetDisplayRefreshRate() const override; + private: // |shell::VsyncWaiter| void AwaitVSync() override; + static void OnNativeVsync(JNIEnv* env, + jclass jcaller, + jlong frameTimeNanos, + jlong frameTargetTimeNanos, + jlong java_baton); + + static void ConsumePendingCallback(jlong java_baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time); + FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterAndroid); }; diff --git a/shell/platform/common/cpp/BUILD.gn b/shell/platform/common/cpp/BUILD.gn new file mode 100644 index 0000000000000..265cb37b906ab --- /dev/null +++ b/shell/platform/common/cpp/BUILD.gn @@ -0,0 +1,63 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("desktop_library_implementation") { + defines = [ "FLUTTER_DESKTOP_LIBRARY" ] +} + +_public_headers = [ + "public/flutter_export.h", + "public/flutter_messenger.h", + "public/flutter_plugin_registrar.h", +] + +# Any files that are built by clients (client_wrapper code, library headers for +# implementations using this shared code, etc.) include the public headers +# assuming they are in the include path. This configuration should be added to +# any such code that is also built by GN to make the includes work. +config("relative_flutter_library_headers") { + include_dirs = [ "public" ] +} + +# The headers are a separate source set since the client wrapper is allowed +# to depend on the public headers, but none of the rest of the code. +source_set("common_cpp_library_headers") { + public = _public_headers + + configs += [ ":desktop_library_implementation" ] +} + +source_set("common_cpp") { + public = [ + "incoming_message_dispatcher.h", + "text_input_model.h", + ] + + # TODO: Refactor flutter_glfw.cc to move the implementations corresponding + # to the _public_headers above into this target. + sources = [ + "incoming_message_dispatcher.cc", + "text_input_model.cc", + ] + + configs += [ ":desktop_library_implementation" ] + + deps = [ + ":common_cpp_library_headers", + "$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper", + "$flutter_root/shell/platform/embedder:embedder", + ] + + # TODO: Remove once text input model refactor lands, at which point this code + # won't have a JSON dependency. + defines = [ "USE_RAPID_JSON" ] + deps += [ "//third_party/rapidjson" ] +} + +copy("publish_headers") { + sources = _public_headers + outputs = [ + "$root_out_dir/{{source_file_part}}", + ] +} diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn new file mode 100644 index 0000000000000..1aacc758abea3 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -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("publish.gni") + +_wrapper_includes = [ + "include/flutter/basic_message_channel.h", + "include/flutter/binary_messenger.h", + "include/flutter/engine_method_result.h", + "include/flutter/json_message_codec.h", + "include/flutter/json_method_codec.h", + "include/flutter/json_type.h", + "include/flutter/message_codec.h", + "include/flutter/method_call.h", + "include/flutter/method_channel.h", + "include/flutter/method_codec.h", + "include/flutter/method_result.h", + "include/flutter/plugin_registrar.h", +] + +# TODO: Once the wrapper API is more stable, consolidate to as few files as is +# reasonable (without forcing different kinds of clients to take unnecessary +# code) to simplify use. +_wrapper_sources = [ + "engine_method_result.cc", + "json_message_codec.cc", + "json_method_codec.cc", + "plugin_registrar.cc", +] + +# Client library build for internal use by the shell implementation. +source_set("client_wrapper") { + sources = _wrapper_sources + public = _wrapper_includes + + deps = [ + "$flutter_root/shell/platform/common/cpp:common_cpp_library_headers", + "//third_party/rapidjson", + ] + + defines = [ "USE_RAPID_JSON" ] + + configs += [ + "$flutter_root/shell/platform/common/cpp:desktop_library_implementation", + ] + + public_configs = [ + "$flutter_root/shell/platform/common/cpp:relative_flutter_library_headers", + ] +} + +# Copies the client wrapper code to the output directory. +publish_client_wrapper("publish_wrapper") { + public = _wrapper_includes + sources = _wrapper_sources + [ "README" ] +} + +source_set("client_wrapper_library_stubs") { + sources = [ + "testing/stub_flutter_api.cc", + "testing/stub_flutter_api.h", + ] + + public_deps = [ + "$flutter_root/shell/platform/common/cpp:common_cpp_library_headers", + ] +} + +executable("client_wrapper_unittests") { + testonly = true + + # TODO: Add more unit tests. + sources = [ + "method_call_unittests.cc", + "plugin_registrar_unittests.cc", + ] + + deps = [ + ":client_wrapper", + ":client_wrapper_library_stubs", + "$flutter_root/testing", + + # TODO: Consider refactoring flutter_root/testing so that there's a testing + # target that doesn't require a Dart runtime to be linked in. + "//third_party/dart/runtime:libdart_jit", + ] +} diff --git a/shell/platform/common/cpp/client_wrapper/README b/shell/platform/common/cpp/client_wrapper/README new file mode 100644 index 0000000000000..a7f8764867088 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/README @@ -0,0 +1,9 @@ +This code is intended to be built into plugins and applications to provide +higher-level, C++ abstractions for interacting with the Flutter library. + +Over time, the goal is to move more of this code into the library in a way that +provides a usable ABI (e.g., does not use standard libary in the interfaces). + +Note that this wrapper is still in early stages. Expect significant churn in +both the APIs and the structure of the wrapper (e.g., the exact set of files +that need to be built). diff --git a/shell/platform/common/cpp/client_wrapper/engine_method_result.cc b/shell/platform/common/cpp/client_wrapper/engine_method_result.cc new file mode 100644 index 0000000000000..03e17a82e6bd5 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/engine_method_result.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 "include/flutter/engine_method_result.h" + +#include +#include + +namespace flutter { +namespace internal { + +ReplyManager::ReplyManager(BinaryReply reply_handler) + : reply_handler_(std::move(reply_handler)) { + assert(reply_handler_); +} + +ReplyManager::~ReplyManager() { + if (reply_handler_) { + // Warn, rather than send a not-implemented response, since the engine may + // no longer be valid at this point. + std::cerr + << "Warning: Failed to respond to a message. This is a memory leak." + << std::endl; + } +} + +void ReplyManager::SendResponseData(const std::vector* data) { + if (!reply_handler_) { + std::cerr + << "Error: Only one of Success, Error, or NotImplemented can be " + "called," + << " and it can be called exactly once. Ignoring duplicate result." + << std::endl; + return; + } + + const uint8_t* message = data && !data->empty() ? data->data() : nullptr; + size_t message_size = data ? data->size() : 0; + reply_handler_(message, message_size); + reply_handler_ = nullptr; +} + +} // namespace internal +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h b/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h new file mode 100644 index 0000000000000..a864a36343d4e --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h @@ -0,0 +1,98 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ + +#include +#include + +#include "binary_messenger.h" +#include "message_codec.h" + +namespace flutter { + +// A message reply callback. +// +// Used for submitting a reply back to a Flutter message sender. +template +using MessageReply = std::function; + +// A handler for receiving a message from the Flutter engine. +// +// Implementations must asynchronously call reply exactly once with the reply +// to the message. +template +using MessageHandler = + std::function reply)>; + +// A channel for communicating with the Flutter engine by sending asynchronous +// messages. +template +class BasicMessageChannel { + public: + // Creates an instance that sends and receives method calls on the channel + // named |name|, encoded with |codec| and dispatched via |messenger|. + // + // TODO: Make codec optional once the standard codec is supported (Issue #67). + BasicMessageChannel(BinaryMessenger* messenger, + const std::string& name, + const MessageCodec* codec) + : messenger_(messenger), name_(name), codec_(codec) {} + + ~BasicMessageChannel() = default; + + // Prevent copying. + BasicMessageChannel(BasicMessageChannel const&) = delete; + BasicMessageChannel& operator=(BasicMessageChannel const&) = delete; + + // Sends a message to the Flutter engine on this channel. + void Send(const T& message) { + std::unique_ptr> raw_message = + codec_->EncodeMessage(message); + messenger_->Send(name_, raw_message->data(), raw_message->size()); + } + + // TODO: Add support for a version of Send expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a handler that should be called any time a message is + // received on this channel. + void SetMessageHandler(MessageHandler handler) const { + const auto* codec = codec_; + std::string channel_name = name_; + BinaryMessageHandler binary_handler = [handler, codec, channel_name]( + const uint8_t* binary_message, + const size_t binary_message_size, + BinaryReply binary_reply) { + // Use this channel's codec to decode the message and build a reply + // handler. + std::unique_ptr message = + codec->DecodeMessage(binary_message, binary_message_size); + if (!message) { + std::cerr << "Unable to decode message on channel " << channel_name + << std::endl; + binary_reply(nullptr, 0); + return; + } + + MessageReply unencoded_reply = [binary_reply, + codec](const T& unencoded_response) { + auto binary_response = codec->EncodeMessage(unencoded_response); + binary_reply(binary_response->data(), binary_response->size()); + }; + handler(*message, std::move(unencoded_reply)); + }; + messenger_->SetMessageHandler(name_, std::move(binary_handler)); + } + + private: + BinaryMessenger* messenger_; + std::string name_; + const MessageCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h b/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h new file mode 100644 index 0000000000000..ae91941fedc6c --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ + +#include +#include + +// TODO: Consider adding absl as a dependency and using absl::Span for all of +// the message/message_size pairs. +namespace flutter { + +// A binary message reply callback. +// +// Used for submitting a binary reply back to a Flutter message sender. +typedef std::function + BinaryReply; + +// A message handler callback. +// +// Used for receiving messages from Flutter and providing an asynchronous reply. +typedef std::function< + void(const uint8_t* message, const size_t message_size, BinaryReply reply)> + BinaryMessageHandler; + +// A protocol for a class that handles communication of binary data on named +// channels to and from the Flutter engine. +class BinaryMessenger { + public: + virtual ~BinaryMessenger() = default; + + // Sends a binary message to the Flutter side on the specified channel, + // expecting no reply. + // + // TODO: Consider adding absl as a dependency and using absl::Span. + virtual void Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const = 0; + + // TODO: Add support for a version of Send expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a message handler for incoming binary messages from the Flutter + // side on the specified channel. + // + // Replaces any existing handler. Provide a null handler to unregister the + // existing handler. + virtual void SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h b/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h new file mode 100644 index 0000000000000..9c28a85e3de43 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ + +#include +#include +#include + +#include "binary_messenger.h" +#include "method_codec.h" +#include "method_result.h" + +namespace flutter { + +namespace internal { +// Manages the one-time sending of response data. This is an internal helper +// class for EngineMethodResult, separated out since the implementation doesn't +// vary based on the template type. +class ReplyManager { + public: + ReplyManager(BinaryReply reply_handler_); + ~ReplyManager(); + + // Prevent copying. + ReplyManager(ReplyManager const&) = delete; + ReplyManager& operator=(ReplyManager const&) = delete; + + // Sends the given response data (which must either be nullptr, which + // indicates an unhandled method, or a response serialized with |codec_|) to + // the engine. + void SendResponseData(const std::vector* data); + + private: + BinaryReply reply_handler_; +}; +} // namespace internal + +// Implemention of MethodResult that sends a response to the Flutter engine +// exactly once, encoded using a given codec. +template +class EngineMethodResult : public MethodResult { + public: + // Creates a result object that will send results to |reply_handler|, encoded + // using |codec|. The |codec| pointer must remain valid for as long as this + // object exists. + EngineMethodResult(BinaryReply reply_handler, const MethodCodec* codec) + : reply_manager_( + std::make_unique(std::move(reply_handler))), + codec_(codec) {} + + ~EngineMethodResult() = default; + + protected: + // |flutter::MethodResult| + void SuccessInternal(const T* result) override { + std::unique_ptr> data = + codec_->EncodeSuccessEnvelope(result); + reply_manager_->SendResponseData(data.get()); + } + + // |flutter::MethodResult| + void ErrorInternal(const std::string& error_code, + const std::string& error_message, + const T* error_details) override { + std::unique_ptr> data = + codec_->EncodeErrorEnvelope(error_code, error_message, error_details); + reply_manager_->SendResponseData(data.get()); + } + + // |flutter::MethodResult| + void NotImplementedInternal() override { + reply_manager_->SendResponseData(nullptr); + } + + private: + std::unique_ptr reply_manager_; + + const MethodCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h new file mode 100644 index 0000000000000..ba1aa2ffc3520 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ + +#include "json_type.h" +#include "message_codec.h" + +namespace flutter { + +// A message encoding/decoding mechanism for communications to/from the +// Flutter engine via JSON channels. +class JsonMessageCodec : public MessageCodec { + public: + // Returns the shared instance of the codec. + static const JsonMessageCodec& GetInstance(); + + ~JsonMessageCodec() = default; + + // Prevent copying. + JsonMessageCodec(JsonMessageCodec const&) = delete; + JsonMessageCodec& operator=(JsonMessageCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + JsonMessageCodec() = default; + + // |flutter::MessageCodec| + std::unique_ptr DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const override; + + // |flutter::MessageCodec| + std::unique_ptr> EncodeMessageInternal( + const JsonValueType& message) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h new file mode 100644 index 0000000000000..92af82000ac96 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ + +#include "json_type.h" +#include "method_call.h" +#include "method_codec.h" + +namespace flutter { + +// An implementation of MethodCodec that uses JSON strings as the serialization. +class JsonMethodCodec : public MethodCodec { + public: + // Returns the shared instance of the codec. + static const JsonMethodCodec& GetInstance(); + + ~JsonMethodCodec() = default; + + // Prevent copying. + JsonMethodCodec(JsonMethodCodec const&) = delete; + JsonMethodCodec& operator=(JsonMethodCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + JsonMethodCodec() = default; + + // |flutter::MethodCodec| + std::unique_ptr> DecodeMethodCallInternal( + const uint8_t* message, + const size_t message_size) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeMethodCallInternal( + const MethodCall& method_call) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeSuccessEnvelopeInternal( + const JsonValueType* result) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const JsonValueType* error_details) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h b/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h new file mode 100644 index 0000000000000..476cee7bfb941 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.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_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ + +// By default, the Json codecs use jsoncpp, but a version using RapidJSON is +// implemented as well. To use the latter, set USE_RAPID_JSON. +// +// When writing code using the JSON codec classes, do not use JsonValueType; +// instead use the underlying type for the library you have selected directly. + +#ifdef USE_RAPID_JSON +#include + +// The APIs often pass owning references, which in RapidJSON must include the +// allocator, so the value type for the APIs is Document rather than Value. +using JsonValueType = rapidjson::Document; +#else +#include + +using JsonValueType = Json::Value; +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ \ No newline at end of file diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h new file mode 100644 index 0000000000000..fb012e7a535d4 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ + +#include +#include +#include + +namespace flutter { + +// Translates between a binary message and higher-level method call and +// response/error objects. +template +class MessageCodec { + public: + MessageCodec() = default; + + virtual ~MessageCodec() = default; + + // Prevent copying. + MessageCodec(MessageCodec const&) = delete; + MessageCodec& operator=(MessageCodec const&) = delete; + + // Returns the message encoded in |binary_message|, or nullptr if it cannot be + // decoded by this codec. + // TODO: Consider adding absl as a dependency and using absl::Span. + std::unique_ptr DecodeMessage(const uint8_t* binary_message, + const size_t message_size) const { + return std::move(DecodeMessageInternal(binary_message, message_size)); + } + + // Returns a binary encoding of the given |message|, or nullptr if the + // message cannot be serialized by this codec. + std::unique_ptr> EncodeMessage(const T& message) const { + return std::move(EncodeMessageInternal(message)); + } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeMessageInternal( + const T& message) const = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h new file mode 100644 index 0000000000000..80274e7b0c5b0 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.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_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ + +#include +#include + +namespace flutter { + +// An object encapsulating a method call from Flutter whose arguments are of +// type T. +template +class MethodCall { + public: + // Creates a MethodCall with the given name and arguments. + MethodCall(const std::string& method_name, std::unique_ptr arguments) + : method_name_(method_name), arguments_(std::move(arguments)) {} + + virtual ~MethodCall() = default; + + // Prevent copying. + MethodCall(MethodCall const&) = delete; + MethodCall& operator=(MethodCall const&) = delete; + + // The name of the method being called. + const std::string& method_name() const { return method_name_; } + + // The arguments to the method call, or NULL if there are none. + const T* arguments() const { return arguments_.get(); } + + private: + std::string method_name_; + std::unique_ptr arguments_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h new file mode 100644 index 0000000000000..9e28f9b3812e5 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.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_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ + +#include +#include + +#include "binary_messenger.h" +#include "engine_method_result.h" +#include "method_call.h" +#include "method_codec.h" +#include "method_result.h" + +namespace flutter { + +// A handler for receiving a method call from the Flutter engine. +// +// Implementations must asynchronously call exactly one of the methods on +// |result| to indicate the result of the method call. +template +using MethodCallHandler = + std::function& call, + std::unique_ptr> result)>; + +// A channel for communicating with the Flutter engine using invocation of +// asynchronous methods. +template +class MethodChannel { + public: + // Creates an instance that sends and receives method calls on the channel + // named |name|, encoded with |codec| and dispatched via |messenger|. + // + // TODO: Make codec optional once the standard codec is supported (Issue #67). + MethodChannel(BinaryMessenger* messenger, + const std::string& name, + const MethodCodec* codec) + : messenger_(messenger), name_(name), codec_(codec) {} + + ~MethodChannel() = default; + + // Prevent copying. + MethodChannel(MethodChannel const&) = delete; + MethodChannel& operator=(MethodChannel const&) = delete; + + // Sends a message to the Flutter engine on this channel. + void InvokeMethod(const std::string& method, std::unique_ptr arguments) { + MethodCall method_call(method, std::move(arguments)); + std::unique_ptr> message = + codec_->EncodeMethodCall(method_call); + messenger_->Send(name_, message->data(), message->size()); + } + + // TODO: Add support for a version of InvokeMethod expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a handler that should be called any time a method call is + // received on this channel. + void SetMethodCallHandler(MethodCallHandler handler) const { + const auto* codec = codec_; + std::string channel_name = name_; + BinaryMessageHandler binary_handler = [handler, codec, channel_name]( + const uint8_t* message, + const size_t message_size, + BinaryReply reply) { + // Use this channel's codec to decode the call and build a result handler. + auto result = + std::make_unique>(std::move(reply), codec); + std::unique_ptr> method_call = + codec->DecodeMethodCall(message, message_size); + if (!method_call) { + std::cerr << "Unable to construct method call from message on channel " + << channel_name << std::endl; + result->NotImplemented(); + return; + } + handler(*method_call, std::move(result)); + }; + messenger_->SetMessageHandler(name_, std::move(binary_handler)); + } + + private: + BinaryMessenger* messenger_; + std::string name_; + const MethodCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h new file mode 100644 index 0000000000000..3d45e59c0a8f3 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.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_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ + +#include +#include +#include + +#include "method_call.h" + +namespace flutter { + +// Translates between a binary message and higher-level method call and +// response/error objects. +template +class MethodCodec { + public: + MethodCodec() = default; + + virtual ~MethodCodec() = default; + + // Prevent copying. + MethodCodec(MethodCodec const&) = delete; + MethodCodec& operator=(MethodCodec const&) = delete; + + // Returns the MethodCall encoded in |message|, or nullptr if it cannot be + // decoded. + // TODO: Consider adding absl as a dependency and using absl::Span. + std::unique_ptr> DecodeMethodCall( + const uint8_t* message, + const size_t message_size) const { + return std::move(DecodeMethodCallInternal(message, message_size)); + } + + // Returns a binary encoding of the given |method_call|, or nullptr if the + // method call cannot be serialized by this codec. + std::unique_ptr> EncodeMethodCall( + const MethodCall& method_call) const { + return std::move(EncodeMethodCallInternal(method_call)); + } + + // Returns a binary encoding of |result|. |result| must be a type supported + // by the codec. + std::unique_ptr> EncodeSuccessEnvelope( + const T* result = nullptr) const { + return std::move(EncodeSuccessEnvelopeInternal(result)); + } + + // Returns a binary encoding of |error|. The |error_details| must be a type + // supported by the codec. + std::unique_ptr> EncodeErrorEnvelope( + const std::string& error_code, + const std::string& error_message = "", + const T* error_details = nullptr) const { + return std::move( + EncodeErrorEnvelopeInternal(error_code, error_message, error_details)); + } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> DecodeMethodCallInternal( + const uint8_t* message, + const size_t message_size) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeMethodCallInternal( + const MethodCall& method_call) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeSuccessEnvelopeInternal( + const T* result) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const T* error_details) const = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h new file mode 100644 index 0000000000000..e3bf572996eb4 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ + +#include + +namespace flutter { + +// Encapsulates a result sent back to the Flutter engine in response to a +// MethodCall. Only one method should be called on any given instance. +template +class MethodResult { + public: + MethodResult() = default; + + virtual ~MethodResult() = default; + + // Prevent copying. + MethodResult(MethodResult const&) = delete; + MethodResult& operator=(MethodResult const&) = delete; + + // Sends a success response, indicating that the call completed successfully. + // An optional value can be provided as part of the success message. + void Success(const T* result = nullptr) { SuccessInternal(result); } + + // Sends an error response, indicating that the call was understood but + // handling failed in some way. A string error code must be provided, and in + // addition an optional user-readable error_message and/or details object can + // be included. + void Error(const std::string& error_code, + const std::string& error_message = "", + const T* error_details = nullptr) { + ErrorInternal(error_code, error_message, error_details); + } + + // Sends a not-implemented response, indicating that the method either was not + // recognized, or has not been implemented. + void NotImplemented() { NotImplementedInternal(); } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual void SuccessInternal(const T* result) = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual void ErrorInternal(const std::string& error_code, + const std::string& error_message, + const T* error_details) = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual void NotImplementedInternal() = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h new file mode 100644 index 0000000000000..228a6a311fad0 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ + +#include +#include +#include + +#include + +#include "binary_messenger.h" + +namespace flutter { + +class Plugin; + +// A object managing the registration of a plugin for various events. +// +// Currently this class has very limited functionality, but is expected to +// expand over time to more closely match the functionality of +// the Flutter mobile plugin APIs' plugin registrars. +class PluginRegistrar { + public: + // Creates a new PluginRegistrar. |core_registrar| and the messenger it + // provides must remain valid as long as this object exists. + explicit PluginRegistrar(FlutterDesktopPluginRegistrarRef core_registrar); + + ~PluginRegistrar(); + + // Prevent copying. + PluginRegistrar(PluginRegistrar const&) = delete; + PluginRegistrar& operator=(PluginRegistrar const&) = delete; + + // Returns the messenger to use for creating channels to communicate with the + // Flutter engine. + // + // This pointer will remain valid for the lifetime of this instance. + BinaryMessenger* messenger() { return messenger_.get(); } + + // Takes ownership of |plugin|. + // + // Plugins are not required to call this method if they have other lifetime + // management, but this is a convient place for plugins to be owned to ensure + // that they stay valid for any registered callbacks. + void AddPlugin(std::unique_ptr plugin); + + // Enables input blocking on the given channel name. + // + // If set, then the parent window should disable input callbacks + // while waiting for the handler for messages on that channel to run. + void EnableInputBlockingForChannel(const std::string& channel); + + private: + // Handle for interacting with the C API's registrar. + FlutterDesktopPluginRegistrarRef registrar_; + + std::unique_ptr messenger_; + + // Plugins registered for ownership. + std::set> plugins_; +}; + +// A plugin that can be registered for ownership by a PluginRegistrar. +class Plugin { + public: + virtual ~Plugin() = default; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/client_wrapper/json_message_codec.cc b/shell/platform/common/cpp/client_wrapper/json_message_codec.cc new file mode 100644 index 0000000000000..5610c4762ea83 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/json_message_codec.cc @@ -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. + +#include "include/flutter/json_message_codec.h" + +#include +#include + +#ifdef USE_RAPID_JSON +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +#endif + +namespace flutter { + +// static +const JsonMessageCodec& JsonMessageCodec::GetInstance() { + static JsonMessageCodec sInstance; + return sInstance; +} + +std::unique_ptr> JsonMessageCodec::EncodeMessageInternal( + const JsonValueType& message) const { +#ifdef USE_RAPID_JSON + // TODO: Look into alternate writers that would avoid the buffer copy. + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + message.Accept(writer); + const char* buffer_start = buffer.GetString(); + return std::make_unique>( + buffer_start, buffer_start + buffer.GetSize()); +#else + Json::StreamWriterBuilder writer_builder; + std::string serialization = Json::writeString(writer_builder, message); + return std::make_unique>(serialization.begin(), + serialization.end()); +#endif +} + +std::unique_ptr JsonMessageCodec::DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const { + auto raw_message = reinterpret_cast(binary_message); + auto json_message = std::make_unique(); + std::string parse_errors; + bool parsing_successful = false; +#ifdef USE_RAPID_JSON + rapidjson::ParseResult result = + json_message->Parse(raw_message, message_size); + parsing_successful = result == rapidjson::ParseErrorCode::kParseErrorNone; + if (!parsing_successful) { + parse_errors = rapidjson::GetParseError_En(result.Code()); + } +#else + Json::CharReaderBuilder reader_builder; + std::unique_ptr parser(reader_builder.newCharReader()); + parsing_successful = parser->parse(raw_message, raw_message + message_size, + json_message.get(), &parse_errors); +#endif + if (!parsing_successful) { + std::cerr << "Unable to parse JSON message:" << std::endl + << parse_errors << std::endl; + return nullptr; + } + return json_message; +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/json_method_codec.cc b/shell/platform/common/cpp/client_wrapper/json_method_codec.cc new file mode 100644 index 0000000000000..f576d4ebf994f --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/json_method_codec.cc @@ -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. + +#include "include/flutter/json_method_codec.h" + +#include "include/flutter/json_message_codec.h" + +namespace flutter { + +namespace { +// Keys used in MethodCall encoding. +constexpr char kMessageMethodKey[] = "method"; +constexpr char kMessageArgumentsKey[] = "args"; +} // namespace + +// static +const JsonMethodCodec& JsonMethodCodec::GetInstance() { + static JsonMethodCodec sInstance; + return sInstance; +} + +std::unique_ptr> +JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, + const size_t message_size) const { + std::unique_ptr json_message = + JsonMessageCodec::GetInstance().DecodeMessage(message, message_size); + if (!json_message) { + return nullptr; + } + +#if USE_RAPID_JSON + auto method_name_iter = json_message->FindMember(kMessageMethodKey); + if (method_name_iter == json_message->MemberEnd()) { + return nullptr; + } + if (!method_name_iter->value.IsString()) { + return nullptr; + } + std::string method_name(method_name_iter->value.GetString()); + auto arguments_iter = json_message->FindMember(kMessageArgumentsKey); + std::unique_ptr arguments; + if (arguments_iter != json_message->MemberEnd()) { + // Pull the arguments subtree up to the root of json_message. This is + // destructive to json_message, but the full value is no longer needed, and + // this avoids a subtree copy. + json_message->Swap(arguments_iter->value); + // Swap it into |arguments|. This moves the allocator ownership, so that + // the data won't be deleted when json_message goes out of scope. + arguments = std::make_unique(); + arguments->Swap(*json_message); + } + return std::make_unique>( + method_name, std::move(arguments)); +#else + Json::Value method = (*json_message)[kMessageMethodKey]; + if (method.isNull()) { + return nullptr; + } + return std::make_unique>( + method.asString(), + std::make_unique((*json_message)[kMessageArgumentsKey])); +#endif +} + +std::unique_ptr> JsonMethodCodec::EncodeMethodCallInternal( + const MethodCall& method_call) const { +#if USE_RAPID_JSON + // TODO: Consider revisiting the codec APIs to avoid the need to copy + // everything when doing encoding (e.g., by having a version that takes + // owership of the object to encode, so that it can be moved instead). + rapidjson::Document message(rapidjson::kObjectType); + auto& allocator = message.GetAllocator(); + rapidjson::Value name(method_call.method_name(), allocator); + rapidjson::Value arguments; + if (method_call.arguments()) { + arguments.CopyFrom(*method_call.arguments(), allocator); + } + message.AddMember(kMessageMethodKey, name, allocator); + message.AddMember(kMessageArgumentsKey, arguments, allocator); +#else + Json::Value message(Json::objectValue); + message[kMessageMethodKey] = method_call.method_name(); + const Json::Value* arguments = method_call.arguments(); + message[kMessageArgumentsKey] = arguments ? *arguments : Json::Value(); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(message); +} + +std::unique_ptr> +JsonMethodCodec::EncodeSuccessEnvelopeInternal( + const JsonValueType* result) const { +#if USE_RAPID_JSON + rapidjson::Document envelope; + envelope.SetArray(); + rapidjson::Value result_value; + if (result) { + result_value.CopyFrom(*result, envelope.GetAllocator()); + } + envelope.PushBack(result_value, envelope.GetAllocator()); +#else + Json::Value envelope(Json::arrayValue); + envelope.append(result == nullptr ? Json::Value() : *result); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(envelope); +} + +std::unique_ptr> +JsonMethodCodec::EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const JsonValueType* error_details) const { +#if USE_RAPID_JSON + rapidjson::Document envelope(rapidjson::kArrayType); + auto& allocator = envelope.GetAllocator(); + envelope.PushBack(rapidjson::Value(error_code, allocator), allocator); + envelope.PushBack(rapidjson::Value(error_message, allocator), allocator); + rapidjson::Value details_value; + if (error_details) { + details_value.CopyFrom(*error_details, allocator); + } + envelope.PushBack(details_value, allocator); +#else + Json::Value envelope(Json::arrayValue); + envelope.append(error_code); + envelope.append(error_message.empty() ? Json::Value() : error_message); + envelope.append(error_details == nullptr ? Json::Value() : *error_details); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(envelope); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc b/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc new file mode 100644 index 0000000000000..a3d093d8c8bae --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc @@ -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. + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace flutter { + +TEST(MethodCallTest, Basic) { + const std::string method_name("method_name"); + const int argument = 42; + MethodCall method_call(method_name, std::make_unique(argument)); + EXPECT_EQ(method_call.method_name(), method_name); + ASSERT_NE(method_call.arguments(), nullptr); + EXPECT_EQ(*method_call.arguments(), 42); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc new file mode 100644 index 0000000000000..46c117377b22e --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -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. + +#include "include/flutter/plugin_registrar.h" + +#include "include/flutter/engine_method_result.h" +#include "include/flutter/method_channel.h" + +#include +#include + +namespace flutter { + +namespace { + +// Passes |message| to |user_data|, which must be a BinaryMessageHandler, along +// with a BinaryReply that will send a response on |message|'s response handle. +// +// This serves as an adaptor between the function-pointer-based message callback +// interface provided by the C API and the std::function-based message handler +// interface of BinaryMessenger. +void ForwardToHandler(FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessage* message, + void* user_data) { + auto* response_handle = message->response_handle; + BinaryReply reply_handler = [messenger, response_handle]( + const uint8_t* reply, + const size_t reply_size) mutable { + if (!response_handle) { + std::cerr << "Error: Response can be set only once. Ignoring " + "duplicate response." + << std::endl; + return; + } + FlutterDesktopMessengerSendResponse(messenger, response_handle, reply, + reply_size); + // The engine frees the response handle once + // FlutterDesktopSendMessageResponse is called. + response_handle = nullptr; + }; + + const BinaryMessageHandler& message_handler = + *static_cast(user_data); + + message_handler(message->message, message->message_size, + std::move(reply_handler)); +} + +} // namespace + +// Wrapper around a FlutterDesktopMessengerRef that implements the +// BinaryMessenger API. +class BinaryMessengerImpl : public BinaryMessenger { + public: + explicit BinaryMessengerImpl(FlutterDesktopMessengerRef core_messenger) + : messenger_(core_messenger) {} + + virtual ~BinaryMessengerImpl() = default; + + // Prevent copying. + BinaryMessengerImpl(BinaryMessengerImpl const&) = delete; + BinaryMessengerImpl& operator=(BinaryMessengerImpl const&) = delete; + + // |flutter::BinaryMessenger| + void Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const override; + + // |flutter::BinaryMessenger| + void SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) override; + + private: + // Handle for interacting with the C API. + FlutterDesktopMessengerRef messenger_; + + // A map from channel names to the BinaryMessageHandler that should be called + // for incoming messages on that channel. + std::map handlers_; +}; + +void BinaryMessengerImpl::Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const { + FlutterDesktopMessengerSend(messenger_, channel.c_str(), message, + message_size); +} + +void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) { + if (!handler) { + handlers_.erase(channel); + FlutterDesktopMessengerSetCallback(messenger_, channel.c_str(), nullptr, + nullptr); + return; + } + // Save the handler, to keep it alive. + handlers_[channel] = std::move(handler); + BinaryMessageHandler* message_handler = &handlers_[channel]; + // Set an adaptor callback that will invoke the handler. + FlutterDesktopMessengerSetCallback(messenger_, channel.c_str(), + ForwardToHandler, message_handler); +} + +// PluginRegistrar: + +PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) + : registrar_(registrar) { + auto core_messenger = FlutterDesktopRegistrarGetMessenger(registrar_); + messenger_ = std::make_unique(core_messenger); +} + +PluginRegistrar::~PluginRegistrar() {} + +void PluginRegistrar::AddPlugin(std::unique_ptr plugin) { + plugins_.insert(std::move(plugin)); +} + +void PluginRegistrar::EnableInputBlockingForChannel( + const std::string& channel) { + FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str()); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc new file mode 100644 index 0000000000000..c4e5de5b46b50 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc @@ -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. + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h" + +#include +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h" +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Stub implementation to validate calls to the API. +class TestApi : public testing::StubFlutterApi { + public: + // |flutter::testing::StubFlutterApi| + void MessengerSend(const char* channel, + const uint8_t* message, + const size_t message_size) override { + last_data_sent_ = message; + } + + const uint8_t* last_data_sent() { return last_data_sent_; } + + private: + const uint8_t* last_data_sent_ = nullptr; +}; + +} // namespace + +// Tests that the registrar returns a messenger that calls through to the C API. +TEST(MethodCallTest, MessengerSend) { + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrar registrar(dummy_registrar_handle); + BinaryMessenger* messenger = registrar.messenger(); + + std::vector message = {1, 2, 3, 4}; + messenger->Send("some_channel", &message[0], message.size()); + EXPECT_EQ(test_api->last_data_sent(), &message[0]); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/publish.gni b/shell/platform/common/cpp/client_wrapper/publish.gni new file mode 100644 index 0000000000000..5483ab56a6a7c --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/publish.gni @@ -0,0 +1,52 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Publishes client wrapper files to the output directory for distribution. +# This can be used multiple times to combine various portions of a wrapper +# library into one cohesive library for clients to consume. +# +# Files that should be built by clients go into 'sources', while headers meant +# to be included by the consuming code go into 'public'. +# +# All public code is assumed to be in the 'flutter' namespace. +template("publish_client_wrapper") { + template_target_name = target_name + publish_dir_root = "$root_out_dir/cpp_client_wrapper" + namespace = "flutter" + + group(template_target_name) { + deps = [ + ":${template_target_name}_publish_includes", + ":${template_target_name}_publish_sources", + ] + } + + copy("${template_target_name}_publish_includes") { + visibility = [ + ":$template_target_name", + ":${template_target_name}_publish_sources", + ] + + sources = invoker.public + outputs = [ + "$publish_dir_root/include/$namespace/{{source_file_part}}", + ] + } + + copy("${template_target_name}_publish_sources") { + visibility = [ ":$template_target_name" ] + + sources = invoker.sources + outputs = [ + "$publish_dir_root/{{source_file_part}}", + ] + + # GN on Windows appears to do #include checks even for copy + # targets, so add the dependency to the headers to satisfy + # the check. + deps = [ + ":${template_target_name}_publish_includes", + ] + } +} diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc new file mode 100644 index 0000000000000..55f4100b72efb --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc @@ -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. + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h" + +static flutter::testing::StubFlutterApi* s_stub_implementation; + +namespace flutter { +namespace testing { + +// static +void StubFlutterApi::SetTestStub(StubFlutterApi* stub) { + s_stub_implementation = stub; +} + +// static +StubFlutterApi* StubFlutterApi::GetTestStub() { + return s_stub_implementation; +} + +ScopedStubFlutterApi::ScopedStubFlutterApi(std::unique_ptr stub) + : stub_(std::move(stub)) { + previous_stub_ = StubFlutterApi::GetTestStub(); + StubFlutterApi::SetTestStub(stub_.get()); +} + +ScopedStubFlutterApi::~ScopedStubFlutterApi() { + StubFlutterApi::SetTestStub(previous_stub_); +} + +} // namespace testing +} // namespace flutter + +// Forwarding dummy implementations of the C API. + +FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( + FlutterDesktopPluginRegistrarRef registrar) { + // The stub ignores this, so just return an arbitrary non-zero value. + return reinterpret_cast(1); +} + +void FlutterDesktopRegistrarEnableInputBlocking( + FlutterDesktopPluginRegistrarRef registrar, + const char* channel) { + if (s_stub_implementation) { + s_stub_implementation->RegistrarEnableInputBlocking(channel); + } +} + +void FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger, + const char* channel, + const uint8_t* message, + const size_t message_size) { + if (s_stub_implementation) { + s_stub_implementation->MessengerSend(channel, message, message_size); + } +} + +void FlutterDesktopMessengerSendResponse( + FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length) { + if (s_stub_implementation) { + s_stub_implementation->MessengerSendResponse(handle, data, data_length); + } +} + +void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger, + const char* channel, + FlutterDesktopMessageCallback callback, + void* user_data) { + if (s_stub_implementation) { + s_stub_implementation->MessengerSetCallback(channel, callback, user_data); + } +} diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h new file mode 100644 index 0000000000000..64491405cb805 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_STUB_FLUTTER_API_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_STUB_FLUTTER_API_H_ + +#include + +#include "flutter/shell/platform/common/cpp/public/flutter_messenger.h" +#include "flutter/shell/platform/common/cpp/public/flutter_plugin_registrar.h" + +namespace flutter { +namespace testing { + +// Base class for a object that provides test implementations of the APIs in +// the headers in platform/common/cpp/public/. + +// Linking this class into a test binary will provide dummy forwarding +// implementantions of that C API, so that the wrapper can be tested separately +// from the actual library. +class StubFlutterApi { + public: + // Sets |stub| as the instance to which calls to the Flutter library C APIs + // will be forwarded. + static void SetTestStub(StubFlutterApi* stub); + + // Returns the current stub, as last set by SetTestFluttterStub. + static StubFlutterApi* GetTestStub(); + + virtual ~StubFlutterApi() {} + + // Called for FlutterDesktopRegistrarEnableInputBlocking. + virtual void RegistrarEnableInputBlocking(const char* channel) {} + + // Called for FlutterDesktopMessengerSend. + virtual void MessengerSend(const char* channel, + const uint8_t* message, + const size_t message_size) {} + + // Called for FlutterDesktopMessengerSendResponse. + virtual void MessengerSendResponse( + const FlutterDesktopMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length) {} + + // Called for FlutterDesktopMessengerSetCallback. + virtual void MessengerSetCallback(const char* channel, + FlutterDesktopMessageCallback callback, + void* user_data) {} +}; + +// A test helper that owns a stub implementation, making it the test stub for +// the lifetime of the object, then restoring the previous value. +class ScopedStubFlutterApi { + public: + // Calls SetTestFlutterStub with |stub|. + ScopedStubFlutterApi(std::unique_ptr stub); + + // Restores the previous test stub. + ~ScopedStubFlutterApi(); + + StubFlutterApi* stub() { return stub_.get(); } + + private: + std::unique_ptr stub_; + // The previous stub. + StubFlutterApi* previous_stub_; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_TESTING_STUB_FLUTTER_API_H_ diff --git a/shell/platform/common/cpp/incoming_message_dispatcher.cc b/shell/platform/common/cpp/incoming_message_dispatcher.cc new file mode 100644 index 0000000000000..f766f7738135a --- /dev/null +++ b/shell/platform/common/cpp/incoming_message_dispatcher.cc @@ -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. + +#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h" + +namespace shell { + +IncomingMessageDispatcher::IncomingMessageDispatcher( + FlutterDesktopMessengerRef messenger) + : messenger_(messenger) {} + +IncomingMessageDispatcher::~IncomingMessageDispatcher() = default; + +void IncomingMessageDispatcher::HandleMessage( + const FlutterDesktopMessage& message, + std::function input_block_cb, + std::function input_unblock_cb) { + std::string channel(message.channel); + + // Find the handler for the channel; if there isn't one, report the failure. + if (callbacks_.find(channel) == callbacks_.end()) { + FlutterDesktopMessengerSendResponse(messenger_, message.response_handle, + nullptr, 0); + return; + } + auto& callback_info = callbacks_[channel]; + FlutterDesktopMessageCallback message_callback = callback_info.first; + + // Process the call, handling input blocking if requested. + bool block_input = input_blocking_channels_.count(channel) > 0; + if (block_input) { + input_block_cb(); + } + message_callback(messenger_, &message, callback_info.second); + if (block_input) { + input_unblock_cb(); + } +} + +void IncomingMessageDispatcher::SetMessageCallback( + const std::string& channel, + FlutterDesktopMessageCallback callback, + void* user_data) { + if (!callback) { + callbacks_.erase(channel); + return; + } + callbacks_[channel] = std::make_pair(callback, user_data); +} + +void IncomingMessageDispatcher::EnableInputBlockingForChannel( + const std::string& channel) { + input_blocking_channels_.insert(channel); +} + +} // namespace shell diff --git a/shell/platform/common/cpp/incoming_message_dispatcher.h b/shell/platform/common/cpp/incoming_message_dispatcher.h new file mode 100644 index 0000000000000..fced7db7a8e24 --- /dev/null +++ b/shell/platform/common/cpp/incoming_message_dispatcher.h @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter 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_CPP_INCOMING_MESSAGE_DISPATCHER_H_ +#define FLUTTER_SHELL_PLATFORM_CPP_INCOMING_MESSAGE_DISPATCHER_H_ + +#include +#include +#include +#include +#include + +#include "flutter/shell/platform/common/cpp/public/flutter_messenger.h" + +namespace shell { + +// Manages per-channel registration of callbacks for handling messages from the +// Flutter engine, and dispatching incoming messages to those handlers. +class IncomingMessageDispatcher { + public: + // Creates a new IncomingMessageDispatcher. |messenger| must remain valid as + // long as this object exists. + explicit IncomingMessageDispatcher(FlutterDesktopMessengerRef messenger); + + virtual ~IncomingMessageDispatcher(); + + // Prevent copying. + IncomingMessageDispatcher(IncomingMessageDispatcher const&) = delete; + IncomingMessageDispatcher& operator=(IncomingMessageDispatcher const&) = + delete; + + // Routes |message| to to the registered handler for its channel, if any. + // + // If input blocking has been enabled on that channel, wraps the call to the + // handler with calls to the given callbacks to block and then unblock input. + // + // If no handler is registered for the message's channel, sends a + // NotImplemented response to the engine. + void HandleMessage( + const FlutterDesktopMessage& message, + std::function input_block_cb = [] {}, + std::function input_unblock_cb = [] {}); + + // Registers a message callback for incoming messages from the Flutter + // side on the specified channel. |callback| will be called with the message + // and |user_data| any time a message arrives on that channel. + // + // Replaces any existing callback. Pass a null callback to unregister the + // existing callback. + void SetMessageCallback(const std::string& channel, + FlutterDesktopMessageCallback callback, + void* user_data); + + // Enables input blocking on the given channel name. + // + // If set, then the parent window should disable input callbacks + // while waiting for the handler for messages on that channel to run. + void EnableInputBlockingForChannel(const std::string& channel); + + private: + // Handle for interacting with the C messaging API. + FlutterDesktopMessengerRef messenger_; + + // A map from channel names to the FlutterDesktopMessageCallback that should + // be called for incoming messages on that channel, along with the void* user + // data to pass to it. + std::map> + callbacks_; + + // Channel names for which input blocking should be enabled during the call to + // that channel's handler. + std::set input_blocking_channels_; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_CPP_INCOMING_MESSAGE_DISPATCHER_H_ diff --git a/shell/platform/common/cpp/public/flutter_export.h b/shell/platform/common/cpp/public/flutter_export.h new file mode 100644 index 0000000000000..2f380915f9510 --- /dev/null +++ b/shell/platform/common/cpp/public/flutter_export.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_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ + +#ifdef FLUTTER_DESKTOP_LIBRARY +// Add visibility/export annotations when building the library. + +#ifdef _WIN32 +#define FLUTTER_EXPORT __declspec(dllexport) +#else +#define FLUTTER_EXPORT __attribute__((visibility("default"))) +#endif + +#else // FLUTTER_DESKTOP_LIBRARY + +// Add import annotations when consuming the library. +#ifdef _WIN32 +#define FLUTTER_EXPORT __declspec(dllimport) +#else +#define FLUTTER_EXPORT +#endif + +#endif // FLUTTER_DESKTOP_LIBRARY + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ \ No newline at end of file diff --git a/shell/platform/common/cpp/public/flutter_messenger.h b/shell/platform/common/cpp/public/flutter_messenger.h new file mode 100644 index 0000000000000..ff9f43437708d --- /dev/null +++ b/shell/platform/common/cpp/public/flutter_messenger.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ + +#include +#include + +#include "flutter_export.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a Flutter engine messenger. +typedef struct FlutterDesktopMessenger* FlutterDesktopMessengerRef; + +// Opaque handle for tracking responses to messages. +typedef struct _FlutterPlatformMessageResponseHandle + FlutterDesktopMessageResponseHandle; + +// A message received from Flutter. +typedef struct { + // Size of this struct as created by Flutter. + size_t struct_size; + // The name of the channel used for this message. + const char* channel; + // The raw message data. + const uint8_t* message; + // The length of |message|. + size_t message_size; + // The response handle. If non-null, the receiver of this message must call + // FlutterDesktopSendMessageResponse exactly once with this handle. + const FlutterDesktopMessageResponseHandle* response_handle; +} FlutterDesktopMessage; + +// Function pointer type for message handler callback registration. +// +// The user data will be whatever was passed to FlutterDesktopSetMessageHandler +// for the channel the message is received on. +typedef void (*FlutterDesktopMessageCallback)( + FlutterDesktopMessengerRef /* messenger */, + const FlutterDesktopMessage* /* message*/, + void* /* user data */); + +// Sends a binary message to the Flutter side on the specified channel. +FLUTTER_EXPORT void FlutterDesktopMessengerSend( + FlutterDesktopMessengerRef messenger, + const char* channel, + const uint8_t* message, + const size_t message_size); + +// Sends a reply to a FlutterDesktopMessage for the given response handle. +// +// Once this has been called, |handle| is invalid and must not be used again. +FLUTTER_EXPORT void FlutterDesktopMessengerSendResponse( + FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length); + +// Registers a callback function for incoming binary messages from the Flutter +// side on the specified channel. +// +// Replaces any existing callback. Provide a null handler to unregister the +// existing callback. +// +// If |user_data| is provided, it will be passed in |callback| calls. +FLUTTER_EXPORT void FlutterDesktopMessengerSetCallback( + FlutterDesktopMessengerRef messenger, + const char* channel, + FlutterDesktopMessageCallback callback, + void* user_data); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ diff --git a/shell/platform/common/cpp/public/flutter_plugin_registrar.h b/shell/platform/common/cpp/public/flutter_plugin_registrar.h new file mode 100644 index 0000000000000..1caa3cee1f9e4 --- /dev/null +++ b/shell/platform/common/cpp/public/flutter_plugin_registrar.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ + +#include +#include + +#include "flutter_export.h" +#include "flutter_messenger.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a plugin registrar. +typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef; + +// Returns the engine messenger associated with this registrar. +FLUTTER_EXPORT FlutterDesktopMessengerRef +FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar); + +// Enables input blocking on the given channel. +// +// If set, then the Flutter window will disable input callbacks +// while waiting for the handler for messages on that channel to run. This is +// useful if handling the message involves showing a modal window, for instance. +// +// This must be called after FlutterDesktopSetMessageHandler, as setting a +// handler on a channel will reset the input blocking state back to the +// default of disabled. +FLUTTER_EXPORT void FlutterDesktopRegistrarEnableInputBlocking( + FlutterDesktopPluginRegistrarRef registrar, + const char* channel); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/text_input_model.cc b/shell/platform/common/cpp/text_input_model.cc new file mode 100644 index 0000000000000..2f1c9b3741f1f --- /dev/null +++ b/shell/platform/common/cpp/text_input_model.cc @@ -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. + +#include "flutter/shell/platform/common/cpp/text_input_model.h" + +#include + +// TODO(awdavies): Need to fix this regarding issue #47. +static constexpr char kComposingBaseKey[] = "composingBase"; + +static constexpr char kComposingExtentKey[] = "composingExtent"; + +static constexpr char kSelectionAffinityKey[] = "selectionAffinity"; +static constexpr char kAffinityDownstream[] = "TextAffinity.downstream"; + +static constexpr char kSelectionBaseKey[] = "selectionBase"; +static constexpr char kSelectionExtentKey[] = "selectionExtent"; + +static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional"; + +static constexpr char kTextKey[] = "text"; + +// Input client configuration keys. +static constexpr char kTextInputAction[] = "inputAction"; +static constexpr char kTextInputType[] = "inputType"; +static constexpr char kTextInputTypeName[] = "name"; + +namespace shell { + +TextInputModel::TextInputModel(int client_id, const rapidjson::Value& config) + : text_(""), + client_id_(client_id), + selection_base_(text_.begin()), + selection_extent_(text_.begin()) { + // TODO: Improve error handling during refactoring; this is just minimal + // checking to avoid asserts since RapidJSON is stricter than jsoncpp. + if (config.IsObject()) { + auto input_action = config.FindMember(kTextInputAction); + if (input_action != config.MemberEnd() && input_action->value.IsString()) { + input_action_ = input_action->value.GetString(); + } + auto input_type_info = config.FindMember(kTextInputType); + if (input_type_info != config.MemberEnd() && + input_type_info->value.IsObject()) { + auto input_type = input_type_info->value.FindMember(kTextInputTypeName); + if (input_type != input_type_info->value.MemberEnd() && + input_type->value.IsString()) { + input_type_ = input_type->value.GetString(); + } + } + } +} + +TextInputModel::~TextInputModel() = default; + +bool TextInputModel::SetEditingState(size_t selection_base, + size_t selection_extent, + const std::string& text) { + if (selection_base > selection_extent) { + return false; + } + // Only checks extent since it is implicitly greater-than-or-equal-to base. + if (selection_extent > text.size()) { + return false; + } + text_ = std::string(text); + selection_base_ = text_.begin() + selection_base; + selection_extent_ = text_.begin() + selection_extent; + return true; +} + +void TextInputModel::DeleteSelected() { + selection_base_ = text_.erase(selection_base_, selection_extent_); + // Moves extent back to base, so that it is a single cursor placement again. + selection_extent_ = selection_base_; +} + +void TextInputModel::AddCharacter(char c) { + if (selection_base_ != selection_extent_) { + DeleteSelected(); + } + selection_extent_ = text_.insert(selection_extent_, c); + selection_extent_++; + selection_base_ = selection_extent_; +} + +bool TextInputModel::Backspace() { + if (selection_base_ != selection_extent_) { + DeleteSelected(); + return true; + } + if (selection_base_ != text_.begin()) { + selection_base_ = text_.erase(selection_base_ - 1, selection_base_); + selection_extent_ = selection_base_; + return true; + } + return false; // No edits happened. +} + +bool TextInputModel::Delete() { + if (selection_base_ != selection_extent_) { + DeleteSelected(); + return true; + } + if (selection_base_ != text_.end()) { + selection_base_ = text_.erase(selection_base_, selection_base_ + 1); + selection_extent_ = selection_base_; + return true; + } + return false; +} + +void TextInputModel::MoveCursorToBeginning() { + selection_base_ = text_.begin(); + selection_extent_ = text_.begin(); +} + +void TextInputModel::MoveCursorToEnd() { + selection_base_ = text_.end(); + selection_extent_ = text_.end(); +} + +bool TextInputModel::MoveCursorForward() { + // If about to move set to the end of the highlight (when not selecting). + if (selection_base_ != selection_extent_) { + selection_base_ = selection_extent_; + return true; + } + // If not at the end, move the extent forward. + if (selection_extent_ != text_.end()) { + selection_extent_++; + selection_base_++; + return true; + } + return false; +} + +bool TextInputModel::MoveCursorBack() { + // If about to move set to the beginning of the highlight + // (when not selecting). + if (selection_base_ != selection_extent_) { + selection_extent_ = selection_base_; + return true; + } + // If not at the start, move the beginning backward. + if (selection_base_ != text_.begin()) { + selection_base_--; + selection_extent_--; + return true; + } + return false; +} + +std::unique_ptr TextInputModel::GetState() const { + // TODO(stuartmorgan): Move client_id out up to the plugin so that this + // function just returns the editing state. + auto args = std::make_unique(rapidjson::kArrayType); + auto& allocator = args->GetAllocator(); + args->PushBack(client_id_, allocator); + + rapidjson::Value editing_state(rapidjson::kObjectType); + // TODO(awdavies): Most of these are hard-coded for now. + editing_state.AddMember(kComposingBaseKey, -1, allocator); + editing_state.AddMember(kComposingExtentKey, -1, allocator); + editing_state.AddMember(kSelectionAffinityKey, kAffinityDownstream, + allocator); + editing_state.AddMember(kSelectionBaseKey, + static_cast(selection_base_ - text_.begin()), + allocator); + editing_state.AddMember(kSelectionExtentKey, + static_cast(selection_extent_ - text_.begin()), + allocator); + editing_state.AddMember(kSelectionIsDirectionalKey, false, allocator); + editing_state.AddMember(kTextKey, rapidjson::Value(text_, allocator).Move(), + allocator); + args->PushBack(editing_state, allocator); + return args; +} + +} // namespace shell diff --git a/shell/platform/common/cpp/text_input_model.h b/shell/platform/common/cpp/text_input_model.h new file mode 100644 index 0000000000000..3ae567c50c463 --- /dev/null +++ b/shell/platform/common/cpp/text_input_model.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_CPP_TEXT_INPUT_MODEL_H_ +#define FLUTTER_SHELL_PLATFORM_CPP_TEXT_INPUT_MODEL_H_ + +#include + +#include "rapidjson/document.h" + +namespace shell { +// Handles underlying text input state, using a simple ASCII model. +// +// Ignores special states like "insert mode" for now. +class TextInputModel { + public: + TextInputModel(int client_id, const rapidjson::Value& config); + virtual ~TextInputModel(); + + // Attempts to set the text state. + // + // Returns false if the state is not valid (base or extent are out of + // bounds, or base is less than extent). + bool SetEditingState(size_t selection_base, + size_t selection_extent, + const std::string& text); + + // Adds a character. + // + // Either appends after the cursor (when selection base and extent are the + // same), or deletes the selected characters, replacing the text with the + // character specified. + void AddCharacter(char c); + + // Deletes either the selection, or one character ahead of the cursor. + // + // Deleting one character ahead of the cursor occurs when the selection base + // and extent are the same. + // + // Returns true if any deletion actually occurred. + bool Delete(); + + // Deletes either the selection, or one character behind the cursor. + // + // Deleting one character behind the cursor occurs when the selection base + // and extent are the same. + // + // Returns true if any deletion actually occurred. + bool Backspace(); + + // Attempts to move the cursor backward. + // + // Returns true if the cursor could be moved. Changes base and extent to be + // equal to either the extent (if extent is at the end of the string), or + // for extent to be equal to + bool MoveCursorBack(); + + // Attempts to move the cursor forward. + // + // Returns true if the cursor could be moved. + bool MoveCursorForward(); + + // Attempts to move the cursor to the beginning. + // + // Returns true if the cursor could be moved. + void MoveCursorToBeginning(); + + // Attempts to move the cursor to the back. + // + // Returns true if the cursor could be moved. + void MoveCursorToEnd(); + + // Returns the state in the form of a platform message. + std::unique_ptr GetState() const; + + // Id of the text input client. + int client_id() const { return client_id_; } + + // Keyboard type of the client. See available options: + // https://docs.flutter.io/flutter/services/TextInputType-class.html + std::string input_type() const { return input_type_; } + + // An action requested by the user on the input client. See available options: + // https://docs.flutter.io/flutter/services/TextInputAction-class.html + std::string input_action() const { return input_action_; } + + private: + void DeleteSelected(); + + std::string text_; + int client_id_; + std::string input_type_; + std::string input_action_; + std::string::iterator selection_base_; + std::string::iterator selection_extent_; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_CPP_TEXT_INPUT_MODEL_H_ diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index 3c04dda634945..672ea4537eda3 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -1,15 +1,22 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. assert(is_mac || is_ios) +import("framework_shared.gni") + group("darwin") { if (is_ios) { deps = [ "ios:flutter_framework", ] } + if (is_mac) { + deps = [ + "macos:flutter_framework", + ] + } } source_set("flutter_channels") { @@ -40,6 +47,24 @@ source_set("flutter_channels") { public_configs = [ "$flutter_root:config" ] } +# Framework code shared between iOS and macOS. +source_set("framework_shared") { + set_sources_assignment_filter([]) + sources = [ + "ios/framework/Source/FlutterChannels.mm", + "ios/framework/Source/FlutterCodecs.mm", + "ios/framework/Source/FlutterStandardCodec.mm", + "ios/framework/Source/FlutterStandardCodec_Internal.h", + ] + + public = framework_shared_headers + set_sources_assignment_filter(sources_assignment_filter) + + defines = [ "FLUTTER_FRAMEWORK" ] + + public_configs = [ "$flutter_root:config" ] +} + executable("flutter_channels_unittests") { testonly = true @@ -55,4 +80,6 @@ executable("flutter_channels_unittests") { "$flutter_root/testing", "//third_party/dart/runtime:libdart_jit", ] + + public_configs = [ "$flutter_root:config" ] } diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 38df59c502d75..8dd3387b3f871 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/shell/platform/darwin/common/buffer_conversions.h b/shell/platform/darwin/common/buffer_conversions.h index 7b48a0c8b913d..a5b166d2e3c08 100644 --- a/shell/platform/darwin/common/buffer_conversions.h +++ b/shell/platform/darwin/common/buffer_conversions.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/common/buffer_conversions.mm b/shell/platform/darwin/common/buffer_conversions.mm index f7238f104db7d..237910b2cfbb4 100644 --- a/shell/platform/darwin/common/buffer_conversions.mm +++ b/shell/platform/darwin/common/buffer_conversions.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/common/command_line.h b/shell/platform/darwin/common/command_line.h index 63add3f4285a0..fab7ff0aa21bf 100644 --- a/shell/platform/darwin/common/command_line.h +++ b/shell/platform/darwin/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/common/command_line.mm b/shell/platform/darwin/common/command_line.mm index 18e37f2b4f798..84ab3ca432c77 100644 --- a/shell/platform/darwin/common/command_line.mm +++ b/shell/platform/darwin/common/command_line.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/framework_shared.gni b/shell/platform/darwin/framework_shared.gni new file mode 100644 index 0000000000000..7ca4b2d068017 --- /dev/null +++ b/shell/platform/darwin/framework_shared.gni @@ -0,0 +1,14 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +framework_shared_headers = get_path_info( + [ + # TODO: Move these files, and their implementations, to a shared + # location. + "ios/framework/Headers/FlutterMacros.h", + "ios/framework/Headers/FlutterBinaryMessenger.h", + "ios/framework/Headers/FlutterChannels.h", + "ios/framework/Headers/FlutterCodecs.h", + ], + "abspath") diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index f340f73e0eb6a..8adbfc627970e 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -26,9 +26,10 @@ _flutter_framework_headers = [ "framework/Headers/FlutterChannels.h", "framework/Headers/FlutterCodecs.h", "framework/Headers/FlutterDartProject.h", + "framework/Headers/FlutterEngine.h", "framework/Headers/FlutterHeadlessDartRunner.h", "framework/Headers/FlutterMacros.h", - "framework/Headers/FlutterNavigationController.h", + "framework/Headers/FlutterPlatformViews.h", "framework/Headers/FlutterPlugin.h", "framework/Headers/FlutterPluginAppLifeCycleDelegate.h", "framework/Headers/FlutterTexture.h", @@ -51,12 +52,18 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterCodecs.mm", "framework/Source/FlutterDartProject.mm", "framework/Source/FlutterDartProject_Internal.h", + "framework/Source/FlutterEngine.mm", + "framework/Source/FlutterEngine_Internal.h", "framework/Source/FlutterHeadlessDartRunner.mm", - "framework/Source/FlutterNavigationController.mm", "framework/Source/FlutterObservatoryPublisher.h", "framework/Source/FlutterObservatoryPublisher.mm", + "framework/Source/FlutterOverlayView.h", + "framework/Source/FlutterOverlayView.mm", "framework/Source/FlutterPlatformPlugin.h", "framework/Source/FlutterPlatformPlugin.mm", + "framework/Source/FlutterPlatformViews_Internal.h", + "framework/Source/FlutterPlatformViews_Internal.mm", + "framework/Source/FlutterPlatformViews.mm", "framework/Source/FlutterPluginAppLifeCycleDelegate.mm", "framework/Source/FlutterStandardCodec.mm", "framework/Source/FlutterStandardCodec_Internal.h", @@ -77,12 +84,12 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/platform_message_router.mm", "framework/Source/vsync_waiter_ios.h", "framework/Source/vsync_waiter_ios.mm", - "headless_platform_view_ios.h", - "headless_platform_view_ios.mm", "ios_external_texture_gl.h", "ios_external_texture_gl.mm", "ios_gl_context.h", "ios_gl_context.mm", + "ios_gl_render_target.h", + "ios_gl_render_target.mm", "ios_surface.h", "ios_surface.mm", "ios_surface_gl.h", @@ -105,7 +112,6 @@ shared_library("create_flutter_framework_dylib") { "$flutter_root/shell/common", "$flutter_root/shell/platform/darwin/common", "//third_party/skia", - "//third_party/skia:gpu", ] if (flutter_runtime_mode == "debug" || flutter_runtime_mode == "dynamic_profile" || diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index 49464e40dc06b..9135c8200603c 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,6 +8,16 @@ /** BREAKING CHANGES: + December 17, 2018: + - Changed designated initializer on FlutterEngine + + October 5, 2018: + - Removed FlutterNavigationController.h/.mm + - Changed return signature of `FlutterDartHeadlessCodeRunner.run*` from void + to bool + - Removed HeadlessPlatformViewIOS + - Marked FlutterDartHeadlessCodeRunner deprecated + August 31, 2018: Marked -[FlutterDartProject initFromDefaultSourceForConfiguration] and FlutterStandardBigInteger as unavailable. @@ -47,9 +57,10 @@ #include "FlutterChannels.h" #include "FlutterCodecs.h" #include "FlutterDartProject.h" +#include "FlutterEngine.h" #include "FlutterHeadlessDartRunner.h" #include "FlutterMacros.h" -#include "FlutterNavigationController.h" +#include "FlutterPlatformViews.h" #include "FlutterPlugin.h" #include "FlutterPluginAppLifeCycleDelegate.h" #include "FlutterTexture.h" diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h b/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h index f175ea2925826..8684a22ea7733 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,11 +11,11 @@ #include "FlutterPlugin.h" /** - * UIApplicationDelegate subclass for simple apps that want default behavior. + * `UIApplicationDelegate` subclass for simple apps that want default behavior. * - * This class provides the following behaviors: + * This class implements the following behaviors: * * Status bar touches are forwarded to the key window's root view - * FlutterViewController, in order to trigger scroll to top. + * `FlutterViewController`, in order to trigger scroll to top. * * Keeps the Flutter connection open in debug mode when the phone screen * locks. * @@ -24,7 +24,8 @@ * code as necessary from FlutterAppDelegate.mm. */ FLUTTER_EXPORT -@interface FlutterAppDelegate : UIResponder +@interface FlutterAppDelegate + : UIResponder @property(strong, nonatomic) UIWindow* window; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h b/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h index 606c131349cc4..07815c6494411 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,72 +11,70 @@ NS_ASSUME_NONNULL_BEGIN /** - A message reply callback. - - Used for submitting a binary reply back to a Flutter message sender. Also used - in the dual capacity for handling a binary message reply received from Flutter. - - - Parameters: - - reply: The reply. + * A message reply callback. + * + * Used for submitting a binary reply back to a Flutter message sender. Also used + * in for handling a binary message reply received from Flutter. + * + * @param reply The reply. */ typedef void (^FlutterBinaryReply)(NSData* _Nullable reply); /** - A strategy for handling incoming binary messages from Flutter and to send - asynchronous replies back to Flutter. - - - Parameters: - - message: The message. - - reply: A callback for submitting a reply to the sender. + * A strategy for handling incoming binary messages from Flutter and to send + * asynchronous replies back to Flutter. + * + * @param message The message. + * @param reply A callback for submitting an asynchronous reply to the sender. */ typedef void (^FlutterBinaryMessageHandler)(NSData* _Nullable message, FlutterBinaryReply reply); /** - A facility for communicating with the Flutter side using asynchronous message - passing with binary messages. - - - SeeAlso: - - `FlutterBasicMessageChannel`, which supports communication using structured - messages. - - `FlutterMethodChannel`, which supports communication using asynchronous - method calls. - - `FlutterEventChannel`, which supports commuication using event streams. + * A facility for communicating with the Flutter side using asynchronous message + * passing with binary messages. + * + * Implementated by: + * - `FlutterBasicMessageChannel`, which supports communication using structured + * messages. + * - `FlutterMethodChannel`, which supports communication using asynchronous + * method calls. + * - `FlutterEventChannel`, which supports commuication using event streams. */ FLUTTER_EXPORT -@protocol FlutterBinaryMessenger +@protocol FlutterBinaryMessenger /** - Sends a binary message to the Flutter side on the specified channel, expecting - no reply. - - - Parameters: - - channel: The channel name. - - message: The message. + * Sends a binary message to the Flutter side on the specified channel, expecting + * no reply. + * + * @param channel The channel name. + * @param message The message. */ - (void)sendOnChannel:(NSString*)channel message:(NSData* _Nullable)message; /** - Sends a binary message to the Flutter side on the specified channel, expecting - an asynchronous reply. - - - Parameters: - - channel: The channel name. - - message: The message. - - callback: A callback for receiving a reply. + * Sends a binary message to the Flutter side on the specified channel, expecting + * an asynchronous reply. + * + * @param channel The channel name. + * @param message The message. + * @param callback A callback for receiving a reply. */ - (void)sendOnChannel:(NSString*)channel message:(NSData* _Nullable)message - binaryReply:(FlutterBinaryReply _Nullable)callback; + binaryReply:(FlutterBinaryReply _Nullable)callback + // TODO: Add macOS support for replies once + // https://github.com/flutter/flutter/issues/18852 is fixed. + API_UNAVAILABLE(macos); /** - Registers a message handler for incoming binary messages from the Flutter side - on the specified channel. - - Replaces any existing handler. Use a `nil` handler for unregistering the - existing handler. - - - Parameters: - - channel: The channel name. - - handler: The message handler. + * Registers a message handler for incoming binary messages from the Flutter side + * on the specified channel. + * + * Replaces any existing handler. Use a `nil` handler for unregistering the + * existing handler. + * + * @param channel The channel name. + * @param handler The message handler. */ - (void)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h b/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h index 7150838fafbb3..8849dca01e6d9 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,26 +9,43 @@ #include "FlutterMacros.h" +/** + * An object containing the result of `FlutterCallbackCache`'s `lookupCallbackInformation` + * method. + */ FLUTTER_EXPORT @interface FlutterCallbackInformation : NSObject +/** + * The name of the callback. + */ @property(retain) NSString* callbackName; +/** + * The class name of the callback. + */ @property(retain) NSString* callbackClassName; +/** + * The library path of the callback. + */ @property(retain) NSString* callbackLibraryPath; @end +/** + * The cache containing callback information for spawning a + * `FlutterHeadlessDartRunner`. + */ FLUTTER_EXPORT @interface FlutterCallbackCache : NSObject /** - Returns the callback information for the given callback handle. - This callback information can be used when spawning a - FlutterHeadlessDartRunner. - - - Parameter handle: The handle for a callback, provided by the - Dart method `PluginUtilities.getCallbackHandle`. - - Returns: A FlutterCallbackInformation object which contains the name of the - callback, the name of the class in which the callback is defined, and the - path of the library which contains the callback. If the provided handle is - invalid, nil is returned. + * Returns the callback information for the given callback handle. + * This callback information can be used when spawning a + * `FlutterHeadlessDartRunner`. + * + * @param handle The handle for a callback, provided by the + * Dart method `PluginUtilities.getCallbackHandle`. + * @return A `FlutterCallbackInformation` object which contains the name of the + * callback, the name of the class in which the callback is defined, and the + * path of the library which contains the callback. If the provided handle is + * invalid, nil is returned. */ + (FlutterCallbackInformation*)lookupCallbackInformation:(int64_t)handle; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h b/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h index 2e6cf4a4fd825..9245d79a8ec6c 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,373 +10,366 @@ NS_ASSUME_NONNULL_BEGIN /** - A message reply callback. - - Used for submitting a reply back to a Flutter message sender. Also used in - the dual capacity for handling a message reply received from Flutter. - - - Parameter reply: The reply. + * A message reply callback. + * + * Used for submitting a reply back to a Flutter message sender. Also used in + * the dual capacity for handling a message reply received from Flutter. + * + * @param reply The reply. */ typedef void (^FlutterReply)(id _Nullable reply); /** - A strategy for handling incoming messages from Flutter and to send - asynchronous replies back to Flutter. - - - Parameters: - - message: The message. - - reply: A callback for submitting a reply to the sender. + * A strategy for handling incoming messages from Flutter and to send + * asynchronous replies back to Flutter. + * + * @param message The message. + * @param callback A callback for submitting a reply to the sender. */ typedef void (^FlutterMessageHandler)(id _Nullable message, FlutterReply callback); /** - A channel for communicating with the Flutter side using basic, asynchronous - message passing. + * A channel for communicating with the Flutter side using basic, asynchronous + * message passing. */ FLUTTER_EXPORT @interface FlutterBasicMessageChannel : NSObject /** - Creates a `FlutterBasicMessageChannel` with the specified name and binary - messenger. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - The channel uses `FlutterStandardMessageCodec` to encode and decode messages. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. + * Creates a `FlutterBasicMessageChannel` with the specified name and binary + * messenger. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * The channel uses `FlutterStandardMessageCodec` to encode and decode messages. + * + * @param name The channel name. + * @param messenger The binary messenger. */ + (instancetype)messageChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger; /** - Creates a `FlutterBasicMessageChannel` with the specified name, binary - messenger, - and message codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The message codec. + * Creates a `FlutterBasicMessageChannel` with the specified name, binary + * messenger, and message codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The message codec. */ + (instancetype)messageChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; /** - Initializes a `FlutterBasicMessageChannel` with the specified name, binary - messenger, and message codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The message codec. + * Initializes a `FlutterBasicMessageChannel` with the specified name, binary + * messenger, and message codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The message codec. */ - (instancetype)initWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; /** - Sends the specified message to the Flutter side, ignoring any reply. - - - Parameter message: The message. Must be supported by the codec of this - channel. + * Sends the specified message to the Flutter side, ignoring any reply. + * + * @param message The message. Must be supported by the codec of this + * channel. */ - (void)sendMessage:(id _Nullable)message; /** - Sends the specified message to the Flutter side, expecting an asynchronous - reply. - - - Parameters: - - message: The message. Must be supported by the codec of this channel. - - callback: A callback to be invoked with the message reply from Flutter. + * Sends the specified message to the Flutter side, expecting an asynchronous + * reply. + * + * @param message The message. Must be supported by the codec of this channel. + * @param callback A callback to be invoked with the message reply from Flutter. */ -- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback; +- (void)sendMessage:(id _Nullable)message + reply:(FlutterReply _Nullable)callback + // TODO: Add macOS support for replies once + // https://github.com/flutter/flutter/issues/18852 is fixed. + API_UNAVAILABLE(macos); /** - Registers a message handler with this channel. - - Replaces any existing handler. Use a `nil` handler for unregistering the - existing handler. - - - Parameter handler: The message handler. + * Registers a message handler with this channel. + * + * Replaces any existing handler. Use a `nil` handler for unregistering the + * existing handler. + * + * @param handler The message handler. */ - (void)setMessageHandler:(FlutterMessageHandler _Nullable)handler; @end /** - A method call result callback. - - Used for submitting a method call result back to a Flutter caller. Also used in - the dual capacity for handling a method call result received from Flutter. - - - Parameter result: The result. + * A method call result callback. + * + * Used for submitting a method call result back to a Flutter caller. Also used in + * the dual capacity for handling a method call result received from Flutter. + * + * @param result The result. */ typedef void (^FlutterResult)(id _Nullable result); /** - A strategy for handling method calls. - - - Parameters: - - call: The incoming method call. - - result: A callback to asynchronously submit the result of the call. - Invoke the callback with a `FlutterError` to indicate that the call failed. - Invoke the callback with `FlutterMethodNotImplemented` to indicate that the - method was unknown. Any other values, including `nil`, are interpreted as - successful results. + * A strategy for handling method calls. + * + * @param call The incoming method call. + * @param result A callback to asynchronously submit the result of the call. + * Invoke the callback with a `FlutterError` to indicate that the call failed. + * Invoke the callback with `FlutterMethodNotImplemented` to indicate that the + * method was unknown. Any other values, including `nil`, are interpreted as + * successful results. */ typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call, FlutterResult result); /** - A constant used with `FlutterMethodCallHandler` to respond to the call of an - unknown method. + * A constant used with `FlutterMethodCallHandler` to respond to the call of an + * unknown method. */ FLUTTER_EXPORT extern NSObject const* FlutterMethodNotImplemented; /** - A channel for communicating with the Flutter side using invocation of - asynchronous methods. + * A channel for communicating with the Flutter side using invocation of + * asynchronous methods. */ FLUTTER_EXPORT @interface FlutterMethodChannel : NSObject /** - Creates a `FlutterMethodChannel` with the specified name and binary messenger. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - The channel uses `FlutterStandardMethodCodec` to encode and decode method calls - and result envelopes. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. + * Creates a `FlutterMethodChannel` with the specified name and binary messenger. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * The channel uses `FlutterStandardMethodCodec` to encode and decode method calls + * and result envelopes. + * + * @param name The channel name. + * @param messenger The binary messenger. */ + (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger; /** - Creates a `FlutterMethodChannel` with the specified name, binary messenger, and - method codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The method codec. + * Creates a `FlutterMethodChannel` with the specified name, binary messenger, and + * method codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The method codec. */ + (instancetype)methodChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; /** - Initializes a `FlutterMethodChannel` with the specified name, binary messenger, - and method codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The method codec. + * Initializes a `FlutterMethodChannel` with the specified name, binary messenger, + * and method codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The method codec. */ - (instancetype)initWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; +// clang-format off /** - Invokes the specified Flutter method with the specified arguments, expecting - no results. See - [MethodChannel.setMethodCallHandler](https://docs.flutter.io/flutter/services/MethodChannel/setMethodCallHandler.html). - - - Parameters: - - method: The name of the method to invoke. - - arguments: The arguments. Must be a value supported by the codec of this - channel. + * Invokes the specified Flutter method with the specified arguments, expecting + * no results. + * + * @see [MethodChannel.setMethodCallHandler](https://docs.flutter.io/flutter/services/MethodChannel/setMethodCallHandler.html) + * + * @param method The name of the method to invoke. + * @param arguments The arguments. Must be a value supported by the codec of this + * channel. */ +// clang-format on - (void)invokeMethod:(NSString*)method arguments:(id _Nullable)arguments; /** - Invokes the specified Flutter method with the specified arguments, expecting - an asynchronous result. - - - Parameters: - - method: The name of the method to invoke. - - arguments: The arguments. Must be a value supported by the codec of this - channel. - - result: A callback that will be invoked with the asynchronous result. - The result will be a `FlutterError` instance, if the method call resulted - in an error on the Flutter side. Will be `FlutterMethodNotImplemented`, if - the method called was not implemented on the Flutter side. Any other value, - including `nil`, should be interpreted as successful results. + * Invokes the specified Flutter method with the specified arguments, expecting + * an asynchronous result. + * + * @param method The name of the method to invoke. + * @param arguments The arguments. Must be a value supported by the codec of this + * channel. + * @param callback A callback that will be invoked with the asynchronous result. + * The result will be a `FlutterError` instance, if the method call resulted + * in an error on the Flutter side. Will be `FlutterMethodNotImplemented`, if + * the method called was not implemented on the Flutter side. Any other value, + * including `nil`, should be interpreted as successful results. */ - (void)invokeMethod:(NSString*)method arguments:(id _Nullable)arguments - result:(FlutterResult _Nullable)callback; + result:(FlutterResult _Nullable)callback + // TODO: Add macOS support for replies once + // https://github.com/flutter/flutter/issues/18852 is fixed. + API_UNAVAILABLE(macos); /** - Registers a handler for method calls from the Flutter side. - - Replaces any existing handler. Use a `nil` handler for unregistering the - existing handler. - - - Parameter handler: The method call handler. + * Registers a handler for method calls from the Flutter side. + * + * Replaces any existing handler. Use a `nil` handler for unregistering the + * existing handler. + * + * @param handler The method call handler. */ - (void)setMethodCallHandler:(FlutterMethodCallHandler _Nullable)handler; @end /** - An event sink callback. - - - Parameter event: The event. + * An event sink callback. + * + * @param event The event. */ typedef void (^FlutterEventSink)(id _Nullable event); /** - A strategy for exposing an event stream to the Flutter side. + * A strategy for exposing an event stream to the Flutter side. */ FLUTTER_EXPORT @protocol FlutterStreamHandler /** - Sets up an event stream and begin emitting events. - - Invoked when the first listener is registered with the Stream associated to - this channel on the Flutter side. - - - Parameters: - - arguments: Arguments for the stream. - - events: A callback to asynchronously emit events. Invoke the - callback with a `FlutterError` to emit an error event. Invoke the - callback with `FlutterEndOfEventStream` to indicate that no more - events will be emitted. Any other value, including `nil` are emitted as - successful events. - - Returns: A FlutterError instance, if setup fails. + * Sets up an event stream and begin emitting events. + * + * Invoked when the first listener is registered with the Stream associated to + * this channel on the Flutter side. + * + * @param arguments Arguments for the stream. + * @param events A callback to asynchronously emit events. Invoke the + * callback with a `FlutterError` to emit an error event. Invoke the + * callback with `FlutterEndOfEventStream` to indicate that no more + * events will be emitted. Any other value, including `nil` are emitted as + * successful events. + * @return A FlutterError instance, if setup fails. */ - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)events; /** - Tears down an event stream. - - Invoked when the last listener is deregistered from the Stream associated to - this channel on the Flutter side. - - The channel implementation may call this method with `nil` arguments - to separate a pair of two consecutive set up requests. Such request pairs - may occur during Flutter hot restart. - - - Parameter arguments: Arguments for the stream. - - Returns: A FlutterError instance, if teardown fails. + * Tears down an event stream. + * + * Invoked when the last listener is deregistered from the Stream associated to + * this channel on the Flutter side. + * + * The channel implementation may call this method with `nil` arguments + * to separate a pair of two consecutive set up requests. Such request pairs + * may occur during Flutter hot restart. + * + * @param arguments Arguments for the stream. + * @return A FlutterError instance, if teardown fails. */ - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments; @end /** - A constant used with `FlutterEventChannel` to indicate end of stream. + * A constant used with `FlutterEventChannel` to indicate end of stream. */ FLUTTER_EXPORT extern NSObject const* FlutterEndOfEventStream; /** - A channel for communicating with the Flutter side using event streams. + * A channel for communicating with the Flutter side using event streams. */ FLUTTER_EXPORT @interface FlutterEventChannel : NSObject /** - Creates a `FlutterEventChannel` with the specified name and binary messenger. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - The channel uses `FlutterStandardMethodCodec` to decode stream setup and - teardown requests, and to encode event envelopes. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The method codec. + * Creates a `FlutterEventChannel` with the specified name and binary messenger. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterViewController`. + * + * The channel uses `FlutterStandardMethodCodec` to decode stream setup and + * teardown requests, and to encode event envelopes. + * + * @param name The channel name. + * @param messenger The binary messenger. */ + (instancetype)eventChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger; /** - Creates a `FlutterEventChannel` with the specified name, binary messenger, - and method codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The method codec. + * Creates a `FlutterEventChannel` with the specified name, binary messenger, + * and method codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The method codec. */ + (instancetype)eventChannelWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; /** - Initializes a `FlutterEventChannel` with the specified name, binary messenger, - and method codec. - - The channel name logically identifies the channel; identically named channels - interfere with each other's communication. - - The binary messenger is a facility for sending raw, binary messages to the - Flutter side. This protocol is implemented by `FlutterViewController`. - - - Parameters: - - name: The channel name. - - messenger: The binary messenger. - - codec: The method codec. + * Initializes a `FlutterEventChannel` with the specified name, binary messenger, + * and method codec. + * + * The channel name logically identifies the channel; identically named channels + * interfere with each other's communication. + * + * The binary messenger is a facility for sending raw, binary messages to the + * Flutter side. This protocol is implemented by `FlutterEngine` and `FlutterViewController`. + * + * @param name The channel name. + * @param messenger The binary messenger. + * @param codec The method codec. */ - (instancetype)initWithName:(NSString*)name binaryMessenger:(NSObject*)messenger codec:(NSObject*)codec; /** - Registers a handler for stream setup requests from the Flutter side. - - Replaces any existing handler. Use a `nil` handler for unregistering the - existing handler. - - - Parameter handler: The stream handler. + * Registers a handler for stream setup requests from the Flutter side. + * + * Replaces any existing handler. Use a `nil` handler for unregistering the + * existing handler. + * + * @param handler The stream handler. */ - (void)setStreamHandler:(NSObject* _Nullable)handler; @end diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h b/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h index 54d7eeca54fda..0034a0709a09b 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,76 +16,76 @@ NS_ASSUME_NONNULL_BEGIN FLUTTER_EXPORT @protocol FlutterMessageCodec /** - Returns a shared instance of this `FlutterMessageCodec`. + * Returns a shared instance of this `FlutterMessageCodec`. */ + (instancetype)sharedInstance; /** - Encodes the specified message into binary. - - - Parameter message: The message. - - Returns: The binary encoding, or `nil`, if `message` was `nil`. + * Encodes the specified message into binary. + * + * @param message The message. + * @return The binary encoding, or `nil`, if `message` was `nil`. */ - (NSData* _Nullable)encode:(id _Nullable)message; /** - Decodes the specified message from binary. - - - Parameter message: The message. - - Returns: The decoded message, or `nil`, if `message` was `nil`. + * Decodes the specified message from binary. + * + * @param message The message. + * @return The decoded message, or `nil`, if `message` was `nil`. */ - (id _Nullable)decode:(NSData* _Nullable)message; @end /** - A `FlutterMessageCodec` using unencoded binary messages, represented as - `NSData` instances. - - This codec is guaranteed to be compatible with the corresponding - [BinaryCodec](https://docs.flutter.io/flutter/services/BinaryCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. - - On the Dart side, messages are represented using `ByteData`. + * A `FlutterMessageCodec` using unencoded binary messages, represented as + * `NSData` instances. + * + * This codec is guaranteed to be compatible with the corresponding + * [BinaryCodec](https://docs.flutter.io/flutter/services/BinaryCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + * On the Dart side, messages are represented using `ByteData`. */ FLUTTER_EXPORT @interface FlutterBinaryCodec : NSObject @end /** - A `FlutterMessageCodec` using UTF-8 encoded `NSString` messages. - - This codec is guaranteed to be compatible with the corresponding - [StringCodec](https://docs.flutter.io/flutter/services/StringCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * A `FlutterMessageCodec` using UTF-8 encoded `NSString` messages. + * + * This codec is guaranteed to be compatible with the corresponding + * [StringCodec](https://docs.flutter.io/flutter/services/StringCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. */ FLUTTER_EXPORT @interface FlutterStringCodec : NSObject @end /** - A `FlutterMessageCodec` using UTF-8 encoded JSON messages. - - This codec is guaranteed to be compatible with the corresponding - [JSONMessageCodec](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. - - Supports values accepted by `NSJSONSerialization` plus top-level - `nil`, `NSNumber`, and `NSString`. - - On the Dart side, JSON messages are handled by the JSON facilities of the - [`dart:convert`](https://api.dartlang.org/stable/dart-convert/JSON-constant.html) - package. + * A `FlutterMessageCodec` using UTF-8 encoded JSON messages. + * + * This codec is guaranteed to be compatible with the corresponding + * [JSONMessageCodec](https://docs.flutter.io/flutter/services/JSONMessageCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + * Supports values accepted by `NSJSONSerialization` plus top-level + * `nil`, `NSNumber`, and `NSString`. + * + * On the Dart side, JSON messages are handled by the JSON facilities of the + * [`dart:convert`](https://api.dartlang.org/stable/dart-convert/JSON-constant.html) + * package. */ FLUTTER_EXPORT @interface FlutterJSONMessageCodec : NSObject @end /** - A writer of the Flutter standard binary encoding. - - See `FlutterStandardMessageCodec` for details on the encoding. - - The encoding is extensible via subclasses overriding `writeValue`. + * A writer of the Flutter standard binary encoding. + * + * See `FlutterStandardMessageCodec` for details on the encoding. + * + * The encoding is extensible via subclasses overriding `writeValue`. */ FLUTTER_EXPORT @interface FlutterStandardWriter : NSObject @@ -100,11 +100,11 @@ FLUTTER_EXPORT @end /** - A reader of the Flutter standard binary encoding. - - See `FlutterStandardMessageCodec` for details on the encoding. - - The encoding is extensible via subclasses overriding `readValueOfType`. + * A reader of the Flutter standard binary encoding. + * + * See `FlutterStandardMessageCodec` for details on the encoding. + * + * The encoding is extensible via subclasses overriding `readValueOfType`. */ FLUTTER_EXPORT @interface FlutterStandardReader : NSObject @@ -121,8 +121,8 @@ FLUTTER_EXPORT @end /** - A factory of compatible reader/writer instances using the Flutter standard - binary encoding or extensions thereof. + * A factory of compatible reader/writer instances using the Flutter standard + * binary encoding or extensions thereof. */ FLUTTER_EXPORT @interface FlutterStandardReaderWriter : NSObject @@ -131,29 +131,29 @@ FLUTTER_EXPORT @end /** - A `FlutterMessageCodec` using the Flutter standard binary encoding. - - This codec is guaranteed to be compatible with the corresponding - [StandardMessageCodec](https://docs.flutter.io/flutter/services/StandardMessageCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. - - Supported messages are acyclic values of these forms: - - - `nil` or `NSNull` - - `NSNumber` (including their representation of Boolean values) - - `NSString` - - `FlutterStandardTypedData` - - `NSArray` of supported values - - `NSDictionary` with supported keys and values - - On the Dart side, these values are represented as follows: - - - `nil` or `NSNull`: null - - `NSNumber`: `bool`, `int`, or `double`, depending on the contained value. - - `NSString`: `String` - - `FlutterStandardTypedData`: `Uint8List`, `Int32List`, `Int64List`, or `Float64List` - - `NSArray`: `List` - - `NSDictionary`: `Map` + * A `FlutterMessageCodec` using the Flutter standard binary encoding. + * + * This codec is guaranteed to be compatible with the corresponding + * [StandardMessageCodec](https://docs.flutter.io/flutter/services/StandardMessageCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + * Supported messages are acyclic values of these forms: + * + * - `nil` or `NSNull` + * - `NSNumber` (including their representation of Boolean values) + * - `NSString` + * - `FlutterStandardTypedData` + * - `NSArray` of supported values + * - `NSDictionary` with supported keys and values + * + * On the Dart side, these values are represented as follows: + * + * - `nil` or `NSNull`: null + * - `NSNumber`: `bool`, `int`, or `double`, depending on the contained value. + * - `NSString`: `String` + * - `FlutterStandardTypedData`: `Uint8List`, `Int32List`, `Int64List`, or `Float64List` + * - `NSArray`: `List` + * - `NSDictionary`: `Map` */ FLUTTER_EXPORT @interface FlutterStandardMessageCodec : NSObject @@ -166,39 +166,37 @@ FLUTTER_EXPORT FLUTTER_EXPORT @interface FlutterMethodCall : NSObject /** - Creates a method call for invoking the specified named method with the - specified arguments. - - - Parameters: - - method: the name of the method to call. - - arguments: the arguments value. + * Creates a method call for invoking the specified named method with the + * specified arguments. + * + * @param method the name of the method to call. + * @param arguments the arguments value. */ + (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id _Nullable)arguments; /** - The method name. + * The method name. */ @property(readonly, nonatomic) NSString* method; /** - The arguments. + * The arguments. */ @property(readonly, nonatomic, nullable) id arguments; @end /** - Error object representing an unsuccessful outcome of invoking a method - on a `FlutterMethodChannel`, or an error event on a `FlutterEventChannel`. + * Error object representing an unsuccessful outcome of invoking a method + * on a `FlutterMethodChannel`, or an error event on a `FlutterEventChannel`. */ FLUTTER_EXPORT @interface FlutterError : NSObject /** - Creates a `FlutterError` with the specified error code, message, and details. - - - Parameters: - - code: An error code string for programmatic use. - - message: A human-readable error message. - - details: Custom error details. + * Creates a `FlutterError` with the specified error code, message, and details. + * + * @param code An error code string for programmatic use. + * @param message A human-readable error message. + * @param details Custom error details. */ + (instancetype)errorWithCode:(NSString*)code message:(NSString* _Nullable)message @@ -220,12 +218,12 @@ FLUTTER_EXPORT @end /** - Type of numeric data items encoded in a `FlutterStandardDataType`. - - - FlutterStandardDataTypeUInt8: plain bytes - - FlutterStandardDataTypeInt32: 32-bit signed integers - - FlutterStandardDataTypeInt64: 64-bit signed integers - - FlutterStandardDataTypeFloat64: 64-bit floats + * Type of numeric data items encoded in a `FlutterStandardDataType`. + * + * - FlutterStandardDataTypeUInt8: plain bytes + * - FlutterStandardDataTypeInt32: 32-bit signed integers + * - FlutterStandardDataTypeInt64: 64-bit signed integers + * - FlutterStandardDataTypeFloat64: 64-bit floats */ typedef NS_ENUM(NSInteger, FlutterStandardDataType) { FlutterStandardDataTypeUInt8, @@ -235,174 +233,173 @@ typedef NS_ENUM(NSInteger, FlutterStandardDataType) { }; /** - A byte buffer holding `UInt8`, `SInt32`, `SInt64`, or `Float64` values, used - with `FlutterStandardMessageCodec` and `FlutterStandardMethodCodec`. - - Two's complement encoding is used for signed integers. IEEE754 - double-precision representation is used for floats. The platform's native - endianness is assumed. + * A byte buffer holding `UInt8`, `SInt32`, `SInt64`, or `Float64` values, used + * with `FlutterStandardMessageCodec` and `FlutterStandardMethodCodec`. + * + * Two's complement encoding is used for signed integers. IEEE754 + * double-precision representation is used for floats. The platform's native + * endianness is assumed. */ FLUTTER_EXPORT @interface FlutterStandardTypedData : NSObject /** - Creates a `FlutterStandardTypedData` which interprets the specified data - as plain bytes. - - - Parameter data: the byte data. + * Creates a `FlutterStandardTypedData` which interprets the specified data + * as plain bytes. + * + * @param data the byte data. */ + (instancetype)typedDataWithBytes:(NSData*)data; /** - Creates a `FlutterStandardTypedData` which interprets the specified data - as 32-bit signed integers. - - - Parameter data: the byte data. The length must be divisible by 4. + * Creates a `FlutterStandardTypedData` which interprets the specified data + * as 32-bit signed integers. + * + * @param data the byte data. The length must be divisible by 4. */ + (instancetype)typedDataWithInt32:(NSData*)data; /** - Creates a `FlutterStandardTypedData` which interprets the specified data - as 64-bit signed integers. - - - Parameter data: the byte data. The length must be divisible by 8. + * Creates a `FlutterStandardTypedData` which interprets the specified data + * as 64-bit signed integers. + * + * @param data the byte data. The length must be divisible by 8. */ + (instancetype)typedDataWithInt64:(NSData*)data; /** - Creates a `FlutterStandardTypedData` which interprets the specified data - as 64-bit floats. - - - Parameter data: the byte data. The length must be divisible by 8. + * Creates a `FlutterStandardTypedData` which interprets the specified data + * as 64-bit floats. + * + * @param data the byte data. The length must be divisible by 8. */ + (instancetype)typedDataWithFloat64:(NSData*)data; /** - The raw underlying data buffer. + * The raw underlying data buffer. */ @property(readonly, nonatomic) NSData* data; /** - The type of the encoded values. + * The type of the encoded values. */ @property(readonly, nonatomic) FlutterStandardDataType type; /** - The number of value items encoded. + * The number of value items encoded. */ @property(readonly, nonatomic) UInt32 elementCount; /** - The number of bytes used by the encoding of a single value item. + * The number of bytes used by the encoding of a single value item. */ @property(readonly, nonatomic) UInt8 elementSize; @end /** - An arbitrarily large integer value, used with `FlutterStandardMessageCodec` - and `FlutterStandardMethodCodec`. + * An arbitrarily large integer value, used with `FlutterStandardMessageCodec` + * and `FlutterStandardMethodCodec`. */ FLUTTER_EXPORT -FLUTTER_UNAVAILABLE( - "Unavailable on 2018-08-31. Deprecated on 2018-01-09. " - "FlutterStandardBigInteger was needed because the Dart 1.0 int type had no " - "size limit. With Dart 2.0, the int type is a fixed-size, 64-bit signed " - "integer. If you need to communicate larger integers, use NSString encoding " - "instead.") +FLUTTER_UNAVAILABLE("Unavailable on 2018-08-31. Deprecated on 2018-01-09. " + "FlutterStandardBigInteger was needed because the Dart 1.0 int type had no " + "size limit. With Dart 2.0, the int type is a fixed-size, 64-bit signed " + "integer. If you need to communicate larger integers, use NSString encoding " + "instead.") @interface FlutterStandardBigInteger : NSObject @end /** - A codec for method calls and enveloped results. - - Method calls are encoded as binary messages with enough structure that the - codec can extract a method name `NSString` and an arguments `NSObject`, - possibly `nil`. These data items are used to populate a `FlutterMethodCall`. - - Result envelopes are encoded as binary messages with enough structure that - the codec can determine whether the result was successful or an error. In - the former case, the codec can extract the result `NSObject`, possibly `nil`. - In the latter case, the codec can extract an error code `NSString`, a - human-readable `NSString` error message (possibly `nil`), and a custom - error details `NSObject`, possibly `nil`. These data items are used to - populate a `FlutterError`. + * A codec for method calls and enveloped results. + * + * Method calls are encoded as binary messages with enough structure that the + * codec can extract a method name `NSString` and an arguments `NSObject`, + * possibly `nil`. These data items are used to populate a `FlutterMethodCall`. + * + * Result envelopes are encoded as binary messages with enough structure that + * the codec can determine whether the result was successful or an error. In + * the former case, the codec can extract the result `NSObject`, possibly `nil`. + * In the latter case, the codec can extract an error code `NSString`, a + * human-readable `NSString` error message (possibly `nil`), and a custom + * error details `NSObject`, possibly `nil`. These data items are used to + * populate a `FlutterError`. */ FLUTTER_EXPORT @protocol FlutterMethodCodec /** - Provides access to a shared instance this codec. - - - Returns: The shared instance. + * Provides access to a shared instance this codec. + * + * @return The shared instance. */ + (instancetype)sharedInstance; /** - Encodes the specified method call into binary. - - - Parameter methodCall: The method call. The arguments value - must be supported by this codec. - - Returns: The binary encoding. + * Encodes the specified method call into binary. + * + * @param methodCall The method call. The arguments value + * must be supported by this codec. + * @return The binary encoding. */ - (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall; /** - Decodes the specified method call from binary. - - - Parameter methodCall: The method call to decode. - - Returns: The decoded method call. + * Decodes the specified method call from binary. + * + * @param methodCall The method call to decode. + * @return The decoded method call. */ - (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall; /** - Encodes the specified successful result into binary. - - - Parameter result: The result. Must be a value supported by this codec. - - Returns: The binary encoding. + * Encodes the specified successful result into binary. + * + * @param result The result. Must be a value supported by this codec. + * @return The binary encoding. */ - (NSData*)encodeSuccessEnvelope:(id _Nullable)result; /** - Encodes the specified error result into binary. - - - Parameter error: The error object. The error details value must be supported - by this codec. - - Returns: The binary encoding. + * Encodes the specified error result into binary. + * + * @param error The error object. The error details value must be supported + * by this codec. + * @return The binary encoding. */ - (NSData*)encodeErrorEnvelope:(FlutterError*)error; /** - Deccodes the specified result envelope from binary. - - - Parameter error: The error object. - - Returns: The result value, if the envelope represented a successful result, - or a `FlutterError` instance, if not. + * Deccodes the specified result envelope from binary. + * + * @param envelope The error object. + * @return The result value, if the envelope represented a successful result, + * or a `FlutterError` instance, if not. */ - (id _Nullable)decodeEnvelope:(NSData*)envelope; @end /** - A `FlutterMethodCodec` using UTF-8 encoded JSON method calls and result - envelopes. - - This codec is guaranteed to be compatible with the corresponding - [JSONMethodCodec](https://docs.flutter.io/flutter/services/JSONMethodCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. - - Values supported as methods arguments and result payloads are - those supported as top-level or leaf values by `FlutterJSONMessageCodec`. + * A `FlutterMethodCodec` using UTF-8 encoded JSON method calls and result + * envelopes. + * + * This codec is guaranteed to be compatible with the corresponding + * [JSONMethodCodec](https://docs.flutter.io/flutter/services/JSONMethodCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + * Values supported as methods arguments and result payloads are + * those supported as top-level or leaf values by `FlutterJSONMessageCodec`. */ FLUTTER_EXPORT @interface FlutterJSONMethodCodec : NSObject @end /** - A `FlutterMethodCodec` using the Flutter standard binary encoding. - - This codec is guaranteed to be compatible with the corresponding - [StandardMethodCodec](https://docs.flutter.io/flutter/services/StandardMethodCodec-class.html) - on the Dart side. These parts of the Flutter SDK are evolved synchronously. - - Values supported as method arguments and result payloads are those supported by - `FlutterStandardMessageCodec`. + * A `FlutterMethodCodec` using the Flutter standard binary encoding. + * + * This codec is guaranteed to be compatible with the corresponding + * [StandardMethodCodec](https://docs.flutter.io/flutter/services/StandardMethodCodec-class.html) + * on the Dart side. These parts of the Flutter SDK are evolved synchronously. + * + * Values supported as method arguments and result payloads are those supported by + * `FlutterStandardMessageCodec`. */ FLUTTER_EXPORT @interface FlutterStandardMethodCodec : NSObject diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h b/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h index bef67aa04a937..032d1c281f3a2 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,43 +9,69 @@ #include "FlutterMacros.h" +/** + * A set of Flutter and Dart assets used by a `FlutterEngine` to initialize execution. + */ FLUTTER_EXPORT @interface FlutterDartProject : NSObject +/** + * Initializes a Flutter Dart project from a bundle. + */ - (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithFlutterAssets:(NSURL*)flutterAssetsURL - dartMain:(NSURL*)dartMainURL - packages:(NSURL*)dartPackages NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL - NS_DESIGNATED_INITIALIZER; - +/** + * Unavailable - use `init` instead. + */ - (instancetype)initFromDefaultSourceForConfiguration FLUTTER_UNAVAILABLE("Use -init instead."); /** - Returns the file name for the given asset. - The returned file name can be used to access the asset in the application's main bundle. - - - Parameter asset: The name of the asset. The name can be hierarchical. - - Returns: the file name to be used for lookup in the main bundle. + * Returns the file name for the given asset. If the bundle with the identifier + * "io.flutter.flutter.app" exists, it will try use that bundle; otherwise, it + * will use the main bundle. To specify a different bundle, use + * `-lookupKeyForAsset:asset:fromBundle`. + * + * @param asset The name of the asset. The name can be hierarchical. + * @return the file name to be used for lookup in the main bundle. */ + (NSString*)lookupKeyForAsset:(NSString*)asset; /** - Returns the file name for the given asset which originates from the specified package. - The returned file name can be used to access the asset in the application's main bundle. + * Returns the file name for the given asset. + * The returned file name can be used to access the asset in the supplied bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @param bundle The `NSBundle` to use for looking up the asset. + * @return the file name to be used for lookup in the main bundle. + */ ++ (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(NSBundle*)bundle; - - Parameters: - - asset: The name of the asset. The name can be hierarchical. - - package: The name of the package from which the asset originates. - - Returns: the file name to be used for lookup in the main bundle. +/** + * Returns the file name for the given asset which originates from the specified package. + * The returned file name can be used to access the asset in the application's main bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @param package The name of the package from which the asset originates. + * @return the file name to be used for lookup in the main bundle. */ + (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package; /** - Returns the default identifier for the bundle where we expect to find the Flutter Dart - application. + * Returns the file name for the given asset which originates from the specified package. + * The returned file name can be used to access the asset in the specified bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @param package The name of the package from which the asset originates. + * @param bundle The bundle to use when doing the lookup. + * @return the file name to be used for lookup in the main bundle. + */ ++ (NSString*)lookupKeyForAsset:(NSString*)asset + fromPackage:(NSString*)package + fromBundle:(NSBundle*)bundle; + +/** + * Returns the default identifier for the bundle where we expect to find the Flutter Dart + * application. */ + (NSString*)defaultBundleIdentifier; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h new file mode 100644 index 0000000000000..bf1e757687695 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -0,0 +1,233 @@ +// Copyright 2013 The Flutter 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_FLUTTERENGINE_H_ +#define FLUTTER_FLUTTERENGINE_H_ + +#import +#import + +#include "FlutterBinaryMessenger.h" +#include "FlutterDartProject.h" +#include "FlutterMacros.h" +#include "FlutterPlugin.h" +#include "FlutterTexture.h" + +@class FlutterViewController; + +/** + * The FlutterEngine class coordinates a single instance of execution for a + * `FlutterDartProject`. It may have zero or one `FlutterViewController` at a + * time, which can be specified via `-setViewController:`. + * `FlutterViewController`'s `initWithEngine` initializer will automatically call + * `-setViewController:` for itself. + * + * A FlutterEngine can be created independently of a `FlutterViewController` for + * headless execution. It can also persist across the lifespan of multiple + * `FlutterViewController` instances to maintain state and/or asynchronous tasks + * (such as downloading a large file). + * + * Alternatively, you can simply create a new `FlutterViewController` with only a + * `FlutterDartProject`. That `FlutterViewController` will internally manage its + * own instance of a FlutterEngine, but will not guarantee survival of the engine + * beyond the life of the ViewController. + * + * A newly initialized FlutterEngine will not actually run a Dart Isolate until + * either `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is invoked. + * One of these methods must be invoked before calling `-setViewController:`. + */ +FLUTTER_EXPORT +@interface FlutterEngine + : NSObject +/** + * Initialize this FlutterEngine with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterEngine will attempt to locate + * the project in a default location (the flutter_assets folder in the iOS application + * bundle). + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI:` is called. + * + * FlutterEngine created with this method will have allowHeadlessExecution set to `YES`. + * This means that the engine will continue to run regardless of whether a `FlutterViewController` + * is attached to it or not, until `-destroyContext:` is called or the process finishes. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances, and is used in instrumentation to label + * the threads used by this FlutterEngine. + * @param projectOrNil The `FlutterDartProject` to run. + */ +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; + +/** + * Initialize this FlutterEngine with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterEngine will attempt to locate + * the project in a default location (the flutter_assets folder in the iOS application + * bundle). + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI:` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances, and is used in instrumentation to label + * the threads used by this FlutterEngine. + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Whether or not to allow this instance to continue + * running after passing a nil `FlutterViewController` to `-setViewController:`. + */ +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; + +/** + * The default initializer is not available for this object. + * Callers must use `-[FlutterEngine initWithName:project:]`. + */ +- (instancetype)init NS_UNAVAILABLE; + ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Runs a Dart program on an Isolate from the main Dart library (i.e. the library that + * contains `main()`). + * + * The first call to this method will create a new Isolate. Subsequent calls will return + * immediately. + * + * @param entrypoint The name of a top-level function from the same Dart + * library that contains the app's main() function. If this is nil, it will + * default to `main()`. If it is not the app's main() function, that function + * must be decorated with `@pragma(vm:entry-point)` to ensure the method is not + * tree-shaken by the Dart compiler. + * @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise. + */ +- (BOOL)runWithEntrypoint:(NSString*)entrypoint; + +/** + * Runs a Dart program on an Isolate using the specified entrypoint and Dart library, + * which may not be the same as the library containing the Dart program's `main()` function. + * + * The first call to this method will create a new Isolate. Subsequent calls will return + * immediately. + * + * @param entrypoint The name of a top-level function from a Dart library. If nil, this will + * default to `main()`. If it is not the app's main() function, that function + * must be decorated with `@pragma(vm:entry-point)` to ensure the method is not + * tree-shaken by the Dart compiler. + * @param uri The URI of the Dart library which contains the entrypoint method. IF nil, + * this will default to the same library as the `main()` function in the Dart program. + * @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise. + */ +- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)uri; + +/** + * Destroy running context for an engine. + * + * This method can be used to force the FlutterEngine object to release all resources. + * After sending this message, the object will be in an unusable state until it is deallocated. + * Accessing properties or sending messages to it will result in undefined behavior or runtime + * errors. + */ +- (void)destroyContext; + +/** + * Ensures that Flutter will generate a semantics tree. + * + * This is enabled by default if certain accessibility services are turned on by + * the user, or when using a Simulator. This method allows a user to turn + * semantics on when they would not ordinarily be generated and the performance + * overhead is not a concern, e.g. for UI testing. Note that semantics should + * never be programmatically turned off, as it would potentially disable + * accessibility services an end user has requested. + * + * This method must only be called after launching the engine via + * `-runWithEntrypoint:` or `-runWithEntryPoint:libraryURI`. + * + * Although this method returns synchronously, it does not guarantee that a + * semantics tree is actually available when the method returns. It + * synchronously ensures that the next frame the Flutter framework creates will + * have a semantics tree. + * + * You can subscribe to semantics updates via `NSNotificationCenter` by adding + * an observer for the name `FlutterSemanticsUpdateNotification`. The `object` + * parameter will be the `FlutterViewController` associated with the semantics + * update. This will asynchronously fire after a semantics tree has actually + * built (which may be some time after the frame has been rendered). + */ +- (void)ensureSemanticsEnabled; + +/** + * Sets the `FlutterViewController` for this instance. The FlutterEngine must be + * running (e.g. a successful call to `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI`) + * before calling this method. Callers may pass nil to remove the viewController + * and have the engine run headless in the current process. + * + * A FlutterEngine can only have one `FlutterViewController` at a time. If there is + * already a `FlutterViewController` associated with this instance, this method will replace + * the engine's current viewController with the newly specified one. + * + * Setting the viewController will signal the engine to start animations and drawing, and unsetting + * it will signal the engine to stop animations and drawing. However, neither will impact the state + * of the Dart program's execution. + */ +@property(nonatomic, weak) FlutterViewController* viewController; + +/** + * The `FlutterMethodChannel` used for localization related platform messages, such as + * setting the locale. + */ +@property(nonatomic, readonly) FlutterMethodChannel* localizationChannel; +/** + * The `FlutterMethodChannel` used for navigation related platform messages. + * + * @see [Navigation + * Channel](https://docs.flutter.io/flutter/services/SystemChannels/navigation-constant.html) + * @see [Navigator Widget](https://docs.flutter.io/flutter/widgets/Navigator-class.html) + */ +@property(nonatomic, readonly) FlutterMethodChannel* navigationChannel; + +/** + * The `FlutterMethodChannel` used for core platform messages, such as + * information about the screen orientation. + */ +@property(nonatomic, readonly) FlutterMethodChannel* platformChannel; + +/** + * The `FlutterMethodChannel` used to communicate text input events to the + * Dart Isolate. + * + * @see [Text Input + * Channel](https://docs.flutter.io/flutter/services/SystemChannels/textInput-constant.html) + */ +@property(nonatomic, readonly) FlutterMethodChannel* textInputChannel; + +/** + * The `FlutterBasicMessageChannel` used to communicate app lifecycle events + * to the Dart Isolate. + * + * @see [Lifecycle + * Channel](https://docs.flutter.io/flutter/services/SystemChannels/lifecycle-constant.html) + */ +@property(nonatomic, readonly) FlutterBasicMessageChannel* lifecycleChannel; + +/** + * The `FlutterBasicMessageChannel` used for communicating system events, such as + * memory pressure events. + * + * @see [System + * Channel](https://docs.flutter.io/flutter/services/SystemChannels/system-constant.html) + */ +@property(nonatomic, readonly) FlutterBasicMessageChannel* systemChannel; + +/** + * The `FlutterBasicMessageChannel` used for communicating user settings such as + * clock format and text scale. + */ +@property(nonatomic, readonly) FlutterBasicMessageChannel* settingsChannel; + +@end + +#endif // FLUTTER_FLUTTERENGINE_H_ diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h index f6f37248364ed..24acbc08ad6fe 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,44 +9,68 @@ #include "FlutterBinaryMessenger.h" #include "FlutterDartProject.h" +#include "FlutterEngine.h" #include "FlutterMacros.h" /** -A callback for when FlutterHeadlessDartRunner has attempted to start a Dart -Isolate in the background. - -- Parameter success: YES if the Isolate was started and run successfully, NO - otherwise. -*/ + * A callback for when FlutterHeadlessDartRunner has attempted to start a Dart + * Isolate in the background. + * + * @param success YES if the Isolate was started and run successfully, NO + * otherwise. + */ typedef void (^FlutterHeadlessDartRunnerCallback)(BOOL success); /** - The FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer, - and no native drawing surface. It is appropriate for use in running Dart - code e.g. in the background from a plugin. -*/ + * The FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer, + * and no native drawing surface. It is appropriate for use in running Dart + * code e.g. in the background from a plugin. + * + * Most callers should prefer using `FlutterEngine` directly; this interface exists + * for legacy support. + */ FLUTTER_EXPORT -@interface FlutterHeadlessDartRunner : NSObject +FLUTTER_DEPRECATED("FlutterEngine should be used rather than FlutterHeadlessDartRunner") +@interface FlutterHeadlessDartRunner : FlutterEngine /** - Runs a Dart function on an Isolate that is not the main application's Isolate. - The first call will create a new Isolate. Subsequent calls will return - immediately. + * Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate + * the project in a default location. + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances + * @param projectOrNil The `FlutterDartProject` to run. + */ +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil; - - Parameter entrypoint: The name of a top-level function from the same Dart - library that contains the app's main() function. -*/ -- (void)runWithEntrypoint:(NSString*)entrypoint; +/** + * Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`. + * + * If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate + * the project in a default location. + * + * A newly initialized engine will not run the `FlutterDartProject` until either + * `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called. + * + * @param labelPrefix The label prefix used to identify threads for this instance. Should + * be unique across FlutterEngine instances + * @param projectOrNil The `FlutterDartProject` to run. + * @param allowHeadlessExecution Must be set to `YES`. + */ +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER; /** - Runs a Dart function on an Isolate that is not the main application's Isolate. - The first call will create a new Isolate. Subsequent calls will return - immediately. - - - Parameter entrypoint: The name of a top-level function from a Dart library. - - Parameter uri: The URI of the Dart library which contains entrypoint. -*/ -- (void)runWithEntrypointAndLibraryUri:(NSString*)entrypoint libraryUri:(NSString*)uri; + * Not recommended for use - will initialize with a default label ("io.flutter.headless") + * and the default FlutterDartProject. + */ +- (instancetype)init; @end diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h b/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h index e3050963192e1..51b27bb789d6e 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,19 +21,19 @@ #endif // defined(NS_ASSUME_NONNULL_BEGIN) /** - Indicates that the API has been deprecated for the specified reason. Code that - uses the deprecated API will continue to work as before. However, the API will - soon become unavailable and users are encouraged to immediately take the - appropriate action mentioned in the deprecation message and the BREAKING - CHANGES section present in the Flutter.h umbrella header. + * Indicates that the API has been deprecated for the specified reason. Code + * that uses the deprecated API will continue to work as before. However, the + * API will soon become unavailable and users are encouraged to immediately take + * the appropriate action mentioned in the deprecation message and the BREAKING + * CHANGES section present in the Flutter.h umbrella header. */ #define FLUTTER_DEPRECATED(msg) __attribute__((__deprecated__(msg))) /** - Indicates that the previously deprecated API is now unavailable. Code that - uses the API will not work and the declaration of the API is only a stub meant - to display the given message detailing the actions for the user to take - immediately. + * Indicates that the previously deprecated API is now unavailable. Code that + * uses the API will not work and the declaration of the API is only a stub + * meant to display the given message detailing the actions for the user to take + * immediately. */ #define FLUTTER_UNAVAILABLE(msg) __attribute__((__unavailable__(msg))) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h b/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h deleted file mode 100644 index 1ff0911bd685d..0000000000000 --- a/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface FlutterNavigationController : UINavigationController - -@end diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h new file mode 100644 index 0000000000000..72086dec13a83 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter 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_FLUTTERPLATFORMVIEWS_H_ +#define FLUTTER_FLUTTERPLATFORMVIEWS_H_ + +#import + +#import "FlutterCodecs.h" +#import "FlutterMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Wraps a `UIView` for embedding in the Flutter hierarchy + */ +@protocol FlutterPlatformView +/** + * Returns a reference to the `UIView` that is wrapped by this `FlutterPlatformView`. + */ +- (UIView*)view; +@end + +FLUTTER_EXPORT +@protocol FlutterPlatformViewFactory +/** + * Create a `FlutterPlatformView`. + * + * Implemented by iOS code that expose a `UIView` for embedding in a Flutter app. + * + * The implementation of this method should create a new `UIView` and return it. + * + * @param frame The rectangle for the newly created `UIView` measued in points. + * @param viewId A unique identifier for this `UIView`. + * @param args Parameters for creating the `UIView` sent from the Dart side of the Flutter app. + * If `createArgsCodec` is not implemented, or if no creation arguments were sent from the Dart + * code, this will be null. Otherwise this will be the value sent from the Dart code as decoded by + * `createArgsCodec`. + */ +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args; + +/** + * Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`. + * + * Only needs to be implemented if `createWithFrame` needs an arguments parameter. + */ +@optional +- (NSObject*)createArgsCodec; +@end + +NS_ASSUME_NONNULL_END + +#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_ diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 799aa90fa0b30..4b340bf7b9e78 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,85 +6,109 @@ #define FLUTTER_FLUTTERPLUGIN_H_ #import +#import #include "FlutterBinaryMessenger.h" #include "FlutterChannels.h" #include "FlutterCodecs.h" +#include "FlutterPlatformViews.h" #include "FlutterTexture.h" NS_ASSUME_NONNULL_BEGIN @protocol FlutterPluginRegistrar; +@protocol FlutterPluginRegistry; /** - Implemented by the iOS part of a Flutter plugin. + * A plugin registration callback. + * + * Used for registering plugins with additional instances of + * `FlutterPluginRegistry`. + * + * @param registry The registry to register plugins with. + */ +typedef void (*FlutterPluginRegistrantCallback)(NSObject* registry); - Defines a set of optional callback methods and a method to set up the plugin - and register it to be called by other application components. +/** + * Implemented by the iOS part of a Flutter plugin. + * + * Defines a set of optional callback methods and a method to set up the plugin + * and register it to be called by other application components. */ @protocol FlutterPlugin @required /** - Registers this plugin using the context information and callback registration - methods exposed by the given registrar. - - The registrar is obtained from a `FlutterPluginRegistry` which keeps track of - the identity of registered plugins and provides basic support for cross-plugin - coordination. - - The caller of this method, a plugin registrant, is usually autogenerated by - Flutter tooling based on declared plugin dependencies. The generated registrant - asks the registry for a registrar for each plugin, and calls this method to - allow the plugin to initialize itself and register callbacks with application - objects available through the registrar protocol. - - - Parameters: - - registrar: A helper providing application context and methods for - registering callbacks. + * Registers this plugin using the context information and callback registration + * methods exposed by the given registrar. + * + * The registrar is obtained from a `FlutterPluginRegistry` which keeps track of + * the identity of registered plugins and provides basic support for cross-plugin + * coordination. + * + * The caller of this method, a plugin registrant, is usually autogenerated by + * Flutter tooling based on declared plugin dependencies. The generated registrant + * asks the registry for a registrar for each plugin, and calls this method to + * allow the plugin to initialize itself and register callbacks with application + * objects available through the registrar protocol. + * + * @param registrar A helper providing application context and methods for + * registering callbacks. */ + (void)registerWithRegistrar:(NSObject*)registrar; @optional /** - Called if this plugin has been registered to receive `FlutterMethodCall`s. - - - Parameters: - - call: The method call command object. - - result: A callback for submitting the result of the call. + * Set a callback for registering plugins to an additional `FlutterPluginRegistry`, + * including headless `FlutterEngine` instances. + * + * This method is typically called from within an application's `AppDelegate` at + * startup to allow for plugins which create additional `FlutterEngine` instances + * to register the application's plugins. + * + * @param callback A callback for registering some set of plugins with a + * `FlutterPluginRegistry`. + */ ++ (void)setPluginRegistrantCallback:(FlutterPluginRegistrantCallback)callback; +@optional +/** + * Called if this plugin has been registered to receive `FlutterMethodCall`s. + * + * @param call The method call command object. + * @param result A callback for submitting the result of the call. */ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `NO` if this plugin vetoes application launch. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `NO` if this plugin vetoes application launch. */ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `NO` if this plugin vetoes application launch. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `NO` if this plugin vetoes application launch. */ - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ - (void)applicationDidBecomeActive:(UIApplication*)application; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ - (void)applicationWillResignActive:(UIApplication*)application; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ - (void)applicationDidEnterBackground:(UIApplication*)application; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ - (void)applicationWillEnterForeground:(UIApplication*)application; @@ -94,83 +118,101 @@ NS_ASSUME_NONNULL_BEGIN - (void)applicationWillTerminate:(UIApplication*)application; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings; +#pragma GCC diagnostic pop /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. */ - (BOOL)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. + */ +- (void)application:(UIApplication*)application + didReceiveLocalNotification:(UILocalNotification*)notification; - - Returns: `YES` if this plugin handles the request. -*/ +/** + * Calls all plugins registered for `UNUserNotificationCenterDelegate` callbacks. + */ +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + willPresentNotification:(UNNotification*)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler + API_AVAILABLE(ios(10)); + +/** + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. + */ - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary*)options; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. */ - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. -*/ + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. + */ - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. -*/ + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. + */ - (BOOL)application:(UIApplication*)application performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler API_AVAILABLE(ios(9.0)); /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. */ - (BOOL)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString*)identifier completionHandler:(nonnull void (^)(void))completionHandler; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. */ - (BOOL)application:(UIApplication*)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. - - - Returns: `YES` if this plugin handles the request. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * + * @return `YES` if this plugin handles the request. */ - (BOOL)application:(UIApplication*)application continueUserActivity:(NSUserActivity*)userActivity @@ -178,133 +220,144 @@ NS_ASSUME_NONNULL_BEGIN @end /** - Registration context for a single `FlutterPlugin`, providing a one stop shop - for the plugin to access contextual information and register callbacks for - various application events. - - Registrars are obtained from a `FlutterPluginRegistry` which keeps track of - the identity of registered plugins and provides basic support for cross-plugin - coordination. + *Registration context for a single `FlutterPlugin`, providing a one stop shop + *for the plugin to access contextual information and register callbacks for + *various application events. + * + *Registrars are obtained from a `FlutterPluginRegistry` which keeps track of + *the identity of registered plugins and provides basic support for cross-plugin + *coordination. */ @protocol FlutterPluginRegistrar /** - Returns a `FlutterBinaryMessenger` for creating Dart/iOS communication - channels to be used by the plugin. - - - Returns: The messenger. + * Returns a `FlutterBinaryMessenger` for creating Dart/iOS communication + * channels to be used by the plugin. + * + * @return The messenger. */ - (NSObject*)messenger; /** - Returns a `FlutterTextureRegistry` for registering textures - provided by the plugin. - - - Returns: The texture registry. + * Returns a `FlutterTextureRegistry` for registering textures + * provided by the plugin. + * + * @return The texture registry. */ - (NSObject*)textures; /** - Publishes a value for external use of the plugin. - - Plugins may publish a single value, such as an instance of the - plugin's main class, for situations where external control or - interaction is needed. - - The published value will be available from the `FlutterPluginRegistry`. - Repeated calls overwrite any previous publication. - - - Parameter value: The value to be published. + * Registers a `FlutterPlatformViewFactory` for creation of platfrom views. + * + * Plugins expose `UIView` for embedding in Flutter apps by registering a view factory. + * + * @param factory The view factory that will be registered. + * @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use + * this identifier to request creation of a `UIView` by the registered factory. + */ +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId; + +/** + * Publishes a value for external use of the plugin. + * + * Plugins may publish a single value, such as an instance of the + * plugin's main class, for situations where external control or + * interaction is needed. + * + * The published value will be available from the `FlutterPluginRegistry`. + * Repeated calls overwrite any previous publication. + * + * @param value The value to be published. */ - (void)publish:(NSObject*)value; /** - Registers the plugin as a receiver of incoming method calls from the Dart side - on the specified `FlutterMethodChannel`. - - - Parameters: - - delegate: The receiving object, such as the plugin's main class. - - channel: The channel + * Registers the plugin as a receiver of incoming method calls from the Dart side + * on the specified `FlutterMethodChannel`. + * + * @param delegate The receiving object, such as the plugin's main class. + * @param channel The channel */ - (void)addMethodCallDelegate:(NSObject*)delegate channel:(FlutterMethodChannel*)channel; /** - Registers the plugin as a receiver of `UIApplicationDelegate` calls. - - - Parameters delegate: The receiving object, such as the plugin's main class. + * Registers the plugin as a receiver of `UIApplicationDelegate` calls. + * + * @param delegate The receiving object, such as the plugin's main class. */ - (void)addApplicationDelegate:(NSObject*)delegate; /** - Returns the file name for the given asset. - The returned file name can be used to access the asset in the application's main bundle. - - - Parameter asset: The name of the asset. The name can be hierarchical. - - Returns: the file name to be used for lookup in the main bundle. + * Returns the file name for the given asset. + * The returned file name can be used to access the asset in the application's main bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @return the file name to be used for lookup in the main bundle. */ - (NSString*)lookupKeyForAsset:(NSString*)asset; /** - Returns the file name for the given asset which originates from the specified package. - The returned file name can be used to access the asset in the application's main bundle. - - - Parameters: - - asset: The name of the asset. The name can be hierarchical. - - package: The name of the package from which the asset originates. - - Returns: the file name to be used for lookup in the main bundle. + * Returns the file name for the given asset which originates from the specified package. + * The returned file name can be used to access the asset in the application's main bundle. + * + * + * @param asset The name of the asset. The name can be hierarchical. + * @param package The name of the package from which the asset originates. + * @return the file name to be used for lookup in the main bundle. */ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package; @end /** - A registry of Flutter iOS plugins. - - Plugins are identified by unique string keys, typically the name of the - plugin's main class. The registry tracks plugins by this key, mapping it to - a value published by the plugin during registration, if any. This provides a - very basic means of cross-plugin coordination with loose coupling between - unrelated plugins. - - Plugins typically need contextual information and the ability to register - callbacks for various application events. To keep the API of the registry - focused, these facilities are not provided directly by the registry, but by - a `FlutterPluginRegistrar`, created by the registry in exchange for the unique - key of the plugin. - - There is no implied connection between the registry and the registrar. - Specifically, callbacks registered by the plugin via the registrar may be - relayed directly to the underlying iOS application objects. + * A registry of Flutter iOS plugins. + * + * Plugins are identified by unique string keys, typically the name of the + * plugin's main class. The registry tracks plugins by this key, mapping it to + * a value published by the plugin during registration, if any. This provides a + * very basic means of cross-plugin coordination with loose coupling between + * unrelated plugins. + * + * Plugins typically need contextual information and the ability to register + * callbacks for various application events. To keep the API of the registry + * focused, these facilities are not provided directly by the registry, but by + * a `FlutterPluginRegistrar`, created by the registry in exchange for the unique + * key of the plugin. + * + * There is no implied connection between the registry and the registrar. + * Specifically, callbacks registered by the plugin via the registrar may be + * relayed directly to the underlying iOS application objects. */ @protocol FlutterPluginRegistry /** - Returns a registrar for registering a plugin. - - - Parameter pluginKey: The unique key identifying the plugin. + * Returns a registrar for registering a plugin. + * + * @param pluginKey The unique key identifying the plugin. */ - (NSObject*)registrarForPlugin:(NSString*)pluginKey; /** - Returns whether the specified plugin has been registered. - - - Parameter pluginKey: The unique key identifying the plugin. - - Returns: `YES` if `registrarForPlugin` has been called with `pluginKey`. + * Returns whether the specified plugin has been registered. + * + * @param pluginKey The unique key identifying the plugin. + * @return `YES` if `registrarForPlugin` has been called with `pluginKey`. */ - (BOOL)hasPlugin:(NSString*)pluginKey; /** - Returns a value published by the specified plugin. - - - Parameter pluginKey: The unique key identifying the plugin. - - Returns: An object published by the plugin, if any. Will be `NSNull` if - nothing has been published. Will be `nil` if the plugin has not been - registered. + * Returns a value published by the specified plugin. + * + * @param pluginKey The unique key identifying the plugin. + * @return An object published by the plugin, if any. Will be `NSNull` if + * nothing has been published. Will be `nil` if the plugin has not been + * registered. */ - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey; @end /** - Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves - to the application life cycle events. -*/ + * Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register + * themselves to the application life cycle events. + */ @protocol FlutterAppLifeCycleProvider - (void)addApplicationLifeCycleDelegate:(NSObject*)delegate; @end diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h b/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h index 8c9c0358669ed..a8dda282b56fd 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,139 +10,158 @@ NS_ASSUME_NONNULL_BEGIN /** - Propagates `UIAppDelegate` callbacks to registered plugins. -*/ + * Propagates `UIAppDelegate` callbacks to registered plugins. + */ FLUTTER_EXPORT @interface FlutterPluginAppLifeCycleDelegate : NSObject /** - Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as - long as it is alive. - - `delegate` will only referenced weakly. -*/ + * Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate + * as long as it is alive. + * + * `delegate` will only referenced weakly. + */ - (void)addDelegate:(NSObject*)delegate; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. - - - Returns: `NO` if any plugin vetoes application launch. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. + * + * @return `NO` if any plugin vetoes application launch. */ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. - - - Returns: `NO` if any plugin vetoes application launch. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. + * + * @return `NO` if any plugin vetoes application launch. */ - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)applicationDidBecomeActive:(UIApplication*)application; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)applicationWillResignActive:(UIApplication*)application; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)applicationDidEnterBackground:(UIApplication*)application; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)applicationWillEnterForeground:(UIApplication*)application; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)applicationWillTerminate:(UIApplication*)application; /** - Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + * Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings; +#pragma GCC diagnostic pop /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. */ - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. + * Calls all plugins registered for `UIApplicationDelegate` callbacks. + */ +- (void)application:(UIApplication*)application + didReceiveLocalNotification:(UILocalNotification*)notification; - - Returns: `YES` if any plugin handles the request. -*/ +/** + * Calls all plugins registered for `UNUserNotificationCenterDelegate` callbacks. + */ +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + willPresentNotification:(UNNotification*)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler + API_AVAILABLE(ios(10)); + +/** + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @return `YES` if any plugin handles the request. + */ - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary*)options; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. - - - Returns: `YES` if any plugin handles the request. + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @return `YES` if any plugin handles the request. */ - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. - - - Returns: `YES` if any plugin handles the request. -*/ + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @return `YES` if any plugin handles the request. + */ - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks. -*/ + * Calls all plugins registered for `UIApplicationDelegate` callbacks. + */ - (void)application:(UIApplication*)application performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler API_AVAILABLE(ios(9.0)); /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. - - - Returns: `YES` if any plugin handles the request. -*/ + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @return `YES` if any plugin handles the request. + */ - (BOOL)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString*)identifier completionHandler:(nonnull void (^)(void))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. - - - Returns: `YES` if any plugin handles the request. -*/ + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @returns `YES` if any plugin handles the request. + */ - (BOOL)application:(UIApplication*)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until - some plugin handles the request. - - Returns: `YES` if any plugin handles the request. -*/ + * Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + * some plugin handles the request. + * + * @return `YES` if any plugin handles the request. + */ - (BOOL)application:(UIApplication*)application continueUserActivity:(NSUserActivity*)userActivity restorationHandler:(void (^)(NSArray*))restorationHandler; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h index f2b8670a8a9e2..e7cd01337deb9 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,12 +13,12 @@ NS_ASSUME_NONNULL_BEGIN FLUTTER_EXPORT -@protocol FlutterTexture +@protocol FlutterTexture - (CVPixelBufferRef _Nullable)copyPixelBuffer; @end FLUTTER_EXPORT -@protocol FlutterTextureRegistry +@protocol FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture; - (void)textureFrameAvailable:(int64_t)textureId; - (void)unregisterTexture:(int64_t)textureId; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 5459c17f4f8e6..b38b118fff9cd 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,14 +10,61 @@ #include "FlutterBinaryMessenger.h" #include "FlutterDartProject.h" +#include "FlutterEngine.h" #include "FlutterMacros.h" #include "FlutterPlugin.h" #include "FlutterTexture.h" +@class FlutterEngine; + +/** + * The name used for semantic update nofications via `NSNotificationCenter`. + * + * The object passed as the sender is the `FlutterViewController` associated + * with the update. + */ +FLUTTER_EXPORT +extern NSNotificationName const FlutterSemanticsUpdateNotification; + +/** + * A `UIViewController` implementation for Flutter views. + * + * Dart execution, channel communication, texture registration, and plugin registration + * are all handled by `FlutterEngine`. Calls on this class to those members all proxy + * through to the `FlutterEngine` attached FlutterViewController. + * + * A FlutterViewController can be initialized either with an already-running `FlutterEngine`, + * or it can be initialized with a `FlutterDartProject` that will be used to spin up + * a new `FlutterEngine`. Developers looking to present and hide FlutterViewControllers + * in native iOS applications will usually want to maintain the `FlutterEngine` instance + * so as not to lose Dart-related state and asynchronous tasks when navigating back and + * forth between a FlutterViewController and other `UIViewController`s. + */ FLUTTER_EXPORT @interface FlutterViewController : UIViewController +/** + * Initializes this FlutterViewController with the specified `FlutterEngine`. + * + * The initialized viewcontroller will attach itself to the engine as part of this process. + * + * @param engine The `FlutterEngine` instance to attach to. + * @param nibNameOrNil The NIB name to initialize this UIViewController with. + * @param nibBundleOrNil The NIB bundle. + */ +- (instancetype)initWithEngine:(FlutterEngine*)engine + nibName:(NSString*)nibNameOrNil + bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER; + +/** + * Initializes a new FlutterViewController and `FlutterEngine` with the specified + * `FlutterDartProject`. + * + * @param projectOrNil The `FlutterDartProject` to initialize the `FlutterEngine` with. + * @param nibNameOrNil The NIB name to initialize this UIViewController with. + * @param nibBundleOrNil The NIB bundle. + */ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil nibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER; @@ -25,73 +72,98 @@ FLUTTER_EXPORT - (void)handleStatusBarTouches:(UIEvent*)event; /** - Registers a callback that will be invoked when the Flutter view has been rendered. - The callback will be fired only once. - - Replaces an existing callback. Use a `nil` callback to unregister the existing one. + * Registers a callback that will be invoked when the Flutter view has been rendered. + * The callback will be fired only once. + * + * Replaces an existing callback. Use a `nil` callback to unregister the existing one. */ - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback; /** - Returns the file name for the given asset. - The returned file name can be used to access the asset in the application's main bundle. - - - Parameter asset: The name of the asset. The name can be hierarchical. - - Returns: the file name to be used for lookup in the main bundle. + * Returns the file name for the given asset. + * The returned file name can be used to access the asset in the application's + * main bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @return The file name to be used for lookup in the main bundle. */ - (NSString*)lookupKeyForAsset:(NSString*)asset; /** - Returns the file name for the given asset which originates from the specified package. - The returned file name can be used to access the asset in the application's main bundle. - - - Parameters: - - asset: The name of the asset. The name can be hierarchical. - - package: The name of the package from which the asset originates. - - Returns: the file name to be used for lookup in the main bundle. + * Returns the file name for the given asset which originates from the specified + * package. + * The returned file name can be used to access the asset in the application's + * main bundle. + * + * @param asset The name of the asset. The name can be hierarchical. + * @param package The name of the package from which the asset originates. + * @return The file name to be used for lookup in the main bundle. */ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package; /** - Sets the first route that the Flutter app shows. The default is "/". - This method will guarnatee that the initial route is delivered, even if the - Flutter window hasn't been created yet when called. It cannot be used to update - the current route being shown in a visible FlutterViewController (see pushRoute - and popRoute). - - - Parameter route: The name of the first route to show. + * Sets the first route that the Flutter app shows. The default is "/". + * This method will guarnatee that the initial route is delivered, even if the + * Flutter window hasn't been created yet when called. It cannot be used to update + * the current route being shown in a visible FlutterViewController (see pushRoute + * and popRoute). + * + * @param route The name of the first route to show. */ - (void)setInitialRoute:(NSString*)route; /** - Instructs the Flutter Navigator (if any) to go back. + * Instructs the Flutter Navigator (if any) to go back. */ - (void)popRoute; /** - Instructs the Flutter Navigator (if any) to push a route on to the navigation - stack. The setInitialRoute method should be prefered if this is called before the - FlutterViewController has come into view. - - - Parameter route: The name of the route to push to the navigation stack. + * Instructs the Flutter Navigator (if any) to push a route on to the navigation + * stack. The setInitialRoute method should be prefered if this is called before the + * FlutterViewController has come into view. + * + * @param route The name of the route to push to the navigation stack. */ - (void)pushRoute:(NSString*)route; +/** + * The `FlutterPluginRegistry` used by this FlutterViewController. + */ - (id)pluginRegistry; /** - Specifies the view to use as a splash screen. Flutter's rendering is asynchronous, so the first - frame rendered by the Flutter application might not immediately appear when the Flutter view is - initially placed in the view hierarchy. The splash screen view will be used as a replacement - until the first frame is rendered. + * Specifies the view to use as a splash screen. Flutter's rendering is asynchronous, so the first + * frame rendered by the Flutter application might not immediately appear when theFlutter view is + * initially placed in the view hierarchy. The splash screen view will be used as + * a replacement until the first frame is rendered. + * + * The view used should be appropriate for multiple sizes; an autoresizing mask to + * have a flexible width and height will be applied automatically. + */ +@property(strong, nonatomic) UIView* splashScreenView; + +/** + * Attempts to set the `splashScreenView` property from the `UILaunchStoryboardName` from the + * main bundle's `Info.plist` file. This method will not change the value of `splashScreenView` + * if it cannot find a default one from a storyboard or nib. + * + * @return `YES` if successful, `NO` otherwise. + */ +- (BOOL)loadDefaultSplashScreenView; - The view used should be appropriate for multiple sizes; an autoresizing mask to have a flexible - width and height will be applied automatically. +/** + * Controls whether the created view will be opaque or not. + * + * Default is `YES`. Note that setting this to `NO` may negatively impact performance + * when using hardware acceleration, and toggling this will trigger a re-layout of the + * view. + */ +@property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; - If not specified, uses a view generated from `UILaunchStoryboardName` from the main bundle's - `Info.plist` file. +/** + * The `FlutterEngine` instance for this view controller. */ -@property(strong, nonatomic) UIView* splashScreenView; +@property(weak, nonatomic, readonly) FlutterEngine* engine; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index a7921be5c6d32..f3f1786e4555a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -71,11 +71,14 @@ - (void)applicationWillTerminate:(UIApplication*)application { [_lifeCycleDelegate applicationWillTerminate:application]; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings { [_lifeCycleDelegate application:application didRegisterUserNotificationSettings:notificationSettings]; } +#pragma GCC diagnostic pop - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { @@ -91,6 +94,23 @@ - (void)application:(UIApplication*)application fetchCompletionHandler:completionHandler]; } +- (void)application:(UIApplication*)application + didReceiveLocalNotification:(UILocalNotification*)notification { + [_lifeCycleDelegate application:application didReceiveLocalNotification:notification]; +} + +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + willPresentNotification:(UNNotification*)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler + API_AVAILABLE(ios(10)) { + if (@available(iOS 10.0, *)) { + [_lifeCycleDelegate userNotificationCenter:center + willPresentNotification:notification + withCompletionHandler:completionHandler]; + } +} + - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary*)options { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h index c01a33d6f3266..999e56c534ac0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm index eb353e019bed4..562155d319130 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h index 553c485dc2e9d..5bb2e7bf7646e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm b/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm index 4cb4499e09a0e..732c983f0e3b1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterChannels.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm index e49ec86aadff1..0c2340038b828 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterCodecs.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 22d39950d93f1..4e0c97b3f113a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,6 +15,14 @@ #include "flutter/shell/platform/darwin/common/command_line.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +extern "C" { +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +// Used for debugging dart:* sources. +extern const uint8_t kPlatformStrongDill[]; +extern const intptr_t kPlatformStrongDillSize; +#endif +} + static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin"; static blink::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) { @@ -123,6 +131,17 @@ } } +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + // There are no ownership concerns here as all mappings are owned by the + // embedder and not the engine. + auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { + return [mapping, size]() { return std::make_unique(mapping, size); }; + }; + + settings.dart_library_sources_kernel = + make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); +#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + return settings; } @@ -134,7 +153,7 @@ @implementation FlutterDartProject { #pragma mark - Override base class designated initializers - (instancetype)init { - return [self initWithFlutterAssets:nil dartMain:nil packages:nil]; + return [self initWithPrecompiledDartBundle:nil]; } #pragma mark - Designated initializers @@ -150,42 +169,6 @@ - (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle { return self; } -- (instancetype)initWithFlutterAssets:(NSURL*)flutterAssetsURL - dartMain:(NSURL*)dartMainURL - packages:(NSURL*)dartPackages { - self = [super init]; - - if (self) { - _settings = DefaultSettingsForProcess(); - - if (dartMainURL != nil && [[NSFileManager defaultManager] fileExistsAtPath:dartMainURL.path]) { - _settings.main_dart_file_path = dartMainURL.path.UTF8String; - } - - if (dartPackages.path != nil && - [[NSFileManager defaultManager] fileExistsAtPath:dartPackages.path]) { - _settings.packages_file_path = dartPackages.path.UTF8String; - } - } - - return self; -} - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL { - self = [super init]; - - if (self) { - _settings = DefaultSettingsForProcess(); - - if (flutterAssetsURL != nil && - [[NSFileManager defaultManager] fileExistsAtPath:flutterAssetsURL.path]) { - _settings.assets_path = flutterAssetsURL.path.UTF8String; - } - } - - return self; -} - #pragma mark - Settings accessors - (const blink::Settings&)settings { @@ -193,27 +176,60 @@ - (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL } - (shell::RunConfiguration)runConfiguration { - return shell::RunConfiguration::InferFromSettings(_settings); + return [self runConfigurationForEntrypoint:nil]; +} + +- (shell::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil { + return [self runConfigurationForEntrypoint:entrypointOrNil libraryOrNil:nil]; +} + +- (shell::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil + libraryOrNil:(NSString*)dartLibraryOrNil { + shell::RunConfiguration config = shell::RunConfiguration::InferFromSettings(_settings); + if (dartLibraryOrNil && entrypointOrNil) { + config.SetEntrypointAndLibrary(std::string([entrypointOrNil UTF8String]), + std::string([dartLibraryOrNil UTF8String])); + + } else if (entrypointOrNil) { + config.SetEntrypoint(std::string([entrypointOrNil UTF8String])); + } + return config; } #pragma mark - Assets-related utilities + (NSString*)flutterAssetsName:(NSBundle*)bundle { + if (bundle == nil) { + bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]]; + } + if (bundle == nil) { + bundle = [NSBundle mainBundle]; + } NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"]; if (flutterAssetsName == nil) { - // Default to "flutter_assets" - flutterAssetsName = @"flutter_assets"; + flutterAssetsName = @"Frameworks/App.framework/flutter_assets"; } return flutterAssetsName; } + (NSString*)lookupKeyForAsset:(NSString*)asset { - NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:[NSBundle mainBundle]]; + return [self lookupKeyForAsset:asset fromBundle:nil]; +} + ++ (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(NSBundle*)bundle { + NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:bundle]; return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset]; } + (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { - return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]]; + return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil]; +} + ++ (NSString*)lookupKeyForAsset:(NSString*)asset + fromPackage:(NSString*)package + fromBundle:(NSBundle*)bundle { + return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset] + fromBundle:bundle]; } + (NSString*)defaultBundleIdentifier { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h index 155f3c8ef4533..844c74d01ad34 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -14,6 +14,9 @@ - (const blink::Settings&)settings; - (shell::RunConfiguration)runConfiguration; +- (shell::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil; +- (shell::RunConfiguration)runConfigurationForEntrypoint:(NSString*)entrypointOrNil + libraryOrNil:(NSString*)dartLibraryOrNil; + (NSString*)flutterAssetsName:(NSBundle*)bundle; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm new file mode 100644 index 0000000000000..00fead7de304c --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -0,0 +1,634 @@ +// Copyright 2013 The Flutter 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 + +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" + +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/fml/platform/darwin/platform_version.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/common/engine.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/darwin/common/command_line.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" +#include "flutter/shell/platform/darwin/ios/ios_surface.h" +#include "flutter/shell/platform/darwin/ios/platform_view_ios.h" + +@interface FlutterEngine () +// Maintains a dictionary of plugin names that have registered with the engine. Used by +// FlutterEngineRegistrar to implement a FlutterPluginRegistrar. +@property(nonatomic, readonly) NSMutableDictionary* pluginPublications; +@end + +@interface FlutterEngineRegistrar : NSObject +- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine; +@end + +@implementation FlutterEngine { + fml::scoped_nsobject _dartProject; + shell::ThreadHost _threadHost; + std::unique_ptr _shell; + NSString* _labelPrefix; + std::unique_ptr> _weakFactory; + + fml::WeakPtr _viewController; + fml::scoped_nsobject _publisher; + + std::unique_ptr _platformViewsController; + + // Channels + fml::scoped_nsobject _platformPlugin; + fml::scoped_nsobject _textInputPlugin; + fml::scoped_nsobject _localizationChannel; + fml::scoped_nsobject _navigationChannel; + fml::scoped_nsobject _platformChannel; + fml::scoped_nsobject _platformViewsChannel; + fml::scoped_nsobject _textInputChannel; + fml::scoped_nsobject _lifecycleChannel; + fml::scoped_nsobject _systemChannel; + fml::scoped_nsobject _settingsChannel; + + int64_t _nextTextureId; + + uint64_t _nextPointerFlowId; + + BOOL _allowHeadlessExecution; +} + +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; +} + +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { + self = [super init]; + NSAssert(self, @"Super init cannot be nil"); + NSAssert(labelPrefix, @"labelPrefix is required"); + + _allowHeadlessExecution = allowHeadlessExecution; + _labelPrefix = [labelPrefix copy]; + + _weakFactory = std::make_unique>(self); + + if (projectOrNil == nil) + _dartProject.reset([[FlutterDartProject alloc] init]); + else + _dartProject.reset([projectOrNil retain]); + + _pluginPublications = [NSMutableDictionary new]; + _platformViewsController.reset(new shell::FlutterPlatformViewsController()); + + [self setupChannels]; + + return self; +} + +- (void)dealloc { + [_pluginPublications release]; + [super dealloc]; +} + +- (shell::Shell&)shell { + FML_DCHECK(_shell); + return *_shell; +} + +- (fml::WeakPtr)getWeakPtr { + return _weakFactory->GetWeakPtr(); +} + +- (void)updateViewportMetrics:(blink::ViewportMetrics)viewportMetrics { + self.shell.GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = self.shell.GetEngine(), metrics = viewportMetrics]() { + if (engine) { + engine->SetViewportMetrics(std::move(metrics)); + } + }); +} + +- (void)dispatchPointerDataPacket:(std::unique_ptr)packet { + TRACE_EVENT0("flutter", "dispatchPointerDataPacket"); + TRACE_FLOW_BEGIN("flutter", "PointerEvent", _nextPointerFlowId); + self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = self.shell.GetEngine(), packet = std::move(packet), flow_id = _nextPointerFlowId] { + if (engine) { + engine->DispatchPointerDataPacket(*packet, flow_id); + } + })); + _nextPointerFlowId++; +} + +- (fml::WeakPtr)platformView { + FML_DCHECK(_shell); + return _shell->GetPlatformView(); +} + +- (shell::PlatformViewIOS*)iosPlatformView { + FML_DCHECK(_shell); + return static_cast(_shell->GetPlatformView().get()); +} + +- (fml::RefPtr)platformTaskRunner { + FML_DCHECK(_shell); + return _shell->GetTaskRunners().GetPlatformTaskRunner(); +} + +- (void)ensureSemanticsEnabled { + self.iosPlatformView->SetSemanticsEnabled(true); +} + +- (void)setViewController:(FlutterViewController*)viewController { + FML_DCHECK(self.iosPlatformView); + _viewController = [viewController getWeakPtr]; + self.iosPlatformView->SetOwnerViewController(_viewController); + [self maybeSetupPlatformViewChannels]; +} + +- (void)notifyViewControllerDeallocated { + if (!_allowHeadlessExecution) { + [self destroyContext]; + } +} + +- (void)destroyContext { + [self resetChannels]; + _shell.reset(); + _threadHost.Reset(); +} + +- (FlutterViewController*)viewController { + if (!_viewController) { + return nil; + } + return _viewController.get(); +} + +- (FlutterPlatformPlugin*)platformPlugin { + return _platformPlugin.get(); +} +- (shell::FlutterPlatformViewsController*)platformViewsController { + return _platformViewsController.get(); +} +- (FlutterTextInputPlugin*)textInputPlugin { + return _textInputPlugin.get(); +} +- (FlutterMethodChannel*)localizationChannel { + return _localizationChannel.get(); +} +- (FlutterMethodChannel*)navigationChannel { + return _navigationChannel.get(); +} +- (FlutterMethodChannel*)platformChannel { + return _platformChannel.get(); +} +- (FlutterMethodChannel*)textInputChannel { + return _textInputChannel.get(); +} +- (FlutterBasicMessageChannel*)lifecycleChannel { + return _lifecycleChannel.get(); +} +- (FlutterBasicMessageChannel*)systemChannel { + return _systemChannel.get(); +} +- (FlutterBasicMessageChannel*)settingsChannel { + return _settingsChannel.get(); +} + +- (void)resetChannels { + _localizationChannel.reset(); + _navigationChannel.reset(); + _platformChannel.reset(); + _platformViewsChannel.reset(); + _textInputChannel.reset(); + _lifecycleChannel.reset(); + _systemChannel.reset(); + _settingsChannel.reset(); +} + +// If you add a channel, be sure to also update `resetChannels`. +// Channels get a reference to the engine, and therefore need manual +// cleanup for proper collection. +- (void)setupChannels { + _localizationChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/localization" + binaryMessenger:self + codec:[FlutterJSONMethodCodec sharedInstance]]); + + _navigationChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/navigation" + binaryMessenger:self + codec:[FlutterJSONMethodCodec sharedInstance]]); + + _platformChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/platform" + binaryMessenger:self + codec:[FlutterJSONMethodCodec sharedInstance]]); + + _platformViewsChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/platform_views" + binaryMessenger:self + codec:[FlutterStandardMethodCodec sharedInstance]]); + + _textInputChannel.reset([[FlutterMethodChannel alloc] + initWithName:@"flutter/textinput" + binaryMessenger:self + codec:[FlutterJSONMethodCodec sharedInstance]]); + + _lifecycleChannel.reset([[FlutterBasicMessageChannel alloc] + initWithName:@"flutter/lifecycle" + binaryMessenger:self + codec:[FlutterStringCodec sharedInstance]]); + + _systemChannel.reset([[FlutterBasicMessageChannel alloc] + initWithName:@"flutter/system" + binaryMessenger:self + codec:[FlutterJSONMessageCodec sharedInstance]]); + + _settingsChannel.reset([[FlutterBasicMessageChannel alloc] + initWithName:@"flutter/settings" + binaryMessenger:self + codec:[FlutterJSONMessageCodec sharedInstance]]); + + _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]); + _textInputPlugin.get().textInputDelegate = self; + + _platformPlugin.reset([[FlutterPlatformPlugin alloc] initWithEngine:[self getWeakPtr]]); +} + +- (void)maybeSetupPlatformViewChannels { + if (_shell && self.shell.IsSetup()) { + [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [_platformPlugin.get() handleMethodCall:call result:result]; + }]; + + [_platformViewsChannel.get() + setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + _platformViewsController->OnMethodCall(call, result); + }]; + + [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [_textInputPlugin.get() handleMethodCall:call result:result]; + }]; + self.iosPlatformView->SetTextInputPlugin(_textInputPlugin); + } +} + +- (shell::Rasterizer::Screenshot)screenshot:(shell::Rasterizer::ScreenshotType)type + base64Encode:(bool)base64Encode { + return self.shell.Screenshot(type, base64Encode); +} + +- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil { + // Launch the Dart application with the inferred run configuration. + self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = _shell->GetEngine(), + config = [_dartProject.get() runConfigurationForEntrypoint:entrypoint + libraryOrNil:libraryOrNil] // + ]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (result == shell::Engine::RunStatus::Failure) { + FML_LOG(ERROR) << "Could not launch engine with configuration."; + } + } + })); +} + +- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { + if (_shell != nullptr) { + FML_LOG(WARNING) << "This FlutterEngine was already invoked."; + return NO; + } + + static size_t shellCount = 1; + + auto settings = [_dartProject.get() settings]; + + if (libraryURI) { + FML_DCHECK(entrypoint) << "Must specify entrypoint if specifying library"; + settings.advisory_script_entrypoint = entrypoint.UTF8String; + settings.advisory_script_uri = libraryURI.UTF8String; + } else if (entrypoint) { + settings.advisory_script_entrypoint = entrypoint.UTF8String; + settings.advisory_script_uri = std::string("main.dart"); + } else { + settings.advisory_script_entrypoint = std::string("main"); + settings.advisory_script_uri = std::string("main.dart"); + } + + const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++]; + FML_DLOG(INFO) << "Creating threadHost for " << threadLabel.UTF8String; + // The current thread will be used as the platform thread. Ensure that the message loop is + // initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + _threadHost = { + threadLabel.UTF8String, // label + shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO}; + + // Lambda captures by pointers to ObjC objects are fine here because the + // create call is + // synchronous. + shell::Shell::CreateCallback on_create_platform_view = + [](shell::Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }; + + shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + if (shell::IsIosEmbeddedViewsPreviewEnabled()) { + // Embedded views requires the gpu and the platform views to be the same. + // The plan is to eventually dynamically merge the threads when there's a + // platform view in the layer tree. + // For now we use a fixed thread configuration with the same thread used as the + // gpu and platform task runner. + // TODO(amirh/chinmaygarde): remove this, and dynamically change the thread configuration. + // https://github.com/flutter/flutter/issues/23975 + + blink::TaskRunners task_runners(threadLabel.UTF8String, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + fml::MessageLoop::GetCurrent().GetTaskRunner(), // gpu + _threadHost.ui_thread->GetTaskRunner(), // ui + _threadHost.io_thread->GetTaskRunner() // io + ); + // Create the shell. This is a blocking operation. + _shell = shell::Shell::Create(std::move(task_runners), // task runners + std::move(settings), // settings + on_create_platform_view, // platform view creation + on_create_rasterizer // rasterzier creation + ); + } else { + blink::TaskRunners task_runners(threadLabel.UTF8String, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + _threadHost.gpu_thread->GetTaskRunner(), // gpu + _threadHost.ui_thread->GetTaskRunner(), // ui + _threadHost.io_thread->GetTaskRunner() // io + ); + // Create the shell. This is a blocking operation. + _shell = shell::Shell::Create(std::move(task_runners), // task runners + std::move(settings), // settings + on_create_platform_view, // platform view creation + on_create_rasterizer // rasterzier creation + ); + } + + if (_shell == nullptr) { + FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: " + << entrypoint.UTF8String; + } else { + [self setupChannels]; + if (!_platformViewsController) { + _platformViewsController.reset(new shell::FlutterPlatformViewsController()); + } + _publisher.reset([[FlutterObservatoryPublisher alloc] init]); + [self maybeSetupPlatformViewChannels]; + } + + return _shell != nullptr; +} + +- (BOOL)runWithEntrypoint:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { + if ([self createShell:entrypoint libraryURI:libraryURI]) { + [self launchEngine:entrypoint libraryURI:libraryURI]; + } + + return _shell != nullptr; +} + +- (BOOL)runWithEntrypoint:(NSString*)entrypoint { + return [self runWithEntrypoint:entrypoint libraryURI:nil]; +} + +#pragma mark - Text input delegate + +- (void)updateEditingClient:(int)client withState:(NSDictionary*)state { + [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" + arguments:@[ @(client), state ]]; +} + +- (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state + withClient:(int)client + withPosition:(NSDictionary*)position { + NSString* stateString; + switch (state) { + case FlutterFloatingCursorDragStateStart: + stateString = @"FloatingCursorDragState.start"; + break; + case FlutterFloatingCursorDragStateUpdate: + stateString = @"FloatingCursorDragState.update"; + break; + case FlutterFloatingCursorDragStateEnd: + stateString = @"FloatingCursorDragState.end"; + break; + } + [_textInputChannel.get() invokeMethod:@"TextInputClient.updateFloatingCursor" + arguments:@[ @(client), stateString, position ]]; +} + +- (void)performAction:(FlutterTextInputAction)action withClient:(int)client { + NSString* actionString; + switch (action) { + case FlutterTextInputActionUnspecified: + // Where did the term "unspecified" come from? iOS has a "default" and Android + // has "unspecified." These 2 terms seem to mean the same thing but we need + // to pick just one. "unspecified" was chosen because "default" is often a + // reserved word in languages with switch statements (dart, java, etc). + actionString = @"TextInputAction.unspecified"; + break; + case FlutterTextInputActionDone: + actionString = @"TextInputAction.done"; + break; + case FlutterTextInputActionGo: + actionString = @"TextInputAction.go"; + break; + case FlutterTextInputActionSend: + actionString = @"TextInputAction.send"; + break; + case FlutterTextInputActionSearch: + actionString = @"TextInputAction.search"; + break; + case FlutterTextInputActionNext: + actionString = @"TextInputAction.next"; + break; + case FlutterTextInputActionContinue: + actionString = @"TextInputAction.continue"; + break; + case FlutterTextInputActionJoin: + actionString = @"TextInputAction.join"; + break; + case FlutterTextInputActionRoute: + actionString = @"TextInputAction.route"; + break; + case FlutterTextInputActionEmergencyCall: + actionString = @"TextInputAction.emergencyCall"; + break; + case FlutterTextInputActionNewline: + actionString = @"TextInputAction.newline"; + break; + } + [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction" + arguments:@[ @(client), actionString ]]; +} + +#pragma mark - Screenshot Delegate + +- (shell::Rasterizer::Screenshot)takeScreenshot:(shell::Rasterizer::ScreenshotType)type + asBase64Encoded:(BOOL)base64Encode { + FML_DCHECK(_shell) << "Cannot takeScreenshot without a shell"; + return _shell->Screenshot(type, base64Encode); +} + +#pragma mark - FlutterBinaryMessenger + +- (void)sendOnChannel:(NSString*)channel message:(NSData*)message { + [self sendOnChannel:channel message:message binaryReply:nil]; +} + +- (void)sendOnChannel:(NSString*)channel + message:(NSData*)message + binaryReply:(FlutterBinaryReply)callback { + NSAssert(channel, @"The channel must not be null"); + fml::RefPtr response = + (callback == nil) ? nullptr + : fml::MakeRefCounted( + ^(NSData* reply) { + callback(reply); + }, + _shell->GetTaskRunners().GetPlatformTaskRunner()); + fml::RefPtr platformMessage = + (message == nil) ? fml::MakeRefCounted(channel.UTF8String, response) + : fml::MakeRefCounted( + channel.UTF8String, shell::GetVectorFromNSData(message), response); + + _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); +} + +- (void)setMessageHandlerOnChannel:(NSString*)channel + binaryMessageHandler:(FlutterBinaryMessageHandler)handler { + NSAssert(channel, @"The channel must not be null"); + FML_DCHECK(_shell && _shell->IsSetup()); + self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler); +} + +#pragma mark - FlutterTextureRegistry + +- (int64_t)registerTexture:(NSObject*)texture { + int64_t textureId = _nextTextureId++; + self.iosPlatformView->RegisterExternalTexture(textureId, texture); + return textureId; +} + +- (void)unregisterTexture:(int64_t)textureId { + _shell->GetPlatformView()->UnregisterTexture(textureId); +} + +- (void)textureFrameAvailable:(int64_t)textureId { + _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); +} + +- (NSString*)lookupKeyForAsset:(NSString*)asset { + return [FlutterDartProject lookupKeyForAsset:asset]; +} + +- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { + return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; +} + +- (id)pluginRegistry { + return self; +} + +#pragma mark - FlutterPluginRegistry + +- (NSObject*)registrarForPlugin:(NSString*)pluginKey { + NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); + self.pluginPublications[pluginKey] = [NSNull null]; + return [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey flutterEngine:self]; +} + +- (BOOL)hasPlugin:(NSString*)pluginKey { + return _pluginPublications[pluginKey] != nil; +} + +- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { + return _pluginPublications[pluginKey]; +} + +@end + +@implementation FlutterEngineRegistrar { + NSString* _pluginKey; + FlutterEngine* _flutterEngine; +} + +- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { + self = [super init]; + NSAssert(self, @"Super init cannot be nil"); + _pluginKey = [pluginKey retain]; + _flutterEngine = [flutterEngine retain]; + return self; +} + +- (void)dealloc { + [_pluginKey release]; + [_flutterEngine release]; + [super dealloc]; +} + +- (NSObject*)messenger { + return _flutterEngine; +} + +- (NSObject*)textures { + return _flutterEngine; +} + +- (void)publish:(NSObject*)value { + _flutterEngine.pluginPublications[_pluginKey] = value; +} + +- (void)addMethodCallDelegate:(NSObject*)delegate + channel:(FlutterMethodChannel*)channel { + [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [delegate handleMethodCall:call result:result]; + }]; +} + +- (void)addApplicationDelegate:(NSObject*)delegate { + id appDelegate = [[UIApplication sharedApplication] delegate]; + if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) { + id lifeCycleProvider = + (id)appDelegate; + [lifeCycleProvider addApplicationLifeCycleDelegate:delegate]; + } +} + +- (NSString*)lookupKeyForAsset:(NSString*)asset { + return [_flutterEngine lookupKeyForAsset:asset]; +} + +- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { + return [_flutterEngine lookupKeyForAsset:asset fromPackage:package]; +} + +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId { + [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId); +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h new file mode 100644 index 0000000000000..e21444cc6a9d8 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_ + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" + +#import "FlutterPlatformViews_Internal.h" + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#include "flutter/shell/platform/darwin/ios/platform_view_ios.h" + +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" + +@interface FlutterEngine () + +- (shell::Shell&)shell; + +- (void)updateViewportMetrics:(blink::ViewportMetrics)viewportMetrics; +- (void)dispatchPointerDataPacket:(std::unique_ptr)packet; + +- (fml::RefPtr)platformTaskRunner; + +- (fml::WeakPtr)platformView; + +- (shell::Rasterizer::Screenshot)screenshot:(shell::Rasterizer::ScreenshotType)type + base64Encode:(bool)base64Encode; + +- (FlutterPlatformPlugin*)platformPlugin; +- (shell::FlutterPlatformViewsController*)platformViewsController; +- (FlutterTextInputPlugin*)textInputPlugin; +- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil; +- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil; +- (void)notifyViewControllerDeallocated; + +@end + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERENGINE_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index 5d0f42af6c1e0..6299e9e684474 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -1,14 +1,14 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" + #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h" -#include #include -#include #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" @@ -22,128 +22,25 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" -#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -static std::unique_ptr CreateHeadlessPlatformView( - shell::Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); -} - -static std::unique_ptr CreateHeadlessRasterizer(shell::Shell& shell) { - return std::make_unique(shell.GetTaskRunners()); -} - -static std::string CreateShellLabel() { - static size_t count = 1; - std::stringstream stream; - stream << "io.flutter.headless."; - stream << count++; - return stream.str(); -} - @implementation FlutterHeadlessDartRunner { - shell::ThreadHost _threadHost; - std::unique_ptr _shell; } -- (void)runWithEntrypointAndLibraryUri:(NSString*)entrypoint libraryUri:(NSString*)uri { - if (_shell != nullptr || entrypoint.length == 0) { - FML_LOG(ERROR) << "This headless dart runner was already used to run some code."; - return; - } - - const auto label = CreateShellLabel(); - - // Create the threads to run the shell on. - _threadHost = { - label, // native thread label - shell::ThreadHost::Type::UI // managed threads to create - }; - - // Configure shell task runners. - auto current_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); - auto single_task_runner = _threadHost.ui_thread->GetTaskRunner(); - blink::TaskRunners task_runners(label, // dart thread label - current_task_runner, // platform - single_task_runner, // gpu - single_task_runner, // ui - single_task_runner // io - ); - - auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); - - // These values set the name of the isolate for debugging. - settings.advisory_script_entrypoint = entrypoint.UTF8String; - settings.advisory_script_uri = uri.UTF8String; - - // Create the shell. This is a blocking operation. - _shell = shell::Shell::Create( - std::move(task_runners), // task runners - std::move(settings), // settings - std::bind(&CreateHeadlessPlatformView, std::placeholders::_1), // platform view creation - std::bind(&CreateHeadlessRasterizer, std::placeholders::_1) // rasterzier creation - ); - - if (_shell == nullptr) { - FML_LOG(ERROR) << "Could not start a shell for the headless dart runner with entrypoint: " - << entrypoint.UTF8String; - return; - } - - FlutterDartProject* project = [[[FlutterDartProject alloc] init] autorelease]; - - auto config = project.runConfiguration; - config.SetEntrypointAndLibrary(entrypoint.UTF8String, uri.UTF8String); - - // Override the default run configuration with the specified entrypoint. - _shell->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = _shell->GetEngine(), config = std::move(config)]() mutable { - BOOL success = NO; - FML_LOG(INFO) << "Attempting to launch background engine configuration..."; - if (!engine || engine->Run(std::move(config)) == shell::Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Could not launch engine with configuration."; - } else { - FML_LOG(INFO) << "Background Isolate successfully started and run."; - success = YES; - } - })); -} - -- (void)runWithEntrypoint:(NSString*)entrypoint { - [self runWithEntrypointAndLibraryUri:entrypoint libraryUri:nil]; +- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil { + return [self initWithName:labelPrefix project:projectOrNil allowHeadlessExecution:YES]; } -#pragma mark - FlutterBinaryMessenger - -- (void)sendOnChannel:(NSString*)channel message:(NSData*)message { - [self sendOnChannel:channel message:message binaryReply:nil]; -} - -- (void)sendOnChannel:(NSString*)channel - message:(NSData*)message - binaryReply:(FlutterBinaryReply)callback { - NSAssert(channel, @"The channel must not be null"); - fml::RefPtr response = - (callback == nil) ? nullptr - : fml::MakeRefCounted( - ^(NSData* reply) { - callback(reply); - }, - _shell->GetTaskRunners().GetPlatformTaskRunner()); - fml::RefPtr platformMessage = - (message == nil) ? fml::MakeRefCounted(channel.UTF8String, response) - : fml::MakeRefCounted( - channel.UTF8String, shell::GetVectorFromNSData(message), response); - - _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); +- (instancetype)initWithName:(NSString*)labelPrefix + project:(FlutterDartProject*)projectOrNil + allowHeadlessExecution:(BOOL)allowHeadlessExecution { + NSAssert(allowHeadlessExecution == YES, + @"Cannot initialize a FlutterHeadlessDartRunner without headless execution."); + return [super initWithName:labelPrefix + project:projectOrNil + allowHeadlessExecution:allowHeadlessExecution]; } - -- (void)setMessageHandlerOnChannel:(NSString*)channel - binaryMessageHandler:(FlutterBinaryMessageHandler)handler { - reinterpret_cast(_shell->GetPlatformView().get()) - ->GetPlatformMessageRouter() - .SetMessageHandler(channel.UTF8String, handler); +- (instancetype)init { + return [self initWithName:@"io.flutter.headless" project:nil]; } - @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm b/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm deleted file mode 100644 index 2ca2978a548f1..0000000000000 --- a/shell/platform/darwin/ios/framework/Source/FlutterNavigationController.mm +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2017 The Chromium 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/shell/platform/darwin/ios/framework/Headers/FlutterNavigationController.h" - -@implementation FlutterNavigationController - -- (void)viewWillAppear:(BOOL)animated { - [self setNavigationBarHidden:YES]; - [super viewWillAppear:animated]; -} - -@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h index fbcc9c8b0235a..68f97c6b6698e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,4 +11,4 @@ @end -#endif // FLUTTER_FLUTTEROBSERVATORYPUBLISHER_H_ \ No newline at end of file +#endif // FLUTTER_FLUTTEROBSERVATORYPUBLISHER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm index 495a7c72ac8f3..c0add5502389c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm @@ -1,9 +1,19 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +#import + +// NSNetService works fine on physical devices, but doesn't expose the services to regular mDNS +// queries on the Simulator. We can work around this by using the lower level C API, but that's +// only available from iOS 9.3+/macOS 10.11.4+. +#if TARGET_IPHONE_SIMULATOR +#include // nogncheck +#include // nogncheck +#endif // TARGET_IPHONE_SIMLUATOR + #import "FlutterObservatoryPublisher.h" #include "flutter/fml/logging.h" @@ -26,7 +36,11 @@ @interface FlutterObservatoryPublisher () @end @implementation FlutterObservatoryPublisher { +#if TARGET_IPHONE_SIMULATOR + DNSServiceRef _dnsServiceRef; +#else // TARGET_IPHONE_SIMULATOR fml::scoped_nsobject _netService; +#endif // TARGET_IPHONE_SIMULATOR blink::DartServiceIsolate::CallbackHandle _callbackHandle; std::unique_ptr> _weakFactory; @@ -53,15 +67,27 @@ - (instancetype)init { return self; } -- (void)dealloc { +- (void)stopService { +#if TARGET_IPHONE_SIMULATOR + if (_dnsServiceRef) { + DNSServiceRefDeallocate(_dnsServiceRef); + _dnsServiceRef = NULL; + } +#else // TARGET_IPHONE_SIMULATOR [_netService.get() stop]; +#endif // TARGET_IPHONE_SIMULATOR +} + +- (void)dealloc { + [self stopService]; + blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); [super dealloc]; } - (void)publishServiceProtocolPort:(std::string)uri { + [self stopService]; if (uri.empty()) { - [_netService.get() stop]; return; } // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port @@ -69,24 +95,33 @@ - (void)publishServiceProtocolPort:(std::string)uri { NSURL* url = [[[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]] autorelease]; - // DNS name has to be a max of 63 bytes. Prefer to cut off the app name rather than - // the device hostName. e.g. 'io.flutter.example@someones-iphone', or - // 'ongAppNameBecauseThisCouldHappenAtSomePoint@somelongname-iphone' - NSString* serviceName = [NSString - stringWithFormat:@"%@@%@", - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"], - [NSProcessInfo processInfo].hostName]; - if ([serviceName length] > 63) { - serviceName = [serviceName substringFromIndex:[serviceName length] - 63]; - } + NSString* serviceName = + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; + +#if TARGET_IPHONE_SIMULATOR + DNSServiceFlags flags = kDNSServiceFlagsDefault; + uint32_t interfaceIndex = if_nametoindex("lo0"); + const char* registrationType = "_dartobservatory._tcp"; + const char* domain = "local."; // default domain + uint16_t port = [[url port] intValue]; + int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, [serviceName UTF8String], + registrationType, domain, NULL, htons(port), 0, NULL, + registrationCallback, NULL); + + if (err != 0) { + FML_LOG(ERROR) << "Failed to register observatory port with mDNS."; + } else { + DNSServiceSetDispatchQueue(_dnsServiceRef, dispatch_get_main_queue()); + } +#else // TARGET_IPHONE_SIMULATOR _netService.reset([[NSNetService alloc] initWithDomain:@"local." type:@"_dartobservatory._tcp." name:serviceName port:[[url port] intValue]]); - [_netService.get() setDelegate:self]; [_netService.get() publish]; +#endif // TARGET_IPHONE_SIMULATOR } - (void)netServiceDidPublish:(NSNetService*)sender { @@ -98,6 +133,23 @@ - (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict "network settings and relaunch the application."; } +#if TARGET_IPHONE_SIMULATOR +static void DNSSD_API registrationCallback(DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char* name, + const char* regType, + const char* domain, + void* context) { + if (errorCode == kDNSServiceErr_NoError) { + FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!"; + } else { + FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " + "network settings and relaunch the application."; + } +} +#endif // TARGET_IPHONE_SIMULATOR + #endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE != // FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h new file mode 100644 index 0000000000000..7332e46115313 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h @@ -0,0 +1,33 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_OVERLAY_VIEW_H_ +#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_OVERLAY_VIEW_H_ + +#include + +#include + +#import "FlutterPlatformViews_Internal.h" + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_surface.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" + +@interface FlutterOverlayView : UIView + +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; + +- (instancetype)init NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithContentsScale:(CGFloat)contentsScale; +- (std::unique_ptr)createSoftwareSurface; +- (std::unique_ptr)createGLSurfaceWithContext: + (std::shared_ptr)gl_context; + +@end + +#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_OVERLAY_VIEW_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm new file mode 100644 index 0000000000000..394ae89550150 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -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/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" + +#include "flutter/common/settings.h" +#include "flutter/common/task_runners.h" +#include "flutter/flow/layers/layer_tree.h" +#include "flutter/fml/platform/darwin/cf_utils.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_software.h" +#include "third_party/skia/include/utils/mac/SkCGUtils.h" + +// This is mostly a duplication of FlutterView. +// TODO(amirh): once GL support is in evaluate if we can merge this with FlutterView. +@implementation FlutterOverlayView + +- (instancetype)initWithFrame:(CGRect)frame { + @throw([NSException exceptionWithName:@"FlutterOverlayView must init or initWithContentsScale" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithCoder:(NSCoder*)aDecoder { + @throw([NSException exceptionWithName:@"FlutterOverlayView must init or initWithContentsScale" + reason:nil + userInfo:nil]); +} + +- (instancetype)init { + self = [super initWithFrame:CGRectZero]; + + if (self) { + self.layer.opaque = NO; + self.userInteractionEnabled = NO; + } + + return self; +} + +- (instancetype)initWithContentsScale:(CGFloat)contentsScale { + self = [self init]; + + if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { + CAEAGLLayer* layer = reinterpret_cast(self.layer); + layer.allowsGroupOpacity = NO; + layer.contentsScale = contentsScale; + layer.rasterizationScale = contentsScale; + } + + return self; +} + ++ (Class)layerClass { +#if TARGET_IPHONE_SIMULATOR + return [CALayer class]; +#else // TARGET_IPHONE_SIMULATOR + return [CAEAGLLayer class]; +#endif // TARGET_IPHONE_SIMULATOR +} + +- (std::unique_ptr)createSoftwareSurface { + fml::scoped_nsobject layer(reinterpret_cast([self.layer retain])); + return std::make_unique(std::move(layer), nullptr); +} + +- (std::unique_ptr)createGLSurfaceWithContext: + (std::shared_ptr)gl_context { + fml::scoped_nsobject eagl_layer(reinterpret_cast([self.layer retain])); + // TODO(amirh): We can lower this to iOS 8.0 once we have a Metal rendering backend. + // https://github.com/flutter/flutter/issues/24132 + if (@available(iOS 9.0, *)) { + eagl_layer.get().presentsWithTransaction = YES; + } + return std::make_unique(eagl_layer, std::move(gl_context)); +} + +// TODO(amirh): implement drawLayer to suppoer snapshotting. + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h index 09bdfa8b0ea47..d1558a1cfdd51 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,13 +7,12 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" - -#include +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" @interface FlutterPlatformPlugin : NSObject - (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithViewController:(fml::WeakPtr)viewController - NS_DESIGNATED_INITIALIZER; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithEngine:(fml::WeakPtr)engine NS_DESIGNATED_INITIALIZER; - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 54873532ae18b..bfb3032dcc267 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -33,21 +33,21 @@ using namespace shell; @implementation FlutterPlatformPlugin { - fml::WeakPtr _viewController; + fml::WeakPtr _engine; } - (instancetype)init { - @throw([NSException exceptionWithName:@"FlutterPlatformPlugin must initWithViewController" + @throw([NSException exceptionWithName:@"FlutterPlatformPlugin must initWithEngine" reason:nil userInfo:nil]); } -- (instancetype)initWithViewController:(fml::WeakPtr)viewController { - FML_DCHECK(viewController) << "viewController must be set"; +- (instancetype)initWithEngine:(fml::WeakPtr)engine { + FML_DCHECK(engine) << "engine must be set"; self = [super init]; if (self) { - _viewController = viewController; + _engine = engine; } return self; @@ -203,8 +203,11 @@ - (void)popSystemNavigator { UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; - } else if (viewController != _viewController.get()) { - [_viewController.get() dismissViewControllerAnimated:NO completion:nil]; + } else { + auto engineViewController = static_cast([_engine.get() viewController]); + if (engineViewController != viewController) { + [engineViewController dismissViewControllerAnimated:NO completion:nil]; + } } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm new file mode 100644 index 0000000000000..d3c42ed446b07 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -0,0 +1,483 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#import "FlutterOverlayView.h" +#import "flutter/shell/platform/darwin/ios/ios_surface.h" +#import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" + +#include +#include +#include + +#include "FlutterPlatformViews_Internal.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" + +namespace shell { + +void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { + flutter_view_.reset([flutter_view retain]); +} + +void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult& result) { + if ([[call method] isEqualToString:@"create"]) { + OnCreate(call, result); + } else if ([[call method] isEqualToString:@"dispose"]) { + OnDispose(call, result); + } else if ([[call method] isEqualToString:@"acceptGesture"]) { + OnAcceptGesture(call, result); + } else if ([[call method] isEqualToString:@"rejectGesture"]) { + OnRejectGesture(call, result); + } else { + result(FlutterMethodNotImplemented); + } +} + +void FlutterPlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult& result) { + if (!flutter_view_.get()) { + // Right now we assume we have a reference to FlutterView when creating a new view. + // TODO(amirh): support this by setting the refernce to FlutterView when it becomes available. + // https://github.com/flutter/flutter/issues/23787 + result([FlutterError errorWithCode:@"create_failed" + message:@"can't create a view on a headless engine" + details:nil]); + return; + } + NSDictionary* args = [call arguments]; + + long viewId = [args[@"id"] longValue]; + std::string viewType([args[@"viewType"] UTF8String]); + + if (views_.count(viewId) != 0) { + result([FlutterError errorWithCode:@"recreating_view" + message:@"trying to create an already created view" + details:[NSString stringWithFormat:@"view id: '%ld'", viewId]]); + } + + NSObject* factory = factories_[viewType].get(); + if (factory == nil) { + result([FlutterError errorWithCode:@"unregistered_view_type" + message:@"trying to create a view with an unregistered type" + details:[NSString stringWithFormat:@"unregistered view type: '%@'", + args[@"viewType"]]]); + return; + } + + id params = nil; + if ([factory respondsToSelector:@selector(createArgsCodec)]) { + NSObject* codec = [factory createArgsCodec]; + if (codec != nil && args[@"params"] != nil) { + FlutterStandardTypedData* paramsData = args[@"params"]; + params = [codec decode:paramsData.data]; + } + } + + NSObject* embedded_view = [factory createWithFrame:CGRectZero + viewIdentifier:viewId + arguments:params]; + views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); + + FlutterTouchInterceptingView* touch_interceptor = + [[[FlutterTouchInterceptingView alloc] initWithEmbeddedView:embedded_view.view + flutterView:flutter_view_] autorelease]; + + touch_interceptors_[viewId] = + fml::scoped_nsobject([touch_interceptor retain]); + + result(nil); +} + +void FlutterPlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult& result) { + NSNumber* arg = [call arguments]; + int64_t viewId = [arg longLongValue]; + + if (views_.count(viewId) == 0) { + result([FlutterError errorWithCode:@"unknown_view" + message:@"trying to dispose an unknown" + details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; + } + + UIView* touch_interceptor = touch_interceptors_[viewId].get(); + [touch_interceptor removeFromSuperview]; + views_.erase(viewId); + touch_interceptors_.erase(viewId); + overlays_.erase(viewId); + result(nil); +} + +void FlutterPlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, + FlutterResult& result) { + NSDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longLongValue]; + + if (views_.count(viewId) == 0) { + result([FlutterError errorWithCode:@"unknown_view" + message:@"trying to set gesture state for an unknown view" + details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; + } + + FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + [view releaseGesture]; + + result(nil); +} + +void FlutterPlatformViewsController::OnRejectGesture(FlutterMethodCall* call, + FlutterResult& result) { + NSDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longLongValue]; + + if (views_.count(viewId) == 0) { + result([FlutterError errorWithCode:@"unknown_view" + message:@"trying to set gesture state for an unknown view" + details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; + } + + FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + [view blockGesture]; + + result(nil); +} + +void FlutterPlatformViewsController::RegisterViewFactory( + NSObject* factory, + NSString* factoryId) { + std::string idString([factoryId UTF8String]); + FML_CHECK(factories_.count(idString) == 0); + factories_[idString] = + fml::scoped_nsobject>([factory retain]); +} + +void FlutterPlatformViewsController::SetFrameSize(SkISize frame_size) { + frame_size_ = frame_size; +} + +void FlutterPlatformViewsController::PrerollCompositeEmbeddedView(int view_id) { + picture_recorders_[view_id] = std::make_unique(); + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); + picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); + composition_order_.push_back(view_id); +} + +NSObject* FlutterPlatformViewsController::GetPlatformViewByID(int view_id) { + if (views_.empty()) { + return nil; + } + return views_[view_id].get(); +} + +std::vector FlutterPlatformViewsController::GetCurrentCanvases() { + std::vector canvases; + for (size_t i = 0; i < composition_order_.size(); i++) { + int64_t view_id = composition_order_[i]; + canvases.push_back(picture_recorders_[view_id]->getRecordingCanvas()); + } + return canvases; +} + +SkCanvas* FlutterPlatformViewsController::CompositeEmbeddedView( + int view_id, + const flow::EmbeddedViewParams& params) { + // TODO(amirh): assert that this is running on the platform thread once we support the iOS + // embedded views thread configuration. + // TODO(amirh): do nothing if the params didn't change. + CGFloat screenScale = [[UIScreen mainScreen] scale]; + CGRect rect = + CGRectMake(params.offsetPixels.x() / screenScale, params.offsetPixels.y() / screenScale, + params.sizePoints.width(), params.sizePoints.height()); + + UIView* touch_interceptor = touch_interceptors_[view_id].get(); + [touch_interceptor setFrame:rect]; + + return picture_recorders_[view_id]->getRecordingCanvas(); +} + +void FlutterPlatformViewsController::Reset() { + UIView* flutter_view = flutter_view_.get(); + for (UIView* sub_view in [flutter_view subviews]) { + [sub_view removeFromSuperview]; + } + views_.clear(); + overlays_.clear(); + composition_order_.clear(); + active_composition_order_.clear(); + picture_recorders_.clear(); +} + +bool FlutterPlatformViewsController::SubmitFrame(bool gl_rendering, + GrContext* gr_context, + std::shared_ptr gl_context) { + bool did_submit = true; + for (size_t i = 0; i < composition_order_.size(); i++) { + int64_t view_id = composition_order_[i]; + if (gl_rendering) { + EnsureGLOverlayInitialized(view_id, gl_context, gr_context); + } else { + EnsureOverlayInitialized(view_id); + } + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); + SkCanvas* canvas = frame->SkiaCanvas(); + canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); + canvas->flush(); + did_submit &= frame->Submit(); + } + picture_recorders_.clear(); + if (composition_order_ == active_composition_order_) { + composition_order_.clear(); + return did_submit; + } + DetachUnusedLayers(); + active_composition_order_.clear(); + UIView* flutter_view = flutter_view_.get(); + + for (size_t i = 0; i < composition_order_.size(); i++) { + int view_id = composition_order_[i]; + UIView* intercepter = touch_interceptors_[view_id].get(); + UIView* overlay = overlays_[view_id]->overlay_view; + FML_CHECK(intercepter.superview == overlay.superview); + + if (intercepter.superview == flutter_view) { + [flutter_view bringSubviewToFront:intercepter]; + [flutter_view bringSubviewToFront:overlay]; + } else { + [flutter_view addSubview:intercepter]; + [flutter_view addSubview:overlay]; + } + + active_composition_order_.push_back(view_id); + } + composition_order_.clear(); + return did_submit; +} + +void FlutterPlatformViewsController::DetachUnusedLayers() { + std::unordered_set composition_order_set; + + for (int64_t view_id : composition_order_) { + composition_order_set.insert(view_id); + } + + for (int64_t view_id : active_composition_order_) { + if (composition_order_set.find(view_id) == composition_order_set.end()) { + [touch_interceptors_[view_id].get() removeFromSuperview]; + [overlays_[view_id]->overlay_view.get() removeFromSuperview]; + } + } +} + +void FlutterPlatformViewsController::EnsureOverlayInitialized(int64_t overlay_id) { + if (overlays_.count(overlay_id) != 0) { + return; + } + FlutterOverlayView* overlay_view = [[FlutterOverlayView alloc] init]; + overlay_view.frame = flutter_view_.get().bounds; + overlay_view.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = overlay_view.createSoftwareSurface; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + overlays_[overlay_id] = std::make_unique( + fml::scoped_nsobject(overlay_view), std::move(ios_surface), std::move(surface)); +} + +void FlutterPlatformViewsController::EnsureGLOverlayInitialized( + int64_t overlay_id, + std::shared_ptr gl_context, + GrContext* gr_context) { + if (overlays_.count(overlay_id) != 0) { + if (gr_context != overlays_gr_context_) { + overlays_gr_context_ = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurfaceGL* ios_surface_gl = (IOSSurfaceGL*)overlays_[overlay_id]->ios_surface.get(); + std::unique_ptr surface = ios_surface_gl->CreateSecondaryGPUSurface(gr_context); + overlays_[overlay_id]->surface = std::move(surface); + } + return; + } + auto contentsScale = flutter_view_.get().layer.contentsScale; + FlutterOverlayView* overlay_view = + [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]; + overlay_view.frame = flutter_view_.get().bounds; + overlay_view.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view createGLSurfaceWithContext:std::move(gl_context)]; + std::unique_ptr surface = ios_surface->CreateSecondaryGPUSurface(gr_context); + overlays_[overlay_id] = std::make_unique( + fml::scoped_nsobject(overlay_view), std::move(ios_surface), std::move(surface)); + overlays_gr_context_ = gr_context; +} + +} // namespace shell + +// This recognizers delays touch events from being dispatched to the responder chain until it failed +// recognizing a gesture. +// +// We only fail this recognizer when asked to do so by the Flutter framework (which does so by +// invoking an acceptGesture method on the platform_views channel). And this is how we allow the +// Flutter framework to delay or prevent the embedded view from getting a touch sequence. +@interface DelayingGestureRecognizer : UIGestureRecognizer +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer; +@end + +// While the DelayingGestureRecognizer is preventing touches from hitting the responder chain +// the touch events are not arriving to the FlutterView (and thus not arriving to the Flutter +// framework). We use this gesture recognizer to dispatch the events directly to the FlutterView +// while during this phase. +// +// If the Flutter framework decides to dispatch events to the embedded view, we fail the +// DelayingGestureRecognizer which sends the events up the responder chain. But since the events +// are handled by the embedded view they are not delivered to the Flutter framework in this phase +// as well. So during this phase as well the ForwardingGestureRecognizer dispatched the events +// directly to the FlutterView. +@interface ForwardingGestureRecognizer : UIGestureRecognizer +- (instancetype)initWithTarget:(id)target flutterView:(UIView*)flutterView; +@end + +@implementation FlutterTouchInterceptingView { + fml::scoped_nsobject _delayingRecognizer; +} +- (instancetype)initWithEmbeddedView:(UIView*)embeddedView flutterView:(UIView*)flutterView { + self = [super initWithFrame:embeddedView.frame]; + if (self) { + self.multipleTouchEnabled = YES; + embeddedView.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + + [self addSubview:embeddedView]; + + ForwardingGestureRecognizer* forwardingRecognizer = + [[[ForwardingGestureRecognizer alloc] initWithTarget:self + flutterView:flutterView] autorelease]; + + _delayingRecognizer.reset([[DelayingGestureRecognizer alloc] + initWithTarget:self + action:nil + forwardingRecognizer:forwardingRecognizer]); + + [self addGestureRecognizer:_delayingRecognizer.get()]; + [self addGestureRecognizer:forwardingRecognizer]; + } + return self; +} + +- (void)releaseGesture { + _delayingRecognizer.get().state = UIGestureRecognizerStateFailed; +} + +- (void)blockGesture { + _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; +} + +// We want the intercepting view to consume the touches and not pass the touches up to the parent +// view. Make the touch event method not call super will not pass the touches up to the parent view. +// Hence we overide the touch event methods and do nothing. +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { +} + +@end + +@implementation DelayingGestureRecognizer { + fml::scoped_nsobject _forwardingRecognizer; +} + +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer { + self = [super initWithTarget:target action:action]; + if (self) { + self.delaysTouchesBegan = YES; + self.delegate = self; + _forwardingRecognizer.reset([forwardingRecognizer retain]); + } + return self; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { + // The forwarding gesture recognizer should always get all touch events, so it should not be + // required to fail by any other gesture recognizer. + return otherGestureRecognizer != _forwardingRecognizer.get() && otherGestureRecognizer != self; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { + return otherGestureRecognizer == self; +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + self.state = UIGestureRecognizerStateFailed; +} +@end + +@implementation ForwardingGestureRecognizer { + // We can't dispatch events to the framework without this back pointer. + // This is a weak reference, the ForwardingGestureRecognizer is owned by the + // FlutterTouchInterceptingView which is strong referenced only by the FlutterView. + // So this is safe as when FlutterView is deallocated the reference to ForwardingGestureRecognizer + // will go away. + UIView* _flutterView; + // Counting the pointers that has started in one touch sequence. + NSInteger _currentTouchPointersCount; +} + +- (instancetype)initWithTarget:(id)target flutterView:(UIView*)flutterView { + self = [super initWithTarget:target action:nil]; + if (self) { + self.delegate = self; + _flutterView = flutterView; + _currentTouchPointersCount = 0; + } + return self; +} + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + [_flutterView touchesBegan:touches withEvent:event]; + _currentTouchPointersCount += touches.count; +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { + [_flutterView touchesMoved:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + [_flutterView touchesEnded:touches withEvent:event]; + _currentTouchPointersCount -= touches.count; + // Touches in one touch sequence are sent to the touchesEnded method separately if different + // fingers stop touching the screen at different time. So one touchesEnded method triggering does + // not necessarially mean the touch sequence has ended. We Only set the state to + // UIGestureRecognizerStateFailed when all the touches in the current touch sequence is ended. + if (_currentTouchPointersCount == 0) { + self.state = UIGestureRecognizerStateFailed; + } +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + [_flutterView touchesCancelled:touches withEvent:event]; + _currentTouchPointersCount = 0; + self.state = UIGestureRecognizerStateFailed; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldRecognizeSimultaneouslyWithGestureRecognizer: + (UIGestureRecognizer*)otherGestureRecognizer { + return YES; +} +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h new file mode 100644 index 0000000000000..a0ebbac98b302 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -0,0 +1,119 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" + +// A UIView that is used as the parent for embedded UIViews. +// +// This view has 2 roles: +// 1. Delay or prevent touch events from arriving the embedded view. +// 2. Dispatching all events that are hittested to the embedded view to the FlutterView. +@interface FlutterTouchInterceptingView : UIView +- (instancetype)initWithEmbeddedView:(UIView*)embeddedView flutterView:(UIView*)flutterView; + +// Stop delaying any active touch sequence (and let it arrive the embedded view). +- (void)releaseGesture; + +// Prevent the touch sequence from ever arriving to the embedded view. +- (void)blockGesture; +@end + +namespace shell { + +class IOSGLContext; +class IOSSurface; + +struct FlutterPlatformViewLayer { + FlutterPlatformViewLayer(fml::scoped_nsobject overlay_view, + std::unique_ptr ios_surface, + std::unique_ptr surface); + + ~FlutterPlatformViewLayer(); + + fml::scoped_nsobject overlay_view; + std::unique_ptr ios_surface; + std::unique_ptr surface; +}; + +class FlutterPlatformViewsController { + public: + FlutterPlatformViewsController(); + + ~FlutterPlatformViewsController(); + + void SetFlutterView(UIView* flutter_view); + + void RegisterViewFactory(NSObject* factory, NSString* factoryId); + + void SetFrameSize(SkISize frame_size); + + void PrerollCompositeEmbeddedView(int view_id); + + // Returns the `FlutterPlatformView` object associated with the view_id. + // + // If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or + // a `FlutterPlatformView` object asscociated with the view_id cannot be found, the method returns + // nil. + NSObject* GetPlatformViewByID(int view_id); + + std::vector GetCurrentCanvases(); + + SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params); + + // Discards all platform views instances and auxiliary resources. + void Reset(); + + bool SubmitFrame(bool gl_rendering, + GrContext* gr_context, + std::shared_ptr gl_context); + + void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); + + private: + fml::scoped_nsobject channel_; + fml::scoped_nsobject flutter_view_; + std::map>> factories_; + std::map>> views_; + std::map> touch_interceptors_; + std::map> overlays_; + // The GrContext that is currently used by all of the overlay surfaces. + // We track this to know when the GrContext for the Flutter app has changed + // so we can update the overlays with the new context. + GrContext* overlays_gr_context_; + SkISize frame_size_; + + // A vector of embedded view IDs according to their composition order. + // The last ID in this vector belond to the that is composited on top of all others. + std::vector composition_order_; + + // The latest composition order that was presented in Present(). + std::vector active_composition_order_; + + std::map> picture_recorders_; + + void OnCreate(FlutterMethodCall* call, FlutterResult& result); + void OnDispose(FlutterMethodCall* call, FlutterResult& result); + void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); + void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); + + void DetachUnusedLayers(); + void EnsureOverlayInitialized(int64_t overlay_id); + void EnsureGLOverlayInitialized(int64_t overlay_id, + std::shared_ptr gl_context, + GrContext* gr_context); + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm new file mode 100644 index 0000000000000..b6c85c60974fb --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -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. + +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" + +#include "flutter/shell/platform/darwin/ios/ios_surface.h" + +namespace shell { +FlutterPlatformViewLayer::FlutterPlatformViewLayer(fml::scoped_nsobject overlay_view, + std::unique_ptr ios_surface, + std::unique_ptr surface) + : overlay_view(std::move(overlay_view)), + ios_surface(std::move(ios_surface)), + surface(std::move(surface)){}; + +FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; + +FlutterPlatformViewsController::FlutterPlatformViewsController() = default; + +FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; + +} // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm index e4d9c03cf9fe1..70b54b0f29231 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -158,6 +158,8 @@ - (void)applicationWillTerminate:(UIApplication*)application { } } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings { for (id plugin in _pluginDelegates) { @@ -169,6 +171,7 @@ - (void)application:(UIApplication*)application } } } +#pragma GCC diagnostic pop - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { @@ -199,6 +202,36 @@ - (void)application:(UIApplication*)application } } +- (void)application:(UIApplication*)application + didReceiveLocalNotification:(UILocalNotification*)notification { + for (id plugin in _pluginDelegates) { + if (!plugin) { + continue; + } + if ([plugin respondsToSelector:_cmd]) { + [plugin application:application didReceiveLocalNotification:notification]; + } + } +} + +- (void)userNotificationCenter:(UNUserNotificationCenter*)center + willPresentNotification:(UNNotification*)notification + withCompletionHandler: + (void (^)(UNNotificationPresentationOptions options))completionHandler { + if (@available(iOS 10.0, *)) { + for (id plugin in _pluginDelegates) { + if (!plugin) { + continue; + } + if ([plugin respondsToSelector:_cmd]) { + [plugin userNotificationCenter:center + willPresentNotification:notification + withCompletionHandler:completionHandler]; + } + } + } +} + - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url options:(NSDictionary*)options { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm index bd25217679c9b..d7c151bfa7562 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -265,36 +265,37 @@ - (void)writeValue:(id)value { if (value == nil || value == [NSNull null]) { [self writeByte:FlutterStandardFieldNil]; } else if ([value isKindOfClass:[NSNumber class]]) { - NSNumber* number = value; - const char* type = [number objCType]; - if ([self isBool:number type:type]) { - BOOL b = number.boolValue; + CFNumberRef number = (CFNumberRef)value; + BOOL success = NO; + if (CFGetTypeID(number) == CFBooleanGetTypeID()) { + BOOL b = CFBooleanGetValue((CFBooleanRef)number); [self writeByte:(b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)]; - } else if (strcmp(type, @encode(signed int)) == 0 || strcmp(type, @encode(signed short)) == 0 || - strcmp(type, @encode(unsigned short)) == 0 || - strcmp(type, @encode(signed char)) == 0 || - strcmp(type, @encode(unsigned char)) == 0) { - SInt32 n = number.intValue; - [self writeByte:FlutterStandardFieldInt32]; - [self writeBytes:(UInt8*)&n length:4]; - } else if (strcmp(type, @encode(signed long)) == 0 || - strcmp(type, @encode(unsigned int)) == 0) { - SInt64 n = number.longValue; - [self writeByte:FlutterStandardFieldInt64]; - [self writeBytes:(UInt8*)&n length:8]; - } else if (strcmp(type, @encode(double)) == 0 || strcmp(type, @encode(float)) == 0) { - Float64 f = number.doubleValue; - [self writeByte:FlutterStandardFieldFloat64]; - [self writeAlignment:8]; - [self writeBytes:(UInt8*)&f length:8]; - } else if (strcmp(type, @encode(unsigned long)) == 0 || - strcmp(type, @encode(signed long long)) == 0 || - strcmp(type, @encode(unsigned long long)) == 0) { - NSString* hex = [NSString stringWithFormat:@"%llx", number.unsignedLongLongValue]; - [self writeByte:FlutterStandardFieldIntHex]; - [self writeUTF8:hex]; - } else { - NSLog(@"Unsupported value: %@ of type %s", value, type); + success = YES; + } else if (CFNumberIsFloatType(number)) { + Float64 f; + success = CFNumberGetValue(number, kCFNumberFloat64Type, &f); + if (success) { + [self writeByte:FlutterStandardFieldFloat64]; + [self writeAlignment:8]; + [self writeBytes:(UInt8*)&f length:8]; + } + } else if (CFNumberGetByteSize(number) <= 4) { + SInt32 n; + success = CFNumberGetValue(number, kCFNumberSInt32Type, &n); + if (success) { + [self writeByte:FlutterStandardFieldInt32]; + [self writeBytes:(UInt8*)&n length:4]; + } + } else if (CFNumberGetByteSize(number) <= 8) { + SInt64 n; + success = CFNumberGetValue(number, kCFNumberSInt64Type, &n); + if (success) { + [self writeByte:FlutterStandardFieldInt64]; + [self writeBytes:(UInt8*)&n length:8]; + } + } + if (!success) { + NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number)); NSAssert(NO, @"Unsupported value for standard codec"); } } else if ([value isKindOfClass:[NSString class]]) { @@ -329,11 +330,6 @@ - (void)writeValue:(id)value { NSAssert(NO, @"Unsupported value for standard codec"); } } - -- (BOOL)isBool:(NSNumber*)number type:(const char*)type { - return strcmp(type, @encode(signed char)) == 0 && - [NSStringFromClass([number class]) isEqual:@"__NSCFBoolean"]; -} @end @implementation FlutterStandardReader { @@ -428,12 +424,12 @@ - (id)readValueOfType:(UInt8)type { case FlutterStandardFieldInt32: { SInt32 value; [self readBytes:&value length:4]; - return [NSNumber numberWithInt:value]; + return @(value); } case FlutterStandardFieldInt64: { SInt64 value; [self readBytes:&value length:8]; - return [NSNumber numberWithLong:value]; + return @(value); } case FlutterStandardFieldFloat64: { Float64 value; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h index 24197e1ea8d6a..c1eab20fa1ac3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterStandardCodec_Internal.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index 3136eb08ccafe..d3451b5bd4cd4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,10 +21,19 @@ typedef NS_ENUM(NSInteger, FlutterTextInputAction) { FlutterTextInputActionNewline, }; -@protocol FlutterTextInputDelegate +typedef NS_ENUM(NSInteger, FlutterFloatingCursorDragState) { + FlutterFloatingCursorDragStateStart, + FlutterFloatingCursorDragStateUpdate, + FlutterFloatingCursorDragStateEnd, +}; + +@protocol FlutterTextInputDelegate - (void)updateEditingClient:(int)client withState:(NSDictionary*)state; - (void)performAction:(FlutterTextInputAction)action withClient:(int)client; +- (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state + withClient:(int)client + withPosition:(NSDictionary*)point; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h index 198381e4ebbce..9283dd9b809a3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -36,7 +36,7 @@ @end /** A range of text in the buffer of a Flutter text editing widget. */ -@interface FlutterTextRange : UITextRange +@interface FlutterTextRange : UITextRange @property(nonatomic, readonly) NSRange range; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index da64123432f93..dd8971f402e58 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,9 @@ static UIKeyboardType ToUIKeyboardType(NSDictionary* type) { if ([inputType isEqualToString:@"TextInputType.number"]) { if ([type[@"signed"] boolValue]) return UIKeyboardTypeNumbersAndPunctuation; - return UIKeyboardTypeDecimalPad; + if ([type[@"decimal"] boolValue]) + return UIKeyboardTypeDecimalPad; + return UIKeyboardTypeNumberPad; } if ([inputType isEqualToString:@"TextInputType.phone"]) return UIKeyboardTypePhonePad; @@ -216,6 +218,14 @@ - (void)setTextInputState:(NSDictionary*)state { [self.text setString:newText]; } + NSInteger composingBase = [state[@"composingBase"] intValue]; + NSInteger composingExtent = [state[@"composingExtent"] intValue]; + NSRange composingRange = [self clampSelection:NSMakeRange(MIN(composingBase, composingExtent), + ABS(composingBase - composingExtent)) + forText:self.text]; + self.markedTextRange = + composingRange.length > 0 ? [FlutterTextRange rangeWithNSRange:composingRange] : nil; + NSInteger selectionBase = [state[@"selectionBase"] intValue]; NSInteger selectionExtent = [state[@"selectionExtent"] intValue]; NSRange selectedRange = [self clampSelection:NSMakeRange(MIN(selectionBase, selectionExtent), @@ -233,14 +243,6 @@ - (void)setTextInputState:(NSDictionary*)state { [self.inputDelegate selectionDidChange:self]; } - NSInteger composingBase = [state[@"composingBase"] intValue]; - NSInteger composingExtent = [state[@"composingExtent"] intValue]; - NSRange composingRange = [self clampSelection:NSMakeRange(MIN(composingBase, composingExtent), - ABS(composingBase - composingExtent)) - forText:self.text]; - self.markedTextRange = - composingRange.length > 0 ? [FlutterTextRange rangeWithNSRange:composingRange] : nil; - if (textChanged) { [self.inputDelegate textDidChange:self]; @@ -289,6 +291,13 @@ - (void)setSelectedTextRange:(UITextRange*)selectedTextRange updateEditingState: } } +- (id)insertDictationResultPlaceholder { + return @""; +} + +- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult { +} + - (NSString*)textInRange:(UITextRange*)range { NSRange textRange = ((FlutterTextRange*)range).range; return [self.text substringWithRange:textRange]; @@ -297,7 +306,6 @@ - (NSString*)textInRange:(UITextRange*)range { - (void)replaceRange:(UITextRange*)range withText:(NSString*)text { NSRange replaceRange = ((FlutterTextRange*)range).range; NSRange selectedRange = _selectedTextRange.range; - // Adjust the text selection: // * reduce the length by the intersection length // * adjust the location by newLength - oldLength + intersectionLength @@ -560,6 +568,24 @@ - (UITextRange*)characterRangeAtPoint:(CGPoint)point { return [FlutterTextRange rangeWithNSRange:[self rangeForCharacterAtIndex:currentIndex]]; } +- (void)beginFloatingCursorAtPoint:(CGPoint)point { + [_textInputDelegate updateFloatingCursor:FlutterFloatingCursorDragStateStart + withClient:_textInputClient + withPosition:@{@"X" : @(point.x), @"Y" : @(point.y)}]; +} + +- (void)updateFloatingCursorAtPoint:(CGPoint)point { + [_textInputDelegate updateFloatingCursor:FlutterFloatingCursorDragStateUpdate + withClient:_textInputClient + withPosition:@{@"X" : @(point.x), @"Y" : @(point.y)}]; +} + +- (void)endFloatingCursor { + [_textInputDelegate updateFloatingCursor:FlutterFloatingCursorDragStateEnd + withClient:_textInputClient + withPosition:@{@"X" : @(0), @"Y" : @(0)}]; +} + #pragma mark - UIKeyInput Overrides - (void)updateEditingState { @@ -595,6 +621,27 @@ - (void)insertText:(NSString*)text { - (void)deleteBackward { _selectionAffinity = _kTextAffinityDownstream; + + // When deleting Thai vowel, _selectedTextRange has location + // but does not have length, so we have to manually set it. + // In addition, we needed to delete only a part of grapheme cluster + // because it is the expected behavior of Thai input. + // https://github.com/flutter/flutter/issues/24203 + // https://github.com/flutter/flutter/issues/21745 + // + // This is needed for correct handling of the deletion of Thai vowel input. + // TODO(cbracken): Get a good understanding of expected behaviour of Thai + // input and ensure that this is the correct solution. + // https://github.com/flutter/flutter/issues/28962 + if (_selectedTextRange.isEmpty && [self hasText]) { + NSRange oldRange = ((FlutterTextRange*)_selectedTextRange).range; + if (oldRange.location > 0) { + NSRange newRange = NSMakeRange(oldRange.location - 1, 1); + [self setSelectedTextRange:[FlutterTextRange rangeWithNSRange:newRange] + updateEditingState:false]; + } + } + if (!_selectedTextRange.isEmpty) [self replaceRange:_selectedTextRange withText:@""]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m b/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m index dbdce3c596da9..b567d90dadee4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index 5e3d303401725..73f597f5cfafa 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,10 +9,30 @@ #include +#import "FlutterPlatformViews_Internal.h" + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" +@protocol FlutterViewEngineDelegate + +- (shell::Rasterizer::Screenshot)takeScreenshot:(shell::Rasterizer::ScreenshotType)type + asBase64Encoded:(BOOL)base64Encode; + +- (shell::FlutterPlatformViewsController*)platformViewsController; + +@end + @interface FlutterView : UIView +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; +- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE; + +- (instancetype)initWithDelegate:(id)delegate + opaque:(BOOL)opaque NS_DESIGNATED_INITIALIZER; - (std::unique_ptr)createSurface; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 32c54f632992f..f60f114f2ea1c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,7 +12,6 @@ #include "flutter/fml/trace_event.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" -#include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_surface_software.h" @@ -24,28 +23,42 @@ @interface FlutterView () @implementation FlutterView -- (FlutterViewController*)flutterViewController { - // Find the first view controller in the responder chain and see if it is a FlutterViewController. - for (UIResponder* responder = self.nextResponder; responder != nil; - responder = responder.nextResponder) { - if ([responder isKindOfClass:[UIViewController class]]) { - if ([responder isKindOfClass:[FlutterViewController class]]) { - return reinterpret_cast(responder); - } else { - // Should only happen if a non-FlutterViewController tries to somehow (via dynamic class - // resolution or reparenting) set a FlutterView as its view. - return nil; - } - } +id _delegate; + +- (instancetype)init { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithFrame:(CGRect)frame { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithCoder:(NSCoder*)aDecoder { + @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" + reason:nil + userInfo:nil]); +} + +- (instancetype)initWithDelegate:(id)delegate opaque:(BOOL)opaque { + FML_DCHECK(delegate) << "Delegate must not be nil."; + self = [super initWithFrame:CGRectNull]; + + if (self) { + _delegate = delegate; + self.layer.opaque = opaque; } - return nil; + + return self; } - (void)layoutSubviews { if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { CAEAGLLayer* layer = reinterpret_cast(self.layer); layer.allowsGroupOpacity = YES; - layer.opaque = YES; CGFloat screenScale = [UIScreen mainScreen].scale; layer.contentsScale = screenScale; layer.rasterizationScale = screenScale; @@ -66,10 +79,21 @@ + (Class)layerClass { if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { fml::scoped_nsobject eagl_layer( reinterpret_cast([self.layer retain])); - return std::make_unique(std::move(eagl_layer)); + if (shell::IsIosEmbeddedViewsPreviewEnabled()) { + // TODO(amirh): We can lower this to iOS 8.0 once we have a Metal rendering backend. + // https://github.com/flutter/flutter/issues/24132 + if (@available(iOS 9.0, *)) { + // TODO(amirh): only do this if there's an embedded view. + // https://github.com/flutter/flutter/issues/24133 + eagl_layer.get().presentsWithTransaction = YES; + } + } + return std::make_unique(std::move(eagl_layer), + [_delegate platformViewsController]); } else { fml::scoped_nsobject layer(reinterpret_cast([self.layer retain])); - return std::make_unique(std::move(layer)); + return std::make_unique(std::move(layer), + [_delegate platformViewsController]); } } @@ -84,16 +108,8 @@ - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { return; } - FlutterViewController* controller = [self flutterViewController]; - - if (controller == nil) { - return; - } - - auto& shell = [controller shell]; - - auto screenshot = shell.Screenshot(shell::Rasterizer::ScreenshotType::UncompressedImage, - false /* base64 encode */); + auto screenshot = [_delegate takeScreenshot:shell::Rasterizer::ScreenshotType::UncompressedImage + asBase64Encoded:NO]; if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) { return; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 2e81213980423..076e3bec3402b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1,9 +1,10 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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 +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include @@ -13,8 +14,7 @@ #include "flutter/fml/platform/darwin/platform_version.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/thread_host.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" @@ -22,31 +22,11 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -@interface FlutterViewController () -@property(nonatomic, readonly) NSMutableDictionary* pluginPublications; -@end - -@interface FlutterViewControllerRegistrar : NSObject -- (instancetype)initWithPlugin:(NSString*)pluginKey - flutterViewController:(FlutterViewController*)flutterViewController; -@end +NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate"; @implementation FlutterViewController { - fml::scoped_nsobject _dartProject; - shell::ThreadHost _threadHost; - std::unique_ptr _shell; std::unique_ptr> _weakFactory; - - // Channels - fml::scoped_nsobject _platformPlugin; - fml::scoped_nsobject _textInputPlugin; - fml::scoped_nsobject _localizationChannel; - fml::scoped_nsobject _navigationChannel; - fml::scoped_nsobject _platformChannel; - fml::scoped_nsobject _textInputChannel; - fml::scoped_nsobject _lifecycleChannel; - fml::scoped_nsobject _systemChannel; - fml::scoped_nsobject _settingsChannel; + fml::scoped_nsobject _engine; // We keep a separate reference to this and create it ahead of time because we want to be able to // setup a shell along with its platform view before the view has to appear. @@ -56,25 +36,46 @@ @implementation FlutterViewController { UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; blink::ViewportMetrics _viewportMetrics; - int64_t _nextTextureId; BOOL _initialized; - - fml::scoped_nsobject _publisher; + BOOL _viewOpaque; + BOOL _engineNeedsLaunch; } #pragma mark - Manage and override all designated initializers +- (instancetype)initWithEngine:(FlutterEngine*)engine + nibName:(NSString*)nibNameOrNil + bundle:(NSBundle*)nibBundleOrNil { + NSAssert(engine != nil, @"Engine is required"); + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + _viewOpaque = YES; + _engine.reset([engine retain]); + _engineNeedsLaunch = NO; + _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); + _weakFactory = std::make_unique>(self); + + [self performCommonViewControllerInitialization]; + [engine setViewController:self]; + } + + return self; +} + - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil nibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { + _viewOpaque = YES; _weakFactory = std::make_unique>(self); - if (projectOrNil == nil) - _dartProject.reset([[FlutterDartProject alloc] init]); - else - _dartProject.reset([projectOrNil retain]); - + _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter" + project:projectOrNil + allowHeadlessExecution:NO]); + _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); + [_engine.get() createShell:nil libraryURI:nil]; + _engineNeedsLaunch = YES; + [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -93,6 +94,18 @@ - (instancetype)init { return [self initWithProject:nil nibName:nil bundle:nil]; } +- (BOOL)isViewOpaque { + return _viewOpaque; +} + +- (void)setViewOpaque:(BOOL)value { + _viewOpaque = value; + if (_flutterView.get().layer.opaque != value) { + _flutterView.get().layer.opaque = value; + [_flutterView.get().layer setNeedsLayout]; + } +} + #pragma mark - Common view controller initialization tasks - (void)performCommonViewControllerInitialization { @@ -101,134 +114,18 @@ - (void)performCommonViewControllerInitialization { _initialized = YES; - _publisher.reset([[FlutterObservatoryPublisher alloc] init]); - _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; - if ([self setupShell]) { - [self setupChannels]; - [self setupNotificationCenterObservers]; - - _pluginPublications = [NSMutableDictionary new]; - } -} - -- (shell::Shell&)shell { - FML_DCHECK(_shell); - return *_shell; + [self setupNotificationCenterObservers]; } -- (fml::WeakPtr)iosPlatformView { - FML_DCHECK(_shell); - return _shell->GetPlatformView(); +- (FlutterEngine*)engine { + return _engine.get(); } -- (BOOL)setupShell { - FML_DCHECK(_shell == nullptr); - - static size_t shell_count = 1; - - auto threadLabel = [NSString stringWithFormat:@"io.flutter.%zu", shell_count++]; - - _threadHost = { - threadLabel.UTF8String, // label - shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO}; - - // The current thread will be used as the platform thread. Ensure that the message loop is - // initialized. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - blink::TaskRunners task_runners(threadLabel.UTF8String, // label - fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - _threadHost.gpu_thread->GetTaskRunner(), // gpu - _threadHost.ui_thread->GetTaskRunner(), // ui - _threadHost.io_thread->GetTaskRunner() // io - ); - - _flutterView.reset([[FlutterView alloc] init]); - - // Lambda captures by pointers to ObjC objects are fine here because the create call is - // synchronous. - shell::Shell::CreateCallback on_create_platform_view = - [flutter_view_controller = self, flutter_view = _flutterView.get()](shell::Shell& shell) { - auto platform_view_ios = std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - flutter_view_controller, // flutter view controller owner - flutter_view // flutter view owner - ); - return platform_view_ios; - }; - - shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { - return std::make_unique(shell.GetTaskRunners()); - }; - - // Create the shell. - _shell = shell::Shell::Create(std::move(task_runners), // - [_dartProject settings], // - on_create_platform_view, // - on_create_rasterizer // - ); - - if (!_shell) { - FML_LOG(ERROR) << "Could not setup a shell to run the Dart application."; - return false; - } - - return true; -} - -- (void)setupChannels { - _localizationChannel.reset([[FlutterMethodChannel alloc] - initWithName:@"flutter/localization" - binaryMessenger:self - codec:[FlutterJSONMethodCodec sharedInstance]]); - - _navigationChannel.reset([[FlutterMethodChannel alloc] - initWithName:@"flutter/navigation" - binaryMessenger:self - codec:[FlutterJSONMethodCodec sharedInstance]]); - - _platformChannel.reset([[FlutterMethodChannel alloc] - initWithName:@"flutter/platform" - binaryMessenger:self - codec:[FlutterJSONMethodCodec sharedInstance]]); - - _textInputChannel.reset([[FlutterMethodChannel alloc] - initWithName:@"flutter/textinput" - binaryMessenger:self - codec:[FlutterJSONMethodCodec sharedInstance]]); - - _lifecycleChannel.reset([[FlutterBasicMessageChannel alloc] - initWithName:@"flutter/lifecycle" - binaryMessenger:self - codec:[FlutterStringCodec sharedInstance]]); - - _systemChannel.reset([[FlutterBasicMessageChannel alloc] - initWithName:@"flutter/system" - binaryMessenger:self - codec:[FlutterJSONMessageCodec sharedInstance]]); - - _settingsChannel.reset([[FlutterBasicMessageChannel alloc] - initWithName:@"flutter/settings" - binaryMessenger:self - codec:[FlutterJSONMessageCodec sharedInstance]]); - - _platformPlugin.reset( - [[FlutterPlatformPlugin alloc] initWithViewController:_weakFactory->GetWeakPtr()]); - [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [_platformPlugin.get() handleMethodCall:call result:result]; - }]; - - _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]); - _textInputPlugin.get().textInputDelegate = self; - [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [_textInputPlugin.get() handleMethodCall:call result:result]; - }]; - static_cast(_shell->GetPlatformView().get()) - ->SetTextInputPlugin(_textInputPlugin); +- (fml::WeakPtr)getWeakPtr { + return _weakFactory->GetWeakPtr(); } - (void)setupNotificationCenterObservers { @@ -320,15 +217,15 @@ - (void)setupNotificationCenterObservers { } - (void)setInitialRoute:(NSString*)route { - [_navigationChannel.get() invokeMethod:@"setInitialRoute" arguments:route]; + [[_engine.get() navigationChannel] invokeMethod:@"setInitialRoute" arguments:route]; } - (void)popRoute { - [_navigationChannel.get() invokeMethod:@"popRoute" arguments:nil]; + [[_engine.get() navigationChannel] invokeMethod:@"popRoute" arguments:nil]; } - (void)pushRoute:(NSString*)route { - [_navigationChannel.get() invokeMethod:@"pushRoute" arguments:route]; + [[_engine.get() navigationChannel] invokeMethod:@"pushRoute" arguments:route]; } #pragma mark - Loading the view @@ -346,7 +243,7 @@ - (void)loadView { - (void)installSplashScreenViewIfNecessary { // Show the launch screen view again on top of the FlutterView if available. // This launch screen view will be removed once the first Flutter frame is rendered. - if (self.isBeingPresented || self.isMovingToParentViewController) { + if (_splashScreenView && (self.isBeingPresented || self.isMovingToParentViewController)) { [_splashScreenView.get() removeFromSuperview]; _splashScreenView.reset(); return; @@ -381,49 +278,60 @@ - (void)removeSplashScreenViewIfPresent { } - (void)installSplashScreenViewCallback { - if (!_shell || !_splashScreenView) { + if (!_splashScreenView) { return; } - auto weak_platform_view = _shell->GetPlatformView(); + auto weak_platform_view = [_engine.get() platformView]; if (!weak_platform_view) { return; } __unsafe_unretained auto weak_flutter_view_controller = self; // This is on the platform thread. - weak_platform_view->SetNextFrameCallback( - [weak_platform_view, weak_flutter_view_controller, - task_runner = _shell->GetTaskRunners().GetPlatformTaskRunner()]() { - // This is on the GPU thread. - task_runner->PostTask([weak_platform_view, weak_flutter_view_controller]() { - // We check if the weak platform view is alive. If it is alive, then the view controller - // also has to be alive since the view controller owns the platform view via the shell - // association. Thus, we are not convinced that the unsafe unretained weak object is in - // fact alive. - if (weak_platform_view) { - [weak_flutter_view_controller removeSplashScreenViewIfPresent]; - } - }); - }); + weak_platform_view->SetNextFrameCallback([weak_platform_view, weak_flutter_view_controller, + task_runner = [_engine.get() platformTaskRunner]]() { + // This is on the GPU thread. + task_runner->PostTask([weak_platform_view, weak_flutter_view_controller]() { + // We check if the weak platform view is alive. If it is alive, then the view controller + // also has to be alive since the view controller owns the platform view via the shell + // association. Thus, we are not convinced that the unsafe unretained weak object is in + // fact alive. + if (weak_platform_view) { + [weak_flutter_view_controller removeSplashScreenViewIfPresent]; + } + }); + }); } #pragma mark - Properties +- (FlutterView*)flutterView { + return _flutterView; +} + - (UIView*)splashScreenView { - if (_splashScreenView == nullptr) { - NSString* launchscreenName = - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; - if (launchscreenName == nil) { - return nil; - } - UIView* splashView = [self splashScreenFromStoryboard:launchscreenName]; - if (!splashView) { - splashView = [self splashScreenFromXib:launchscreenName]; - } - self.splashScreenView = splashView; + if (!_splashScreenView) { + return nil; } return _splashScreenView.get(); } +- (BOOL)loadDefaultSplashScreenView { + NSString* launchscreenName = + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; + if (launchscreenName == nil) { + return NO; + } + UIView* splashView = [self splashScreenFromStoryboard:launchscreenName]; + if (!splashView) { + splashView = [self splashScreenFromXib:launchscreenName]; + } + if (!splashView) { + return NO; + } + self.splashScreenView = splashView; + return YES; +} + - (UIView*)splashScreenFromStoryboard:(NSString*)name { UIStoryboard* storyboard = nil; @try { @@ -448,6 +356,13 @@ - (UIView*)splashScreenFromXib:(NSString*)name { } - (void)setSplashScreenView:(UIView*)view { + if (!view) { + // Special case: user wants to remove the splash screen view. + [self removeSplashScreenViewIfPresent]; + _splashScreenView.reset(); + return; + } + _splashScreenView.reset([view retain]); _splashScreenView.get().autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -463,10 +378,11 @@ - (void)surfaceUpdated:(BOOL)appeared { // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and GPU thread. if (appeared) { [self installSplashScreenViewCallback]; - _shell->GetPlatformView()->NotifyCreated(); - + [_engine.get() platformViewsController] -> SetFlutterView(_flutterView.get()); + [_engine.get() platformView] -> NotifyCreated(); } else { - _shell->GetPlatformView()->NotifyDestroyed(); + [_engine.get() platformView] -> NotifyDestroyed(); + [_engine.get() platformViewsController] -> SetFlutterView(nullptr); } } @@ -475,24 +391,17 @@ - (void)surfaceUpdated:(BOOL)appeared { - (void)viewWillAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillAppear"); - // Launch the Dart application with the inferred run configuration. - _shell->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = _shell->GetEngine(), // - config = [_dartProject.get() runConfiguration] // - ]() mutable { - if (engine) { - auto result = engine->Run(std::move(config)); - if (result == shell::Engine::RunStatus::Failure) { - FML_LOG(ERROR) << "Could not launch engine with configuration."; - } - } - })); + if (_engineNeedsLaunch) { + [_engine.get() launchEngine:nil libraryURI:nil]; + [_engine.get() setViewController:self]; + _engineNeedsLaunch = NO; + } // Only recreate surface on subsequent appearances when viewport metrics are known. // First time surface creation is done on viewDidLayoutSubviews. if (_viewportMetrics.physical_width) [self surfaceUpdated:YES]; - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; [super viewWillAppear:animated]; } @@ -502,14 +411,14 @@ - (void)viewDidAppear:(BOOL)animated { [self onLocaleUpdated:nil]; [self onUserSettingsChanged:nil]; [self onAccessibilityStatusChanged:nil]; - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"]; [super viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillDisappear"); - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; [super viewWillDisappear:animated]; } @@ -517,14 +426,13 @@ - (void)viewWillDisappear:(BOOL)animated { - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); [self surfaceUpdated:NO]; - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"]; - + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; [super viewDidDisappear:animated]; } - (void)dealloc { + [_engine.get() notifyViewControllerDeallocated]; [[NSNotificationCenter defaultCenter] removeObserver:self]; - [_pluginPublications release]; [super dealloc]; } @@ -532,25 +440,25 @@ - (void)dealloc { - (void)applicationBecameActive:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationBecameActive"); - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"]; + if (_viewportMetrics.physical_width) + [self surfaceUpdated:YES]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"]; } - (void)applicationWillResignActive:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationWillResignActive"); - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; + [self surfaceUpdated:NO]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; } - (void)applicationDidEnterBackground:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationDidEnterBackground"); - [self surfaceUpdated:NO]; - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; } - (void)applicationWillEnterForeground:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationWillEnterForeground"); - if (_viewportMetrics.physical_width) - [self surfaceUpdated:YES]; - [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; + [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; } #pragma mark - Touch event handling @@ -676,12 +584,7 @@ - (void)dispatchTouches:(NSSet*)touches packet->SetPointerData(pointer_index++, pointer_data); } - _shell->GetTaskRunners().GetUITaskRunner()->PostTask( - fml::MakeCopyable([engine = _shell->GetEngine(), packet = std::move(packet)] { - if (engine) { - engine->DispatchPointerDataPacket(*packet); - } - })); + [_engine.get() dispatchPointerDataPacket:std::move(packet)]; } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { @@ -703,12 +606,7 @@ - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { #pragma mark - Handle view resizing - (void)updateViewportMetrics { - _shell->GetTaskRunners().GetUITaskRunner()->PostTask( - [engine = _shell->GetEngine(), metrics = _viewportMetrics]() { - if (engine) { - engine->SetViewportMetrics(std::move(metrics)); - } - }); + [_engine.get() updateViewportMetrics:_viewportMetrics]; } - (CGFloat)statusBarPadding { @@ -788,58 +686,6 @@ - (void)keyboardWillBeHidden:(NSNotification*)notification { [self updateViewportMetrics]; } -#pragma mark - Text input delegate - -- (void)updateEditingClient:(int)client withState:(NSDictionary*)state { - [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" - arguments:@[ @(client), state ]]; -} - -- (void)performAction:(FlutterTextInputAction)action withClient:(int)client { - NSString* actionString; - switch (action) { - case FlutterTextInputActionUnspecified: - // Where did the term "unspecified" come from? iOS has a "default" and Android - // has "unspecified." These 2 terms seem to mean the same thing but we need - // to pick just one. "unspecified" was chosen because "default" is often a - // reserved word in languages with switch statements (dart, java, etc). - actionString = @"TextInputAction.unspecified"; - break; - case FlutterTextInputActionDone: - actionString = @"TextInputAction.done"; - break; - case FlutterTextInputActionGo: - actionString = @"TextInputAction.go"; - break; - case FlutterTextInputActionSend: - actionString = @"TextInputAction.send"; - break; - case FlutterTextInputActionSearch: - actionString = @"TextInputAction.search"; - break; - case FlutterTextInputActionNext: - actionString = @"TextInputAction.next"; - break; - case FlutterTextInputActionContinue: - actionString = @"TextInputAction.continue"; - break; - case FlutterTextInputActionJoin: - actionString = @"TextInputAction.join"; - break; - case FlutterTextInputActionRoute: - actionString = @"TextInputAction.route"; - break; - case FlutterTextInputActionEmergencyCall: - actionString = @"TextInputAction.emergencyCall"; - break; - case FlutterTextInputActionNewline: - actionString = @"TextInputAction.newline"; - break; - } - [_textInputChannel.get() invokeMethod:@"TextInputClient.performAction" - arguments:@[ @(client), actionString ]]; -} - #pragma mark - Orientation updates - (void)onOrientationPreferencesUpdated:(NSNotification*)notification { @@ -873,14 +719,14 @@ - (NSUInteger)supportedInterfaceOrientations { #pragma mark - Accessibility - (void)onAccessibilityStatusChanged:(NSNotification*)notification { - auto platformView = _shell->GetPlatformView(); + auto platformView = [_engine.get() platformView]; int32_t flags = 0; if (UIAccessibilityIsInvertColorsEnabled()) - flags ^= static_cast(blink::AccessibilityFeatureFlag::kInvertColors); + flags |= static_cast(blink::AccessibilityFeatureFlag::kInvertColors); if (UIAccessibilityIsReduceMotionEnabled()) - flags ^= static_cast(blink::AccessibilityFeatureFlag::kReduceMotion); + flags |= static_cast(blink::AccessibilityFeatureFlag::kReduceMotion); if (UIAccessibilityIsBoldTextEnabled()) - flags ^= static_cast(blink::AccessibilityFeatureFlag::kBoldText); + flags |= static_cast(blink::AccessibilityFeatureFlag::kBoldText); #if TARGET_OS_SIMULATOR // There doesn't appear to be any way to determine whether the accessibility // inspector is enabled on the simulator. We conservatively always turn on the @@ -889,8 +735,8 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { platformView->SetAccessibilityFeatures(flags); #else bool enabled = UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning(); - if (UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning()) - flags ^= static_cast(blink::AccessibilityFeatureFlag::kAccessibleNavigation); + if (enabled) + flags |= static_cast(blink::AccessibilityFeatureFlag::kAccessibleNavigation); platformView->SetSemanticsEnabled(enabled || UIAccessibilityIsSpeakScreenEnabled()); platformView->SetAccessibilityFeatures(flags); #endif @@ -899,31 +745,55 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { #pragma mark - Memory Notifications - (void)onMemoryWarning:(NSNotification*)notification { - [_systemChannel.get() sendMessage:@{@"type" : @"memoryPressure"}]; + [[_engine.get() systemChannel] sendMessage:@{@"type" : @"memoryPressure"}]; } #pragma mark - Locale updates - (void)onLocaleUpdated:(NSNotification*)notification { + NSArray* preferredLocales = [NSLocale preferredLanguages]; + NSMutableArray* data = [[NSMutableArray new] autorelease]; + + // Force prepend the [NSLocale currentLocale] to the front of the list + // to ensure we are including the full default locale. preferredLocales + // is not guaranteed to include anything beyond the languageCode. NSLocale* currentLocale = [NSLocale currentLocale]; NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; - if (languageCode && countryCode) - // We pass empty strings for undefined scripts and variants to ensure the JSON encoder/decoder - // functions properly. - [_localizationChannel.get() invokeMethod:@"setLocale" - arguments:@[ - languageCode, countryCode, scriptCode ? scriptCode : @"", - variantCode ? variantCode : @"" - ]]; + if (languageCode) { + [data addObject:languageCode]; + [data addObject:(countryCode ? countryCode : @"")]; + [data addObject:(scriptCode ? scriptCode : @"")]; + [data addObject:(variantCode ? variantCode : @"")]; + } + + // Add any secondary locales/languages to the list. + for (NSString* localeID in preferredLocales) { + NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; + NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; + NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; + NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; + NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; + if (!languageCode) { + continue; + } + [data addObject:languageCode]; + [data addObject:(countryCode ? countryCode : @"")]; + [data addObject:(scriptCode ? scriptCode : @"")]; + [data addObject:(variantCode ? variantCode : @"")]; + } + if (data.count == 0) { + return; + } + [[_engine.get() localizationChannel] invokeMethod:@"setLocale" arguments:data]; } #pragma mark - Set user settings - (void)onUserSettingsChanged:(NSNotification*)notification { - [_settingsChannel.get() sendMessage:@{ + [[_engine.get() settingsChannel] sendMessage:@{ @"textScaleFactor" : @([self textScaleFactor]), @"alwaysUse24HourFormat" : @([self isAlwaysUse24HourFormat]), }]; @@ -1061,52 +931,43 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { }); } +#pragma mark - Platform views + +- (shell::FlutterPlatformViewsController*)platformViewsController { + return [_engine.get() platformViewsController]; +} + #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(NSString*)channel message:(NSData*)message { - [self sendOnChannel:channel message:message binaryReply:nil]; + [_engine.get() sendOnChannel:channel message:message]; } - (void)sendOnChannel:(NSString*)channel message:(NSData*)message binaryReply:(FlutterBinaryReply)callback { NSAssert(channel, @"The channel must not be null"); - fml::RefPtr response = - (callback == nil) ? nullptr - : fml::MakeRefCounted( - ^(NSData* reply) { - callback(reply); - }, - _shell->GetTaskRunners().GetPlatformTaskRunner()); - fml::RefPtr platformMessage = - (message == nil) ? fml::MakeRefCounted(channel.UTF8String, response) - : fml::MakeRefCounted( - channel.UTF8String, shell::GetVectorFromNSData(message), response); - - _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); + [_engine.get() sendOnChannel:channel message:message binaryReply:callback]; } - (void)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler:(FlutterBinaryMessageHandler)handler { NSAssert(channel, @"The channel must not be null"); - [self iosPlatformView] -> GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, - handler); + [_engine.get() setMessageHandlerOnChannel:channel binaryMessageHandler:handler]; } #pragma mark - FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture { - int64_t textureId = _nextTextureId++; - [self iosPlatformView] -> RegisterExternalTexture(textureId, texture); - return textureId; + return [_engine.get() registerTexture:texture]; } - (void)unregisterTexture:(int64_t)textureId { - _shell->GetPlatformView()->UnregisterTexture(textureId); + [_engine.get() unregisterTexture:textureId]; } - (void)textureFrameAvailable:(int64_t)textureId { - _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); + [_engine.get() textureFrameAvailable:textureId]; } - (NSString*)lookupKeyForAsset:(NSString*)asset { @@ -1118,81 +979,21 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { } - (id)pluginRegistry { - return self; + return _engine; } #pragma mark - FlutterPluginRegistry - (NSObject*)registrarForPlugin:(NSString*)pluginKey { - NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey); - self.pluginPublications[pluginKey] = [NSNull null]; - return [[FlutterViewControllerRegistrar alloc] initWithPlugin:pluginKey - flutterViewController:self]; + return [_engine.get() registrarForPlugin:pluginKey]; } - (BOOL)hasPlugin:(NSString*)pluginKey { - return _pluginPublications[pluginKey] != nil; + return [_engine.get() hasPlugin:pluginKey]; } - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { - return _pluginPublications[pluginKey]; -} -@end - -@implementation FlutterViewControllerRegistrar { - NSString* _pluginKey; - FlutterViewController* _flutterViewController; -} - -- (instancetype)initWithPlugin:(NSString*)pluginKey - flutterViewController:(FlutterViewController*)flutterViewController { - self = [super init]; - NSAssert(self, @"Super init cannot be nil"); - _pluginKey = [pluginKey retain]; - _flutterViewController = [flutterViewController retain]; - return self; -} - -- (void)dealloc { - [_pluginKey release]; - [_flutterViewController release]; - [super dealloc]; -} - -- (NSObject*)messenger { - return _flutterViewController; -} - -- (NSObject*)textures { - return _flutterViewController; -} - -- (void)publish:(NSObject*)value { - _flutterViewController.pluginPublications[_pluginKey] = value; -} - -- (void)addMethodCallDelegate:(NSObject*)delegate - channel:(FlutterMethodChannel*)channel { - [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [delegate handleMethodCall:call result:result]; - }]; -} - -- (void)addApplicationDelegate:(NSObject*)delegate { - id appDelegate = [[UIApplication sharedApplication] delegate]; - if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) { - id lifeCycleProvider = - (id)appDelegate; - [lifeCycleProvider addApplicationLifeCycleDelegate:delegate]; - } -} - -- (NSString*)lookupKeyForAsset:(NSString*)asset { - return [_flutterViewController lookupKeyForAsset:asset]; -} - -- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { - return [_flutterViewController lookupKeyForAsset:asset fromPackage:package]; + return [_engine.get() valuePublishedByPlugin:pluginKey]; } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index 482379c8f17eb..4908c7dc80638 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -1,16 +1,21 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" @interface FlutterViewController () -- (shell::Shell&)shell; +- (fml::WeakPtr)getWeakPtr; +- (shell::FlutterPlatformViewsController*)platformViewsController; @end diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index 3c90a23af0851..f48064a258afd 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -27,6 +27,8 @@ namespace shell { class AccessibilityBridge; } // namespace shell +@class FlutterPlatformViewSemanticsContainer; + /** * A node in the iOS semantics tree. */ @@ -71,6 +73,11 @@ class AccessibilityBridge; */ @property(nonatomic, strong) NSMutableArray* children; +/** + * Used if this SemanticsObject is for a platform view. + */ +@property(strong, nonatomic) FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer; + - (BOOL)nodeWillCauseLayoutChange:(const blink::SemanticsNode*)node; #pragma mark - Designated initializers @@ -108,12 +115,31 @@ class AccessibilityBridge; @interface FlutterSemanticsObject : SemanticsObject @end +/** + * Designated to act as an accessibility container of a platform view. + * + * This object does not take any accessibility actions on its own, nor has any accessibility + * label/value/trait/hint... on its own. The accessibility data will be handled by the platform + * view. + * + * See also: + * * `SemanticsObject` for the other type of semantics objects. + * * `FlutterSemanticsObject` for default implementation of `SemanticsObject`. + */ +@interface FlutterPlatformViewSemanticsContainer : UIAccessibilityElement + +- (instancetype)init __attribute__((unavailable("Use initWithAccessibilityContainer: instead"))); + +@end + namespace shell { class PlatformViewIOS; class AccessibilityBridge final { public: - AccessibilityBridge(UIView* view, PlatformViewIOS* platform_view); + AccessibilityBridge(UIView* view, + PlatformViewIOS* platform_view, + FlutterPlatformViewsController* platform_views_controller); ~AccessibilityBridge(); void UpdateSemantics(blink::SemanticsNodeUpdates nodes, @@ -129,15 +155,21 @@ class AccessibilityBridge final { fml::WeakPtr GetWeakPtr(); + FlutterPlatformViewsController* GetPlatformViewsController() const { + return platform_views_controller_; + }; + + void clearState(); + private: SemanticsObject* GetOrCreateObject(int32_t id, blink::SemanticsNodeUpdates& updates); void VisitObjectsRecursivelyAndRemove(SemanticsObject* object, NSMutableArray* doomed_uids); - void ReleaseObjects(std::unordered_map& objects); void HandleEvent(NSDictionary* annotatedEvent); UIView* view_; PlatformViewIOS* platform_view_; + FlutterPlatformViewsController* platform_views_controller_; fml::scoped_nsobject> objects_; fml::scoped_nsprotocol accessibility_channel_; fml::WeakPtrFactory weak_factory_; diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 6b60bc6b3ba68..39bfce29fb702 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,6 +11,7 @@ #import #include "flutter/fml/logging.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" namespace { @@ -85,10 +86,13 @@ - (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject ins - (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject bridge:(fml::WeakPtr)bridge NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, weak) SemanticsObject* semanticsObject; + @end @implementation SemanticsObject { - SemanticsObjectContainer* _container; + fml::scoped_nsobject _container; } #pragma mark - Override base class designated initializers @@ -110,7 +114,7 @@ - (instancetype)initWithBridge:(fml::WeakPtr)bridge if (self) { _bridge = bridge; _uid = uid; - self.children = [[[NSMutableArray alloc] init] autorelease]; + _children = [[NSMutableArray alloc] init]; } return self; @@ -121,10 +125,10 @@ - (void)dealloc { child.parent = nil; } [_children removeAllObjects]; - [_children dealloc]; + [_children release]; _parent = nil; - [_container release]; - _container = nil; + _container.get().semanticsObject = nil; + [_platformViewSemanticsContainer release]; [super dealloc]; } @@ -150,6 +154,9 @@ - (BOOL)nodeWillCauseScroll:(const blink::SemanticsNode*)node { } - (BOOL)hasChildren { + if (_node.IsPlatformViewNode()) { + return YES; + } return [self.children count] != 0; } @@ -163,6 +170,7 @@ - (BOOL)isAccessibilityElement { // We enforce in the framework that no other useful semantics are merged with these nodes. if ([self node].HasFlag(blink::SemanticsFlags::kScopesRoute)) return false; + return ([self node].flags != 0 && [self node].flags != static_cast(blink::SemanticsFlags::kIsHidden)) || ![self node].label.empty() || ![self node].value.empty() || ![self node].hint.empty() || @@ -257,7 +265,7 @@ - (CGRect)globalRect { // `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in // the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to // convert. - CGFloat scale = [[[self bridge] -> view() window] screen].scale; + CGFloat scale = [[[self bridge]->view() window] screen].scale; auto result = CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale); return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge] -> view()); @@ -268,9 +276,9 @@ - (CGRect)globalRect { - (id)accessibilityContainer { if ([self hasChildren] || [self uid] == kRootNodeId) { if (_container == nil) - _container = - [[SemanticsObjectContainer alloc] initWithSemanticsObject:self bridge:[self bridge]]; - return _container; + _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:self + bridge:[self bridge]]); + return _container.get(); } if ([self parent] == nil) { // This can happen when we have released the accessibility tree but iOS is @@ -394,6 +402,25 @@ - (UIAccessibilityTraits)accessibilityTraits { @end +@implementation FlutterPlatformViewSemanticsContainer + +// Method declared as unavailable in the interface +- (instancetype)init { + [self release]; + [super doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithAccessibilityContainer:(id)container { + FML_CHECK(container); + if (self = [super initWithAccessibilityContainer:container]) { + self.isAccessibilityElement = NO; + } + return self; +} + +@end + @implementation SemanticsObjectContainer { SemanticsObject* _semanticsObject; fml::WeakPtr _bridge; @@ -410,36 +437,44 @@ - (instancetype)init { - (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject bridge:(fml::WeakPtr)bridge { - FML_DCHECK(semanticsObject != nil) << "semanticsObject must be set"; + FML_DCHECK(semanticsObject) << "semanticsObject must be set"; self = [super init]; if (self) { _semanticsObject = semanticsObject; - // The pointer is managed manually. - [_semanticsObject retain]; _bridge = bridge; } return self; } -- (void)dealloc { - [_semanticsObject release]; - [super dealloc]; -} - #pragma mark - UIAccessibilityContainer overrides - (NSInteger)accessibilityElementCount { - return [[_semanticsObject children] count] + 1; + NSInteger count = [[_semanticsObject children] count] + 1; + // Need to create an additional child that acts as accessibility container for the platform view. + if (_semanticsObject.node.IsPlatformViewNode()) { + count++; + } + return count; } - (nullable id)accessibilityElementAtIndex:(NSInteger)index { if (index < 0 || index >= [self accessibilityElementCount]) return nil; - if (index == 0) + if (index == 0) { return _semanticsObject; + } + + // Return the additional child acts as a container of platform view. The + // platformViewSemanticsContainer was created and cached in the updateSemantics path. + if (_semanticsObject.node.IsPlatformViewNode() && index == [self accessibilityElementCount] - 1) { + FML_CHECK(_semanticsObject.platformViewSemanticsContainer != nil); + return _semanticsObject.platformViewSemanticsContainer; + } + SemanticsObject* child = [_semanticsObject children][index - 1]; + if ([child hasChildren]) return [child accessibilityContainer]; return child; @@ -448,6 +483,12 @@ - (nullable id)accessibilityElementAtIndex:(NSInteger)index { - (NSInteger)indexOfAccessibilityElement:(id)element { if (element == _semanticsObject) return 0; + + // FlutterPlatformViewSemanticsContainer is always the last element of its parent. + if ([element isKindOfClass:[FlutterPlatformViewSemanticsContainer class]]) { + return [self accessibilityElementCount] - 1; + } + NSMutableArray* children = [_semanticsObject children]; for (size_t i = 0; i < [children count]; i++) { SemanticsObject* child = children[i]; @@ -489,16 +530,19 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { namespace shell { -AccessibilityBridge::AccessibilityBridge(UIView* view, PlatformViewIOS* platform_view) +AccessibilityBridge::AccessibilityBridge(UIView* view, + PlatformViewIOS* platform_view, + FlutterPlatformViewsController* platform_views_controller) : view_(view), platform_view_(platform_view), + platform_views_controller_(platform_views_controller), objects_([[NSMutableDictionary alloc] init]), weak_factory_(this), previous_route_id_(0), previous_routes_({}) { accessibility_channel_.reset([[FlutterBasicMessageChannel alloc] initWithName:@"flutter/accessibility" - binaryMessenger:platform_view->GetOwnerViewController() + binaryMessenger:platform_view->GetOwnerViewController().get() codec:[FlutterStandardMessageCodec sharedInstance]]); [accessibility_channel_.get() setMessageHandler:^(id message, FlutterReply reply) { HandleEvent((NSDictionary*)message); @@ -506,6 +550,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } AccessibilityBridge::~AccessibilityBridge() { + clearState(); view_.accessibilityElements = nil; [accessibility_channel_.get() setMessageHandler:nil]; } @@ -528,7 +573,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { layoutChanged = layoutChanged || [object nodeWillCauseLayoutChange:&node]; scrollOccured = scrollOccured || [object nodeWillCauseScroll:&node]; [object setSemanticsNode:&node]; - const NSUInteger newChildCount = node.childrenInTraversalOrder.size(); + NSUInteger newChildCount = node.childrenInTraversalOrder.size(); NSMutableArray* newChildren = [[[NSMutableArray alloc] initWithCapacity:newChildCount] autorelease]; for (NSUInteger i = 0; i < newChildCount; ++i) { @@ -558,6 +603,20 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } object.accessibilityCustomActions = accessibilityCustomActions; } + + if (object.node.IsPlatformViewNode()) { + shell::FlutterPlatformViewsController* controller = GetPlatformViewsController(); + if (controller) { + object.platformViewSemanticsContainer = [[FlutterPlatformViewSemanticsContainer alloc] + initWithAccessibilityContainer:[object accessibilityContainer]]; + UIView* platformView = [controller->GetPlatformViewByID(object.node.platformViewId) view]; + if (platformView) { + object.platformViewSemanticsContainer.accessibilityElements = @[ platformView ]; + } + } + } else if (object.platformViewSemanticsContainer) { + [object.platformViewSemanticsContainer release]; + } } SemanticsObject* root = objects_.get()[@(kRootNodeId)]; @@ -654,11 +713,11 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { [objects_ removeObjectForKey:@(node.id)]; if (isTextField) { // Text fields are backed by objects that implement UITextInput. - object = - [[[TextInputSemanticsObject alloc] initWithBridge:GetWeakPtr() uid:uid] autorelease]; + object = [[[TextInputSemanticsObject alloc] initWithBridge:GetWeakPtr() + uid:uid] autorelease]; } else { - object = - [[[FlutterSemanticsObject alloc] initWithBridge:GetWeakPtr() uid:uid] autorelease]; + object = [[[FlutterSemanticsObject alloc] initWithBridge:GetWeakPtr() + uid:uid] autorelease]; } [object.parent.children replaceObjectAtIndex:positionInChildlist withObject:object]; objects_.get()[@(node.id)] = object; @@ -687,4 +746,10 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { return weak_factory_.GetWeakPtr(); } +void AccessibilityBridge::clearState() { + [objects_ removeAllObjects]; + previous_route_id_ = 0; + previous_routes_.clear(); +} + } // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h index bcdebfc689deb..163346d7b04dd 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,7 @@ * * This class is used by `TextInputSemanticsObject`. */ -@interface FlutterInactiveTextInput : UIView +@interface FlutterInactiveTextInput : UIView @property(nonatomic, copy) NSString* text; @property(nonatomic, readonly) NSMutableString* markedText; @@ -30,7 +30,7 @@ * field that currently owns input focus. Delegates to * `FlutterInactiveTextInput` otherwise. */ -@interface TextInputSemanticsObject : SemanticsObject +@interface TextInputSemanticsObject : SemanticsObject @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_TEXT_ENTRY_H_ diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index ec98c5d003204..0edde04a2c2c1 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -343,8 +343,9 @@ - (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInte - (UITextPosition*)positionFromPosition:(UITextPosition*)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset { - return - [[self textInputSurrogate] positionFromPosition:position inDirection:direction offset:offset]; + return [[self textInputSurrogate] positionFromPosition:position + inDirection:direction + offset:offset]; } - (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other { @@ -362,8 +363,8 @@ - (UITextPosition*)positionWithinRange:(UITextRange*)range - (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position inDirection:(UITextLayoutDirection)direction { - return - [[self textInputSurrogate] characterRangeByExtendingPosition:position inDirection:direction]; + return [[self textInputSurrogate] characterRangeByExtendingPosition:position + inDirection:direction]; } - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position diff --git a/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm b/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm index b94793d39e812..0e257a42c634b 100644 --- a/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm +++ b/shell/platform/darwin/ios/framework/Source/flutter_codecs_unittest.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -56,7 +56,7 @@ } TEST(FlutterJSONCodec, CanEncodeAndDecodeDictionary) { - NSDictionary* value = @{ @"a" : @3.14, @"b" : @47, @"c" : [NSNull null], @"d" : @[ @"nested" ] }; + NSDictionary* value = @{@"a" : @3.14, @"b" : @47, @"c" : [NSNull null], @"d" : @[ @"nested" ]}; FlutterJSONMessageCodec* codec = [FlutterJSONMessageCodec sharedInstance]; NSData* encoded = [codec encode:value]; NSDictionary* decoded = [codec decode:encoded]; diff --git a/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm b/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm index 41cb39a0792af..e254cc6c4ffac 100644 --- a/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm +++ b/shell/platform/darwin/ios/framework/Source/flutter_standard_codec_unittest.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -66,12 +66,12 @@ void checkEncodeDecode(id value) { checkEncodeDecode(@(value), [NSData dataWithBytes:bytes length:9]); } -TEST(FlutterStandardCodec, CanEncodeAndDecodeUInt64AsHexString) { +TEST(FlutterStandardCodec, CanEncodeUInt64) { FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; UInt64 u64 = 0xfffffffffffffffa; + uint8_t bytes[9] = {0x04, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; NSData* encoded = [codec encode:@(u64)]; - NSString* decoded = [codec decode:encoded]; - ASSERT_TRUE([decoded isEqual:@"fffffffffffffffa"]); + ASSERT_TRUE([encoded isEqual:[NSData dataWithBytes:bytes length:9]]); } TEST(FlutterStandardCodec, CanEncodeAndDecodeSInt8) { diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h index dd6ec72d56db4..ee6b929bc9843 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h +++ b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -20,24 +20,15 @@ namespace shell { class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { public: - void Complete(std::unique_ptr data) override { - fml::RefPtr self(this); - platform_task_runner_->PostTask(fml::MakeCopyable([self, data = std::move(data)]() mutable { - self->callback_.get()(shell::GetNSDataFromMapping(std::move(data))); - })); - } - - void CompleteEmpty() override { - fml::RefPtr self(this); - platform_task_runner_->PostTask( - fml::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); - } + void Complete(std::unique_ptr data) override; + + void CompleteEmpty() override; private: explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback, - fml::RefPtr platform_task_runner) - : callback_(callback, fml::OwnershipPolicy::Retain), - platform_task_runner_(std::move(platform_task_runner)) {} + fml::RefPtr platform_task_runner); + + ~PlatformMessageResponseDarwin() override; fml::ScopedBlock callback_; fml::RefPtr platform_task_runner_; diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm index 8590b4e36942b..0d45e4e2ff4e1 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm +++ b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,6 +6,25 @@ namespace shell { -// +PlatformMessageResponseDarwin::PlatformMessageResponseDarwin( + PlatformMessageResponseCallback callback, + fml::RefPtr platform_task_runner) + : callback_(callback, fml::OwnershipPolicy::Retain), + platform_task_runner_(std::move(platform_task_runner)) {} + +PlatformMessageResponseDarwin::~PlatformMessageResponseDarwin() = default; + +void PlatformMessageResponseDarwin::Complete(std::unique_ptr data) { + fml::RefPtr self(this); + platform_task_runner_->PostTask(fml::MakeCopyable([self, data = std::move(data)]() mutable { + self->callback_.get()(shell::GetNSDataFromMapping(std::move(data))); + })); +} + +void PlatformMessageResponseDarwin::CompleteEmpty() { + fml::RefPtr self(this); + platform_task_runner_->PostTask( + fml::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); +} } // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_router.h b/shell/platform/darwin/ios/framework/Source/platform_message_router.h index 4013dc947da0b..1949d817d888d 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_router.h +++ b/shell/platform/darwin/ios/framework/Source/platform_message_router.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/platform_message_router.mm b/shell/platform/darwin/ios/framework/Source/platform_message_router.mm index 0378539deefab..6cfb3049ac63a 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_router.mm +++ b/shell/platform/darwin/ios/framework/Source/platform_message_router.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h index 000f84a4a35b9..335d47892ae81 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm index 6b80c12946b85..702fdc9a51e13 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/darwin/ios/headless_platform_view_ios.h b/shell/platform/darwin/ios/headless_platform_view_ios.h deleted file mode 100644 index 940e2ade4af58..0000000000000 --- a/shell/platform/darwin/ios/headless_platform_view_ios.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ -#define SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ - -#include - -#include "flutter/fml/closure.h" -#include "flutter/fml/macros.h" -#include "flutter/fml/memory/weak_ptr.h" -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" - -namespace shell { - -class HeadlessPlatformViewIOS : public PlatformView { - public: - explicit HeadlessPlatformViewIOS(Delegate& delegate, - blink::TaskRunners task_runners); - virtual ~HeadlessPlatformViewIOS(); - - PlatformMessageRouter& GetPlatformMessageRouter(); - - private: - PlatformMessageRouter platform_message_router_; - - // |shell::PlatformView| - void HandlePlatformMessage(fml::RefPtr message); - - FML_DISALLOW_COPY_AND_ASSIGN(HeadlessPlatformViewIOS); -}; - -} // namespace shell - -#endif // SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ diff --git a/shell/platform/darwin/ios/headless_platform_view_ios.mm b/shell/platform/darwin/ios/headless_platform_view_ios.mm deleted file mode 100644 index c496253494398..0000000000000 --- a/shell/platform/darwin/ios/headless_platform_view_ios.mm +++ /dev/null @@ -1,20 +0,0 @@ - -#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" - -namespace shell { - -HeadlessPlatformViewIOS::HeadlessPlatformViewIOS(PlatformView::Delegate& delegate, - blink::TaskRunners task_runners) - : PlatformView(delegate, std::move(task_runners)) {} - -HeadlessPlatformViewIOS::~HeadlessPlatformViewIOS() = default; - -PlatformMessageRouter& HeadlessPlatformViewIOS::GetPlatformMessageRouter() { - return platform_message_router_; -} - -// |shell::PlatformView| -void HeadlessPlatformViewIOS::HandlePlatformMessage(fml::RefPtr message) { - platform_message_router_.HandlePlatformMessage(std::move(message)); -} -} diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.h b/shell/platform/darwin/ios/ios_external_texture_gl.h index 0ad69483385f2..d258234708a0b 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.h +++ b/shell/platform/darwin/ios/ios_external_texture_gl.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,21 +13,18 @@ namespace shell { class IOSExternalTextureGL : public flow::Texture { public: - IOSExternalTextureGL(int64_t textureId, - NSObject* externalTexture); + IOSExternalTextureGL(int64_t textureId, NSObject* externalTexture); ~IOSExternalTextureGL() override; // Called from GPU thread. - virtual void Paint(SkCanvas& canvas, - const SkRect& bounds, - bool freeze) override; + void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze) override; - virtual void OnGrContextCreated() override; + void OnGrContextCreated() override; - virtual void OnGrContextDestroyed() override; + void OnGrContextDestroyed() override; - virtual void MarkNewFrameAvailable() override; + void MarkNewFrameAvailable() override; private: NSObject* external_texture_; diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index 638894b8aba5b..543d2582e6310 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,8 +11,6 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrTexture.h" -#include "third_party/skia/include/gpu/GrTypes.h" namespace shell { diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index e81954d58a0ef..08778d21319a7 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,22 +13,18 @@ #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "ios_gl_render_target.h" namespace shell { class IOSGLContext { public: - IOSGLContext(fml::scoped_nsobject layer); + IOSGLContext(); ~IOSGLContext(); - bool IsValid() const; - - bool PresentRenderBuffer() const; - - GLuint framebuffer() const { return framebuffer_; } - - bool UpdateStorageSizeIfNecessary(); + std::unique_ptr CreateRenderTarget( + fml::scoped_nsobject layer); bool MakeCurrent(); @@ -37,15 +33,9 @@ class IOSGLContext { sk_sp ColorSpace() const { return color_space_; } private: - fml::scoped_nsobject layer_; fml::scoped_nsobject context_; fml::scoped_nsobject resource_context_; - GLuint framebuffer_; - GLuint colorbuffer_; - GLint storage_size_width_; - GLint storage_size_height_; sk_sp color_space_; - bool valid_; FML_DISALLOW_COPY_AND_ASSIGN(IOSGLContext); }; diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index 765e3c3650589..c80919a7cab3c 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,13 +12,7 @@ namespace shell { -IOSGLContext::IOSGLContext(fml::scoped_nsobject layer) - : layer_(std::move(layer)), - framebuffer_(GL_NONE), - colorbuffer_(GL_NONE), - storage_size_width_(0), - storage_size_height_(0), - valid_(false) { +IOSGLContext::IOSGLContext() { context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]); if (context_ != nullptr) { resource_context_.reset([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 @@ -29,35 +23,6 @@ sharegroup:context_.get().sharegroup]); } - FML_DCHECK(layer_ != nullptr); - FML_DCHECK(context_ != nullptr); - FML_DCHECK(resource_context_ != nullptr); - - bool context_current = [EAGLContext setCurrentContext:context_]; - - FML_DCHECK(context_current); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - // Generate the framebuffer - - glGenFramebuffers(1, &framebuffer_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - FML_DCHECK(framebuffer_ != GL_NONE); - - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - // Setup color attachment - - glGenRenderbuffers(1, &colorbuffer_); - FML_DCHECK(colorbuffer_ != GL_NONE); - - glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display // gamut just tells us what color space it makes sense to render into. We @@ -71,91 +36,24 @@ case UIDisplayGamutP3: // Should we consider using more than 8-bits of precision given that // P3 specifies a wider range of colors? - color_space_ = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, - SkColorSpace::kDCIP3_D65_Gamut); + color_space_ = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); break; default: break; } } - - NSString* drawableColorFormat = kEAGLColorFormatRGBA8; - layer_.get().drawableProperties = @{ - kEAGLDrawablePropertyColorFormat : drawableColorFormat, - kEAGLDrawablePropertyRetainedBacking : @(NO), - }; - - valid_ = true; -} - -IOSGLContext::~IOSGLContext() { - FML_DCHECK(glGetError() == GL_NO_ERROR); - - // Deletes on GL_NONEs are ignored - glDeleteFramebuffers(1, &framebuffer_); - glDeleteRenderbuffers(1, &colorbuffer_); - - FML_DCHECK(glGetError() == GL_NO_ERROR); -} - -bool IOSGLContext::IsValid() const { - return valid_; -} - -bool IOSGLContext::PresentRenderBuffer() const { - const GLenum discards[] = { - GL_DEPTH_ATTACHMENT, - GL_STENCIL_ATTACHMENT, - }; - - glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof(discards) / sizeof(GLenum), discards); - - glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - return [[EAGLContext currentContext] presentRenderbuffer:GL_RENDERBUFFER]; } -bool IOSGLContext::UpdateStorageSizeIfNecessary() { - const CGSize layer_size = [layer_.get() bounds].size; - const CGFloat contents_scale = layer_.get().contentsScale; - const GLint size_width = layer_size.width * contents_scale; - const GLint size_height = layer_size.height * contents_scale; - - if (size_width == storage_size_width_ && size_height == storage_size_height_) { - // Nothing to since the stoage size is already consistent with the layer. - return true; - } - TRACE_EVENT_INSTANT0("flutter", "IOSGLContext::UpdateStorageSizeIfNecessary"); - FML_DLOG(INFO) << "Updating render buffer storage size."; - - if (![EAGLContext setCurrentContext:context_]) { - return false; - } - - FML_DCHECK(glGetError() == GL_NO_ERROR); - - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - - glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { - return false; - } - - // Fetch the dimensions of the color buffer whose backing was just updated. - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &storage_size_width_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &storage_size_height_); - FML_DCHECK(glGetError() == GL_NO_ERROR); - - FML_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); +IOSGLContext::~IOSGLContext() = default; - return true; +std::unique_ptr IOSGLContext::CreateRenderTarget( + fml::scoped_nsobject layer) { + return std::make_unique(std::move(layer), context_.get(), + resource_context_.get()); } bool IOSGLContext::MakeCurrent() { - return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; + return [EAGLContext setCurrentContext:context_.get()]; } bool IOSGLContext::ResourceMakeCurrent() { diff --git a/shell/platform/darwin/ios/ios_gl_render_target.h b/shell/platform/darwin/ios/ios_gl_render_target.h new file mode 100644 index 0000000000000..ce2a128bd3c48 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_render_target.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_RENDER_TARGET_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_RENDER_TARGET_H_ + +#import +#import +#import +#import + +#include "flutter/fml/macros.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/platform_view.h" + +namespace shell { + +class IOSGLRenderTarget { + public: + IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context); + + ~IOSGLRenderTarget(); + + bool IsValid() const; + + bool PresentRenderBuffer() const; + + GLuint framebuffer() const { return framebuffer_; } + + bool UpdateStorageSizeIfNecessary(); + + bool MakeCurrent(); + + bool ResourceMakeCurrent(); + + sk_sp ColorSpace() const { return color_space_; } + + private: + fml::scoped_nsobject layer_; + fml::scoped_nsobject context_; + fml::scoped_nsobject resource_context_; + GLuint framebuffer_; + GLuint colorbuffer_; + GLint storage_size_width_; + GLint storage_size_height_; + sk_sp color_space_; + bool valid_; + + FML_DISALLOW_COPY_AND_ASSIGN(IOSGLRenderTarget); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_GL_RENDER_TARGET_H_ diff --git a/shell/platform/darwin/ios/ios_gl_render_target.mm b/shell/platform/darwin/ios/ios_gl_render_target.mm new file mode 100644 index 0000000000000..210d9a9d8c684 --- /dev/null +++ b/shell/platform/darwin/ios/ios_gl_render_target.mm @@ -0,0 +1,141 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" + +#include + +#include "flutter/fml/trace_event.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" + +namespace shell { + +IOSGLRenderTarget::IOSGLRenderTarget(fml::scoped_nsobject layer, + EAGLContext* context, + EAGLContext* resource_context) + : layer_(std::move(layer)), + context_([context retain]), + resource_context_([resource_context retain]), + framebuffer_(GL_NONE), + colorbuffer_(GL_NONE), + storage_size_width_(0), + storage_size_height_(0), + valid_(false) { + FML_DCHECK(layer_ != nullptr); + FML_DCHECK(context_ != nullptr); + FML_DCHECK(resource_context_ != nullptr); + + bool context_current = [EAGLContext setCurrentContext:context_]; + + FML_DCHECK(context_current); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + // Generate the framebuffer + + glGenFramebuffers(1, &framebuffer_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + FML_DCHECK(framebuffer_ != GL_NONE); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + // Setup color attachment + + glGenRenderbuffers(1, &colorbuffer_); + FML_DCHECK(colorbuffer_ != GL_NONE); + + glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + NSString* drawableColorFormat = kEAGLColorFormatRGBA8; + layer_.get().drawableProperties = @{ + kEAGLDrawablePropertyColorFormat : drawableColorFormat, + kEAGLDrawablePropertyRetainedBacking : @(NO), + }; + + valid_ = true; +} + +IOSGLRenderTarget::~IOSGLRenderTarget() { + [EAGLContext setCurrentContext:context_]; + FML_DCHECK(glGetError() == GL_NO_ERROR); + + // Deletes on GL_NONEs are ignored + glDeleteFramebuffers(1, &framebuffer_); + glDeleteRenderbuffers(1, &colorbuffer_); + + FML_DCHECK(glGetError() == GL_NO_ERROR); +} + +bool IOSGLRenderTarget::IsValid() const { + return valid_; +} + +bool IOSGLRenderTarget::PresentRenderBuffer() const { + const GLenum discards[] = { + GL_DEPTH_ATTACHMENT, + GL_STENCIL_ATTACHMENT, + }; + + glDiscardFramebufferEXT(GL_FRAMEBUFFER, sizeof(discards) / sizeof(GLenum), discards); + + glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); + return [[EAGLContext currentContext] presentRenderbuffer:GL_RENDERBUFFER]; +} + +bool IOSGLRenderTarget::UpdateStorageSizeIfNecessary() { + const CGSize layer_size = [layer_.get() bounds].size; + const CGFloat contents_scale = layer_.get().contentsScale; + const GLint size_width = layer_size.width * contents_scale; + const GLint size_height = layer_size.height * contents_scale; + + if (size_width == storage_size_width_ && size_height == storage_size_height_) { + // Nothing to since the stoage size is already consistent with the layer. + return true; + } + TRACE_EVENT_INSTANT0("flutter", "IOSGLRenderTarget::UpdateStorageSizeIfNecessary"); + FML_DLOG(INFO) << "Updating render buffer storage size."; + + FML_DCHECK(glGetError() == GL_NO_ERROR); + + if (![EAGLContext setCurrentContext:context_]) { + return false; + } + + FML_DCHECK(glGetError() == GL_NO_ERROR); + + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); + + glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + if (![context_.get() renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer_.get()]) { + return false; + } + + // Fetch the dimensions of the color buffer whose backing was just updated. + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &storage_size_width_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &storage_size_height_); + FML_DCHECK(glGetError() == GL_NO_ERROR); + + FML_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + + return true; +} + +bool IOSGLRenderTarget::MakeCurrent() { + return UpdateStorageSizeIfNecessary() && [EAGLContext setCurrentContext:context_.get()]; +} + +bool IOSGLRenderTarget::ResourceMakeCurrent() { + return [EAGLContext setCurrentContext:resource_context_.get()]; +} + +} // namespace shell diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8011905db4d68..1fc6a4218dc75 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -1,10 +1,12 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" + #include #include "flutter/fml/macros.h" @@ -13,9 +15,13 @@ namespace shell { +// Returns true if the app explicitly specified to use the iOS view embedding +// mechanism which is still in a release preview. +bool IsIosEmbeddedViewsPreviewEnabled(); + class IOSSurface { public: - IOSSurface(); + IOSSurface(FlutterPlatformViewsController* platform_views_controller); virtual ~IOSSurface(); @@ -27,6 +33,12 @@ class IOSSurface { virtual std::unique_ptr CreateGPUSurface() = 0; + protected: + FlutterPlatformViewsController* GetPlatformViewsController(); + + private: + FlutterPlatformViewsController* platform_views_controller_; + public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index b0b9cc3245183..f9d2d4cfbd500 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,13 +6,24 @@ #include -#include -#include +#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_software.h" namespace shell { -IOSSurface::IOSSurface() = default; +// The name of the Info.plist flag to enable the embedded iOS views preview. +const char* const kEmbeddedViewsPreview = "io.flutter.embedded_views_preview"; + +bool IsIosEmbeddedViewsPreviewEnabled() { + return [[[NSBundle mainBundle] objectForInfoDictionaryKey:@(kEmbeddedViewsPreview)] boolValue]; +} + +IOSSurface::IOSSurface(FlutterPlatformViewsController* platform_views_controller) + : platform_views_controller_(platform_views_controller) {} IOSSurface::~IOSSurface() = default; +FlutterPlatformViewsController* IOSSurface::GetPlatformViewsController() { + return platform_views_controller_; +} } // namespace shell diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 1a3fc3ed3b406..93fcc0f510624 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,15 +9,21 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" +#include "flutter/shell/platform/darwin/ios/ios_gl_render_target.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @class CAEAGLLayer; namespace shell { -class IOSSurfaceGL : public IOSSurface, public GPUSurfaceGLDelegate { +class IOSSurfaceGL : public IOSSurface, + public GPUSurfaceGLDelegate, + public flow::ExternalViewEmbedder { public: - IOSSurfaceGL(fml::scoped_nsobject layer); + IOSSurfaceGL(fml::scoped_nsobject layer, + FlutterPlatformViewsController* platform_views_controller); + + IOSSurfaceGL(fml::scoped_nsobject layer, std::shared_ptr context); ~IOSSurfaceGL() override; @@ -29,6 +35,8 @@ class IOSSurfaceGL : public IOSSurface, public GPUSurfaceGLDelegate { std::unique_ptr CreateGPUSurface() override; + std::unique_ptr CreateSecondaryGPUSurface(GrContext* gr_context); + bool GLContextMakeCurrent() override; bool GLContextClearCurrent() override; @@ -39,8 +47,27 @@ class IOSSurfaceGL : public IOSSurface, public GPUSurfaceGLDelegate { bool UseOffscreenSurface() const override; + // |shell::GPUSurfaceGLDelegate| + flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + + // |flow::ExternalViewEmbedder| + void BeginFrame(SkISize frame_size) override; + + // |flow::ExternalViewEmbedder| + void PrerollCompositeEmbeddedView(int view_id) override; + + // |flow::ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |flow::ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) override; + + // |flow::ExternalViewEmbedder| + bool SubmitFrame(GrContext* context) override; + private: - IOSGLContext context_; + std::shared_ptr context_; + std::unique_ptr render_target_; FML_DISALLOW_COPY_AND_ASSIGN(IOSSurfaceGL); }; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 253531c4800aa..cd0bb093d87d4 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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,21 +9,32 @@ namespace shell { -IOSSurfaceGL::IOSSurfaceGL(fml::scoped_nsobject layer) : context_(std::move(layer)) {} +IOSSurfaceGL::IOSSurfaceGL(fml::scoped_nsobject layer, + FlutterPlatformViewsController* platform_views_controller) + : IOSSurface(platform_views_controller) { + context_ = std::make_shared(); + render_target_ = context_->CreateRenderTarget(std::move(layer)); +} + +IOSSurfaceGL::IOSSurfaceGL(fml::scoped_nsobject layer, + std::shared_ptr context) + : IOSSurface(nullptr), context_(context) { + render_target_ = context_->CreateRenderTarget(std::move(layer)); +} IOSSurfaceGL::~IOSSurfaceGL() = default; bool IOSSurfaceGL::IsValid() const { - return context_.IsValid(); + return render_target_->IsValid(); } bool IOSSurfaceGL::ResourceContextMakeCurrent() { - return IsValid() ? context_.ResourceMakeCurrent() : false; + return render_target_->IsValid() ? context_->ResourceMakeCurrent() : false; } void IOSSurfaceGL::UpdateStorageSizeIfNecessary() { if (IsValid()) { - context_.UpdateStorageSizeIfNecessary(); + render_target_->UpdateStorageSizeIfNecessary(); } } @@ -31,8 +42,12 @@ return std::make_unique(this); } +std::unique_ptr IOSSurfaceGL::CreateSecondaryGPUSurface(GrContext* gr_context) { + return std::make_unique(sk_ref_sp(gr_context), this); +} + intptr_t IOSSurfaceGL::GLContextFBO() const { - return IsValid() ? context_.framebuffer() : GL_NONE; + return IsValid() ? render_target_->framebuffer() : GL_NONE; } bool IOSSurfaceGL::UseOffscreenSurface() const { @@ -43,7 +58,10 @@ } bool IOSSurfaceGL::GLContextMakeCurrent() { - return IsValid() ? context_.MakeCurrent() : false; + if (!IsValid()) { + return false; + } + return render_target_->UpdateStorageSizeIfNecessary() && context_->MakeCurrent(); } bool IOSSurfaceGL::GLContextClearCurrent() { @@ -53,7 +71,51 @@ bool IOSSurfaceGL::GLContextPresent() { TRACE_EVENT0("flutter", "IOSSurfaceGL::GLContextPresent"); - return IsValid() ? context_.PresentRenderBuffer() : false; + return IsValid() && render_target_->PresentRenderBuffer(); +} + +flow::ExternalViewEmbedder* IOSSurfaceGL::GetExternalViewEmbedder() { + if (IsIosEmbeddedViewsPreviewEnabled()) { + return this; + } else { + return nullptr; + } +} + +void IOSSurfaceGL::BeginFrame(SkISize frame_size) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->SetFrameSize(frame_size); + [CATransaction begin]; +} + +void IOSSurfaceGL::PrerollCompositeEmbeddedView(int view_id) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->PrerollCompositeEmbeddedView(view_id); +} + +std::vector IOSSurfaceGL::GetCurrentCanvases() { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->GetCurrentCanvases(); +} + +SkCanvas* IOSSurfaceGL::CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->CompositeEmbeddedView(view_id, params); +} + +bool IOSSurfaceGL::SubmitFrame(GrContext* context) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + if (platform_views_controller == nullptr) { + return true; + } + + bool submitted = platform_views_controller->SubmitFrame(true, std::move(context), context_); + [CATransaction commit]; + return submitted; } } // namespace shell diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index 154f057489f48..79695b3a2767d 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -1,10 +1,11 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ +#include "flutter/flow/embedded_views.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_software.h" @@ -15,9 +16,11 @@ namespace shell { class IOSSurfaceSoftware final : public IOSSurface, - public GPUSurfaceSoftwareDelegate { + public GPUSurfaceSoftwareDelegate, + public flow::ExternalViewEmbedder { public: - IOSSurfaceSoftware(fml::scoped_nsobject layer); + IOSSurfaceSoftware(fml::scoped_nsobject layer, + FlutterPlatformViewsController* platform_views_controller); ~IOSSurfaceSoftware() override; @@ -39,6 +42,24 @@ class IOSSurfaceSoftware final : public IOSSurface, // |shell::GPUSurfaceSoftwareDelegate| bool PresentBackingStore(sk_sp backing_store) override; + // |shell::GPUSurfaceSoftwareDelegate| + flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + + // |flow::ExternalViewEmbedder| + void BeginFrame(SkISize frame_size) override; + + // |flow::ExternalViewEmbedder| + void PrerollCompositeEmbeddedView(int view_id) override; + + // |flow::ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |flow::ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) override; + + // |flow::ExternalViewEmbedder| + bool SubmitFrame(GrContext* context) override; + private: fml::scoped_nsobject layer_; sk_sp sk_surface_; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index dba1fd7ce1fe9..714f5a56107fe 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,8 +15,9 @@ namespace shell { -IOSSurfaceSoftware::IOSSurfaceSoftware(fml::scoped_nsobject layer) - : layer_(std::move(layer)) { +IOSSurfaceSoftware::IOSSurfaceSoftware(fml::scoped_nsobject layer, + FlutterPlatformViewsController* platform_views_controller) + : IOSSurface(platform_views_controller), layer_(std::move(layer)) { UpdateStorageSizeIfNecessary(); } @@ -63,7 +64,8 @@ return sk_surface_; } - SkImageInfo info = SkImageInfo::MakeN32(size.fWidth, size.fHeight, kPremul_SkAlphaType); + SkImageInfo info = SkImageInfo::MakeN32(size.fWidth, size.fHeight, kPremul_SkAlphaType, + SkColorSpace::MakeSRGB()); sk_surface_ = SkSurface::MakeRaster(info, nullptr); return sk_surface_; } @@ -125,4 +127,45 @@ return true; } +flow::ExternalViewEmbedder* IOSSurfaceSoftware::GetExternalViewEmbedder() { + if (IsIosEmbeddedViewsPreviewEnabled()) { + return this; + } else { + return nullptr; + } +} + +void IOSSurfaceSoftware::BeginFrame(SkISize frame_size) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->SetFrameSize(frame_size); +} + +void IOSSurfaceSoftware::PrerollCompositeEmbeddedView(int view_id) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->PrerollCompositeEmbeddedView(view_id); +} + +std::vector IOSSurfaceSoftware::GetCurrentCanvases() { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->GetCurrentCanvases(); +} + +SkCanvas* IOSSurfaceSoftware::CompositeEmbeddedView(int view_id, + const flow::EmbeddedViewParams& params) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->CompositeEmbeddedView(view_id, params); +} + +bool IOSSurfaceSoftware::SubmitFrame(GrContext* context) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + if (platform_views_controller == nullptr) { + return true; + } + return platform_views_controller->SubmitFrame(false, nullptr, nullptr); +} + } // namespace shell diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 014f045f70c77..b7495b90fec51 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,32 +16,35 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" -#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" +@class FlutterViewController; + namespace shell { -class PlatformViewIOS final : public HeadlessPlatformViewIOS { +class PlatformViewIOS final : public PlatformView { public: - explicit PlatformViewIOS(PlatformView::Delegate& delegate, - blink::TaskRunners task_runners, - FlutterViewController* owner_controller_, - FlutterView* owner_view_); + explicit PlatformViewIOS(PlatformView::Delegate& delegate, blink::TaskRunners task_runners); ~PlatformViewIOS() override; - FlutterViewController* GetOwnerViewController() const; + PlatformMessageRouter& GetPlatformMessageRouter(); + + fml::WeakPtr GetOwnerViewController() const; + void SetOwnerViewController(fml::WeakPtr owner_controller); void RegisterExternalTexture(int64_t id, NSObject* texture); fml::scoped_nsprotocol GetTextInputPlugin() const; - void SetTextInputPlugin( - fml::scoped_nsprotocol plugin); + void SetTextInputPlugin(fml::scoped_nsprotocol plugin); + + // |shell::PlatformView| + void SetSemanticsEnabled(bool enabled) override; private: - FlutterViewController* owner_controller_; // weak reference. - FlutterView* owner_view_; // weak reference. + fml::WeakPtr owner_controller_; std::unique_ptr ios_surface_; PlatformMessageRouter platform_message_router_; std::unique_ptr accessibility_bridge_; @@ -49,25 +52,27 @@ class PlatformViewIOS final : public HeadlessPlatformViewIOS { fml::closure firstFrameCallback_; // |shell::PlatformView| - std::unique_ptr CreateRenderingSurface() override; + void HandlePlatformMessage(fml::RefPtr message) override; // |shell::PlatformView| - sk_sp CreateResourceContext() const override; + std::unique_ptr CreateRenderingSurface() override; // |shell::PlatformView| - void SetSemanticsEnabled(bool enabled) override; + sk_sp CreateResourceContext() const override; // |shell::PlatformView| void SetAccessibilityFeatures(int32_t flags) override; // |shell::PlatformView| - void UpdateSemantics( - blink::SemanticsNodeUpdates update, - blink::CustomAccessibilityActionUpdates actions) override; + void UpdateSemantics(blink::SemanticsNodeUpdates update, + blink::CustomAccessibilityActionUpdates actions) override; // |shell::PlatformView| std::unique_ptr CreateVSyncWaiter() override; + // |shell::PlatformView| + void OnPreEngineRestart() const override; + FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewIOS); }; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 7bf0895589346..c0b988be882ea 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,30 +12,54 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/io_manager.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" namespace shell { -PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate, - blink::TaskRunners task_runners, - FlutterViewController* owner_controller, - FlutterView* owner_view) - : HeadlessPlatformViewIOS(delegate, std::move(task_runners)), - owner_controller_(owner_controller), - owner_view_(owner_view), - ios_surface_(owner_view_.createSurface) { - FML_DCHECK(ios_surface_ != nullptr); - FML_DCHECK(owner_controller_ != nullptr); - FML_DCHECK(owner_view_ != nullptr); -} +PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate, blink::TaskRunners task_runners) + : PlatformView(delegate, std::move(task_runners)) {} PlatformViewIOS::~PlatformViewIOS() = default; -FlutterViewController* PlatformViewIOS::GetOwnerViewController() const { +PlatformMessageRouter& PlatformViewIOS::GetPlatformMessageRouter() { + return platform_message_router_; +} + +// |shell::PlatformView| +void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr message) { + platform_message_router_.HandlePlatformMessage(std::move(message)); +} + +fml::WeakPtr PlatformViewIOS::GetOwnerViewController() const { return owner_controller_; } +void PlatformViewIOS::SetOwnerViewController(fml::WeakPtr owner_controller) { + if (ios_surface_ || !owner_controller) { + NotifyDestroyed(); + ios_surface_.reset(); + accessibility_bridge_.reset(); + } + owner_controller_ = owner_controller; + if (owner_controller_) { + ios_surface_ = static_cast(owner_controller.get().view).createSurface; + FML_DCHECK(ios_surface_ != nullptr); + + if (accessibility_bridge_) { + accessibility_bridge_.reset( + new AccessibilityBridge(static_cast(owner_controller_.get().view), this, + [owner_controller.get() platformViewsController])); + } + // Do not call `NotifyCreated()` here - let FlutterViewController take care + // of that when its Viewport is sized. If `NotifyCreated()` is called here, + // it can occasionally get invoked before the viewport is sized resulting in + // a framebuffer that will not be able to completely attach. + } +} + void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, NSObject* texture) { RegisterTexture(std::make_shared(texture_id, texture)); @@ -43,24 +67,38 @@ // |shell::PlatformView| std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { + if (!ios_surface_) { + FML_DLOG(INFO) << "Could not CreateRenderingSurface, this PlatformViewIOS " + "has no ViewController."; + return nullptr; + } return ios_surface_->CreateGPUSurface(); } // |shell::PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { - if (!ios_surface_->ResourceContextMakeCurrent()) { - FML_DLOG(INFO) << "Could not make resource context current on IO thread. Async texture uploads " + if (!ios_surface_ || !ios_surface_->ResourceContextMakeCurrent()) { + FML_DLOG(INFO) << "Could not make resource context current on IO thread. " + "Async texture uploads " "will be disabled."; return nullptr; } - return IOManager::CreateCompatibleResourceLoadingContext(GrBackend::kOpenGL_GrBackend); + return IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GPUSurfaceGLDelegate::GetDefaultPlatformGLInterface()); } // |shell::PlatformView| void PlatformViewIOS::SetSemanticsEnabled(bool enabled) { + if (!owner_controller_) { + FML_LOG(WARNING) << "Could not set semantics to enabled, this " + "PlatformViewIOS has no ViewController."; + return; + } if (enabled && !accessibility_bridge_) { - accessibility_bridge_ = std::make_unique(owner_view_, this); + accessibility_bridge_ = std::make_unique( + static_cast(owner_controller_.get().view), this, + [owner_controller_.get() platformViewsController]); } else if (!enabled && accessibility_bridge_) { accessibility_bridge_.reset(); } @@ -75,8 +113,11 @@ // |shell::PlatformView| void PlatformViewIOS::UpdateSemantics(blink::SemanticsNodeUpdates update, blink::CustomAccessibilityActionUpdates actions) { + FML_DCHECK(owner_controller_); if (accessibility_bridge_) { accessibility_bridge_->UpdateSemantics(std::move(update), std::move(actions)); + [[NSNotificationCenter defaultCenter] postNotificationName:FlutterSemanticsUpdateNotification + object:owner_controller_.get()]; } } @@ -85,6 +126,16 @@ return std::make_unique(task_runners_); } +void PlatformViewIOS::OnPreEngineRestart() const { + if (accessibility_bridge_) { + accessibility_bridge_->clearState(); + } + if (!owner_controller_) { + return; + } + [owner_controller_.get() platformViewsController] -> Reset(); +} + fml::scoped_nsprotocol PlatformViewIOS::GetTextInputPlugin() const { return text_input_plugin_; } diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn new file mode 100644 index 0000000000000..87c055bbdae7e --- /dev/null +++ b/shell/platform/darwin/macos/BUILD.gn @@ -0,0 +1,196 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +assert(is_mac) + +import("//build/config/mac/mac_sdk.gni") +import("$flutter_root/common/config.gni") +import("$flutter_root/shell/platform/darwin/framework_shared.gni") + +_flutter_framework_name = "FlutterMacOS" +_flutter_framework_filename = "$_flutter_framework_name.framework" +_flutter_framework_dir = "$root_out_dir/$_flutter_framework_filename" + +# The headers that will be copied to the framework and be accessed from outside +# the Flutter engine source root. +_flutter_framework_headers = [ + "framework/Headers/FlutterMacOS.h", + "framework/Headers/FLEOpenGLContextHandling.h", + "framework/Headers/FLEPlugin.h", + "framework/Headers/FLEPluginRegistrar.h", + "framework/Headers/FLEReshapeListener.h", + "framework/Headers/FLEView.h", + "framework/Headers/FLEViewController.h", +] + +_flutter_framework_headers_copy_dir = + "$_flutter_framework_dir/Versions/A/Headers" + +shared_library("create_flutter_framework_dylib") { + visibility = [ ":*" ] + + output_name = "$_flutter_framework_name" + + sources = [ + "framework/Source/FLETextInputModel.h", + "framework/Source/FLETextInputModel.mm", + "framework/Source/FLETextInputPlugin.h", + "framework/Source/FLETextInputPlugin.mm", + "framework/Source/FLEView.mm", + "framework/Source/FLEViewController.mm", + "framework/Source/FLEViewController_Internal.h", + ] + + sources += _flutter_framework_headers + + deps = [ + "$flutter_root/shell/platform/darwin:framework_shared", + "$flutter_root/shell/platform/embedder:embedder", + ] + + public_configs = [ "$flutter_root:config" ] + + defines = [ "FLUTTER_FRAMEWORK" ] + + cflags_objcc = [ "-fobjc-arc" ] + + libs = [ "Cocoa.framework" ] +} + +copy("copy_framework_dylib") { + visibility = [ ":*" ] + + sources = [ + "$root_out_dir/lib$_flutter_framework_name.dylib", + ] + outputs = [ + "$_flutter_framework_dir/Versions/A/$_flutter_framework_name", + ] + + deps = [ + ":create_flutter_framework_dylib", + ] +} + +action("copy_dylib_and_update_framework_install_name") { + visibility = [ ":*" ] + stamp_file = "$root_out_dir/flutter_install_name_stamp" + script = "$flutter_root/sky/tools/change_install_name.py" + + inputs = [ + "$_flutter_framework_dir/Versions/A/$_flutter_framework_name", + ] + outputs = [ + stamp_file, + ] + + args = [ + "--dylib", + rebase_path("$_flutter_framework_dir/Versions/A/$_flutter_framework_name"), + "--install_name", + "@rpath/$_flutter_framework_filename/Versions/A/$_flutter_framework_name", + "--stamp", + rebase_path(stamp_file), + ] + + deps = [ + ":copy_framework_dylib", + ] +} + +copy("copy_framework_info_plist") { + visibility = [ ":*" ] + sources = [ + "framework/Info.plist", + ] + outputs = [ + "$_flutter_framework_dir/Versions/A/Resources/Info.plist", + ] +} + +copy("copy_framework_module_map") { + visibility = [ ":*" ] + sources = [ + "framework/module.modulemap", + ] + outputs = [ + "$_flutter_framework_dir/Versions/A/Modules/module.modulemap", + ] +} + +action("copy_framework_headers") { + script = "$flutter_root/sky/tools/install_framework_headers.py" + visibility = [ ":*" ] + set_sources_assignment_filter([]) + sources = get_path_info(_flutter_framework_headers, "abspath") + + framework_shared_headers + outputs = [] + foreach(header, sources) { + header_basename = get_path_info(header, "file") + outputs += [ "$_flutter_framework_headers_copy_dir/$header_basename" ] + } + args = [ + "--location", + rebase_path("$_flutter_framework_headers_copy_dir"), + "--headers", + ] + rebase_path(sources, "", "//") + set_sources_assignment_filter(sources_assignment_filter) +} + +copy("copy_framework_icu") { + visibility = [ ":*" ] + set_sources_assignment_filter([]) + sources = [ + "//third_party/icu/flutter/icudtl.dat", + ] + set_sources_assignment_filter(sources_assignment_filter) + outputs = [ + "$_flutter_framework_dir/Versions/A/Resources/{{source_file_part}}", + ] +} + +copy("copy_license") { + visibility = [ ":*" ] + sources = [ + "//LICENSE", + ] + outputs = [ + "$root_out_dir/LICENSE", + ] +} + +action("_generate_symlinks") { + visibility = [ ":*" ] + script = "//build/config/mac/package_framework.py" + outputs = [ + "$root_build_dir/$_flutter_framework_name.stamp", + ] + args = [ + "--framework", + "$_flutter_framework_filename", + "--version", + "A", + "--contents", + "$_flutter_framework_name", + "Resources", + "Headers", + "Modules", + "--stamp", + "$_flutter_framework_name.stamp", + ] + deps = [ + ":copy_dylib_and_update_framework_install_name", + ":copy_framework_headers", + ":copy_framework_icu", + ":copy_framework_info_plist", + ":copy_framework_module_map", + ":copy_license", + ] +} + +group("flutter_framework") { + deps = [ + ":_generate_symlinks", + ] +} diff --git a/shell/platform/darwin/macos/framework/Headers/FLEOpenGLContextHandling.h b/shell/platform/darwin/macos/framework/Headers/FLEOpenGLContextHandling.h new file mode 100644 index 0000000000000..bd4a7f48b0efd --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEOpenGLContextHandling.h @@ -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. + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterMacros.h" +#endif + +/** + * Protocol for views owned by FLEViewController to handle context changes, specifically relating to + * OpenGL context changes. + */ +FLUTTER_EXPORT +@protocol FLEOpenGLContextHandling + +/** + * Sets the receiver as the current context object. + */ +- (void)makeCurrentContext; + +/** + * Called when the display is updated. In an NSOpenGLView this is best handled via a flushBuffer + * call. + */ +- (void)onPresent; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FLEPlugin.h b/shell/platform/darwin/macos/framework/Headers/FLEPlugin.h new file mode 100644 index 0000000000000..830ac84b19451 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEPlugin.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter 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 + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterChannels.h" +#import "FlutterCodecs.h" +#import "FlutterMacros.h" +#endif + +@protocol FLEPluginRegistrar; + +/** + * Implemented by the platform side of a Flutter plugin. + * + * Defines a set of optional callback methods and a method to set up the plugin + * and register it to be called by other application components. + * + * Currently FLEPlugin has very limited functionality, but is expected to expand over time to + * more closely match the functionality of FlutterPlugin. + */ +FLUTTER_EXPORT +@protocol FLEPlugin + +/** + * Creates an instance of the plugin to register with |registrar| using the desired + * FLEPluginRegistrar methods. + */ ++ (void)registerWithRegistrar:(nonnull id)registrar; + +@optional + +/** + * Called when a message is sent from Flutter on a channel that a plugin instance has subscribed + * to via -[FLEPluginRegistrar addMethodCallDelegate:channel:]. + * + * The |result| callback must be called exactly once, with one of: + * - FlutterMethodNotImplemented, if the method call is unknown. + * - A FlutterError, if the method call was understood but there was a + * problem handling it. + * - Any other value (including nil) to indicate success. The value will + * be returned to the Flutter caller, and must be serializable to JSON. + */ +- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(nonnull FlutterResult)result; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FLEPluginRegistrar.h b/shell/platform/darwin/macos/framework/Headers/FLEPluginRegistrar.h new file mode 100644 index 0000000000000..b9f7ac99b2a2a --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEPluginRegistrar.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. + +#import + +#import "FLEPlugin.h" + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterBinaryMessenger.h" +#import "FlutterChannels.h" +#import "FlutterMacros.h" +#endif + +/** + * The protocol for an object managing registration for a plugin. It provides access to application + * context, as as allowing registering for callbacks for handling various conditions. + * + * Currently FLEPluginRegistrar has very limited functionality, but is expected to expand over time + * to more closely match the functionality of FlutterPluginRegistrar. + */ +FLUTTER_EXPORT +@protocol FLEPluginRegistrar + +/** + * The binary messenger used for creating channels to communicate with the Flutter engine. + */ +@property(nonnull, readonly) id messenger; + +/** + * The view displaying Flutter content. + * + * WARNING: If/when multiple Flutter views within the same application are supported (#98), this + * API will change. + */ +@property(nullable, readonly) NSView* view; + +/** + * Registers |delegate| to receive handleMethodCall:result: callbacks for the given |channel|. + */ +- (void)addMethodCallDelegate:(nonnull id)delegate + channel:(nonnull FlutterMethodChannel*)channel; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h b/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h new file mode 100644 index 0000000000000..db64cbea13044 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h @@ -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 + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterMacros.h" +#endif + +/** + * Protocol for listening to reshape events on this FlutterView. + * Used to notify the underlying Flutter engine of the new screen dimensions. + * Reflected from [NSOpenGLView.reshape]. + */ +FLUTTER_EXPORT +@protocol FLEReshapeListener + +- (void)viewDidReshape:(nonnull NSOpenGLView*)view; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FLEView.h b/shell/platform/darwin/macos/framework/Headers/FLEView.h new file mode 100644 index 0000000000000..4ba69daad5897 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEView.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. + +#import + +#import "FLEOpenGLContextHandling.h" +#import "FLEReshapeListener.h" + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterMacros.h" +#endif + +/** + * View capable of acting as a rendering target and input source for the Flutter + * engine. + */ +FLUTTER_EXPORT +@interface FLEView : NSOpenGLView + +/** + * Listener for reshape events. See protocol description. + */ +@property(nonatomic, weak, nullable) IBOutlet id reshapeListener; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FLEViewController.h b/shell/platform/darwin/macos/framework/Headers/FLEViewController.h new file mode 100644 index 0000000000000..e831934cd1911 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEViewController.h @@ -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 + +#import "FLEOpenGLContextHandling.h" +#import "FLEPluginRegistrar.h" +#import "FLEReshapeListener.h" + +#if defined(FLUTTER_FRAMEWORK) +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterMacros.h" +#else +#import "FlutterBinaryMessenger.h" +#import "FlutterMacros.h" +#endif + +typedef NS_ENUM(NSInteger, FlutterMouseTrackingMode) { + // Hover events will never be sent to Flutter. + FlutterMouseTrackingModeNone = 0, + // Hover events will be sent to Flutter when the view is in the key window. + FlutterMouseTrackingModeInKeyWindow, + // Hover events will be sent to Flutter when the view is in the active app. + FlutterMouseTrackingModeInActiveApp, + // Hover events will be sent to Flutter regardless of window and app focus. + FlutterMouseTrackingModeAlways, +}; + +/** + * Controls embedder plugins and communication with the underlying Flutter engine, managing a view + * intended to handle key inputs and drawing protocols (see |view|). + * + * Can be launched headless (no managed view), at which point a Dart executable will be run on the + * Flutter engine in non-interactive mode, or with a drawable Flutter canvas. + */ +FLUTTER_EXPORT +@interface FLEViewController + : NSViewController + +/** + * The view this controller manages when launched in interactive mode (headless set to false). Must + * be capable of handling text input events, and the OpenGL context handling protocols. + */ +@property(nullable) NSView* view; + +/** + * The style of mouse tracking to use for the view. Defaults to + * FlutterMouseTrackingModeNone. + */ +@property(nonatomic) FlutterMouseTrackingMode mouseTrackingMode; + +/** + * Launches the Flutter engine with the provided configuration. + * + * @param assets The path to the flutter_assets folder for the Flutter application to be run. + * @param arguments Arguments to pass to the Flutter engine. See + * https://github.com/flutter/engine/blob/master/shell/common/switches.h + * for details. Not all arguments will apply to embedding mode. + * Note: This API layer will likely abstract arguments in the future, instead of + * providing a direct passthrough. + * @return YES if the engine launched successfully. + */ +- (BOOL)launchEngineWithAssetsPath:(nonnull NSURL*)assets + commandLineArguments:(nullable NSArray*)arguments; + +/** + * Launches the Flutter engine in headless mode with the provided configuration. In headless mode, + * this controller's view should not be displayed. + * + * See launcheEngineWithAssetsPath:commandLineArguments: for details. + */ +- (BOOL)launchHeadlessEngineWithAssetsPath:(nonnull NSURL*)assets + commandLineArguments:(nullable NSArray*)arguments; + +/** + * Returns the FLEPluginRegistrar that should be used to register the plugin with the given name. + */ +- (nonnull id)registrarForPlugin:(nonnull NSString*)pluginName; + +@end diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h new file mode 100644 index 0000000000000..55f949119e0b8 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h @@ -0,0 +1,14 @@ +// Copyright 2013 The Flutter 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 "FLEOpenGLContextHandling.h" +#import "FLEPlugin.h" +#import "FLEPluginRegistrar.h" +#import "FLEReshapeListener.h" +#import "FLEView.h" +#import "FLEViewController.h" +#import "FlutterBinaryMessenger.h" +#import "FlutterChannels.h" +#import "FlutterCodecs.h" +#import "FlutterMacros.h" diff --git a/shell/platform/darwin/macos/framework/Info.plist b/shell/platform/darwin/macos/framework/Info.plist new file mode 100644 index 0000000000000..044a5e31b7ec8 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + FlutterMacOS + CFBundleIdentifier + io.flutter.flutter-macos + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + FlutterMacOS + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + NSPrincipalClass + + + diff --git a/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h b/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h new file mode 100644 index 0000000000000..126d9e80f24c8 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLETextInputModel.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. + +#import + +/** + * The affinity of the current cursor position. If the cursor is at a position representing + * a line break, the cursor may be drawn either at the end of the current line (upstream) + * or at the beginning of the next (downstream). + */ +typedef NS_ENUM(NSUInteger, FLETextAffinity) { FLETextAffinityUpstream, FLETextAffinityDownstream }; + +/** + * Data model representing text input state during an editing session. + */ +@interface FLETextInputModel : NSObject + +/** + * The full text being edited. + */ +@property(nonnull, copy) NSMutableString* text; +/** + * The range of text currently selected. This may have length zero to represent a single + * cursor position. + */ +@property NSRange selectedRange; +/** + * The affinity for the current cursor position. + */ +@property FLETextAffinity textAffinity; +/** + * The range of text that is marked for edit, i.e. under the effects of a multi-keystroke input + * combination. + */ +@property NSRange markedRange; + +/** + * Representation of the model's data as a state dictionary suitable for interchange with the + * Flutter Dart layer. + */ +@property(nonnull) NSDictionary* state; + +/** + * ID of the text input client. + */ +@property(nonatomic, readonly, nonnull) NSNumber* clientID; + +/** + * Keyboard type of the client. See available options: + * https://docs.flutter.io/flutter/services/TextInputType-class.html + */ +@property(nonatomic, readonly, nonnull) NSString* inputType; + +/** + * An action requested by the user on the input client. See available options: + * https://docs.flutter.io/flutter/services/TextInputAction-class.html + */ +@property(nonatomic, readonly, nonnull) NSString* inputAction; + +- (nullable instancetype)init NS_UNAVAILABLE; + +/** + * Initializes a text input model with a [clientId] and [config] arguments. [config] arguments + * provide information on the text input connection. + */ +- (nullable instancetype)initWithClientID:(nonnull NSNumber*)clientID + configuration:(nonnull NSDictionary*)config; +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLETextInputModel.mm b/shell/platform/darwin/macos/framework/Source/FLETextInputModel.mm new file mode 100644 index 0000000000000..79a5cda5e138f --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLETextInputModel.mm @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter 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/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h" + +static NSString* const kTextAffinityDownstream = @"TextAffinity.downstream"; +static NSString* const kTextAffinityUpstream = @"TextAffinity.upstream"; + +static NSString* const kTextInputAction = @"inputAction"; +static NSString* const kTextInputType = @"inputType"; +static NSString* const kTextInputTypeName = @"name"; + +static NSString* const kSelectionBaseKey = @"selectionBase"; +static NSString* const kSelectionExtentKey = @"selectionExtent"; +static NSString* const kSelectionAffinityKey = @"selectionAffinity"; +static NSString* const kSelectionIsDirectionalKey = @"selectionIsDirectional"; +static NSString* const kComposingBaseKey = @"composingBase"; +static NSString* const kComposingExtentKey = @"composingExtent"; +static NSString* const kTextKey = @"text"; + +/** + * These three static methods are necessary because Cocoa and Flutter have different idioms for + * signalling an empty range: Flutter uses {-1, -1} while Cocoa uses {NSNotFound, 0}. Also, + * despite the name, the "extent" fields are actually end indices, not lengths. + */ + +/** + * Updates a range given base and extent fields. + */ +static NSRange UpdateRangeFromBaseExtent(NSNumber* base, NSNumber* extent, NSRange range) { + if (base == nil || extent == nil) { + return range; + } + if (base.intValue == -1 && extent.intValue == -1) { + range.location = NSNotFound; + range.length = 0; + } else { + range.location = [base unsignedLongValue]; + range.length = [extent unsignedLongValue] - range.location; + } + return range; +} + +/** + * Returns the appropriate base field for a given range. + */ +static long GetBaseForRange(NSRange range) { + if (range.location == NSNotFound) { + return -1; + } + return range.location; +} + +/** + * Returns the appropriate extent field for a given range. + */ +static long GetExtentForRange(NSRange range) { + if (range.location == NSNotFound) { + return -1; + } + return range.location + range.length; +} + +@implementation FLETextInputModel + +- (instancetype)initWithClientID:(NSNumber*)clientID configuration:(NSDictionary*)config { + self = [super init]; + if (self != nil) { + _clientID = clientID; + _inputAction = config[kTextInputAction]; + // There's more information that can be used from this dictionary. + // Add more as needed. + NSDictionary* inputTypeInfo = config[kTextInputType]; + _inputType = inputTypeInfo[kTextInputTypeName]; + if (!_clientID || !_inputAction || !_inputType) { + NSLog(@"Missing arguments for %@ init.", [self class]); + return nil; + } + + _text = [[NSMutableString alloc] init]; + _selectedRange = NSMakeRange(NSNotFound, 0); + _markedRange = NSMakeRange(NSNotFound, 0); + _textAffinity = FLETextAffinityUpstream; + } + return self; +} + +- (NSDictionary*)state { + NSString* const textAffinity = + (_textAffinity == FLETextAffinityUpstream) ? kTextAffinityUpstream : kTextAffinityDownstream; + NSDictionary* state = @{ + kSelectionBaseKey : @(GetBaseForRange(_selectedRange)), + kSelectionExtentKey : @(GetExtentForRange(_selectedRange)), + kSelectionAffinityKey : textAffinity, + kSelectionIsDirectionalKey : @NO, + kComposingBaseKey : @(GetBaseForRange(_markedRange)), + kComposingExtentKey : @(GetExtentForRange(_markedRange)), + kTextKey : _text + }; + return state; +} + +- (void)setState:(NSDictionary*)state { + if (state == nil) + return; + + _selectedRange = UpdateRangeFromBaseExtent(state[kSelectionBaseKey], state[kSelectionExtentKey], + _selectedRange); + NSString* selectionAffinity = state[kSelectionAffinityKey]; + if (selectionAffinity != nil) { + _textAffinity = [selectionAffinity isEqualToString:kTextAffinityUpstream] + ? FLETextAffinityUpstream + : FLETextAffinityDownstream; + } + _markedRange = + UpdateRangeFromBaseExtent(state[kComposingBaseKey], state[kComposingExtentKey], _markedRange); + NSString* text = state[kTextKey]; + if (text != nil) + [_text setString:text]; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h new file mode 100644 index 0000000000000..c4e1a33af37f4 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.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. + +#import + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEViewController.h" + +/** + * A plugin to handle text input. + * + * Responsible for bridging the native macOS text input system with the Flutter framework text + * editing classes, via system channels. + * + * This is not an FLEPlugin since it needs access to FLEViewController internals, so needs to be + * managed differently. + */ +@interface FLETextInputPlugin : NSResponder + +/** + * Initializes a text input plugin that coordinates key event handling with |viewController|. + */ +- (instancetype)initWithViewController:(FLEViewController*)viewController; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm new file mode 100644 index 0000000000000..25079d565c07f --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm @@ -0,0 +1,309 @@ +// Copyright 2013 The Flutter 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/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h" + +#import + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h" + +static NSString* const kTextInputChannel = @"flutter/textinput"; + +// See https://docs.flutter.io/flutter/services/SystemChannels/textInput-constant.html +static NSString* const kSetClientMethod = @"TextInput.setClient"; +static NSString* const kShowMethod = @"TextInput.show"; +static NSString* const kHideMethod = @"TextInput.hide"; +static NSString* const kClearClientMethod = @"TextInput.clearClient"; +static NSString* const kSetEditingStateMethod = @"TextInput.setEditingState"; +static NSString* const kUpdateEditStateResponseMethod = @"TextInputClient.updateEditingState"; +static NSString* const kPerformAction = @"TextInputClient.performAction"; +static NSString* const kMultilineInputType = @"TextInputType.multiline"; + +/** + * Private properties of FlutterTextInputPlugin. + */ +@interface FLETextInputPlugin () + +/** + * A text input context, representing a connection to the Cocoa text input system. + */ +@property(nonatomic) NSTextInputContext* textInputContext; + +/** + * A dictionary of text input models, one per client connection, keyed + * by the client connection ID. + */ +@property(nonatomic) NSMutableDictionary* textInputModels; + +/** + * The currently active client connection ID. + */ +@property(nonatomic, nullable) NSNumber* activeClientID; + +/** + * The currently active text input model. + */ +@property(nonatomic, readonly, nullable) FLETextInputModel* activeModel; + +/** + * The channel used to communicate with Flutter. + */ +@property(nonatomic) FlutterMethodChannel* channel; + +/** + * The FLEViewController to manage input for. + */ +@property(nonatomic, weak) FLEViewController* flutterViewController; + +/** + * Handles a Flutter system message on the text input channel. + */ +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; + +@end + +@implementation FLETextInputPlugin + +- (instancetype)initWithViewController:(FLEViewController*)viewController { + self = [super init]; + if (self != nil) { + _flutterViewController = viewController; + _channel = [FlutterMethodChannel methodChannelWithName:kTextInputChannel + binaryMessenger:viewController + codec:[FlutterJSONMethodCodec sharedInstance]]; + __weak FLETextInputPlugin* weakSelf = self; + [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [weakSelf handleMethodCall:call result:result]; + }]; + _textInputModels = [[NSMutableDictionary alloc] init]; + _textInputContext = [[NSTextInputContext alloc] initWithClient:self]; + } + return self; +} + +#pragma mark - Private + +- (FLETextInputModel*)activeModel { + return (_activeClientID == nil) ? nil : _textInputModels[_activeClientID]; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + BOOL handled = YES; + NSString* method = call.method; + if ([method isEqualToString:kSetClientMethod]) { + if (!call.arguments[0] || !call.arguments[1]) { + result([FlutterError + errorWithCode:@"error" + message:@"Missing arguments" + details:@"Missing arguments while trying to set a text input client"]); + return; + } + NSNumber* clientID = call.arguments[0]; + if (clientID != nil && + (_activeClientID == nil || ![_activeClientID isEqualToNumber:clientID])) { + _activeClientID = clientID; + // TODO: Do we need to preserve state across setClient calls? + FLETextInputModel* inputModel = + [[FLETextInputModel alloc] initWithClientID:clientID configuration:call.arguments[1]]; + if (!inputModel) { + result([FlutterError errorWithCode:@"error" + message:@"Failed to create an input model" + details:@"Configuration arguments might be missing"]); + return; + } + _textInputModels[_activeClientID] = inputModel; + } + } else if ([method isEqualToString:kShowMethod]) { + [self.flutterViewController addKeyResponder:self]; + [_textInputContext activate]; + } else if ([method isEqualToString:kHideMethod]) { + [self.flutterViewController removeKeyResponder:self]; + [_textInputContext deactivate]; + } else if ([method isEqualToString:kClearClientMethod]) { + _activeClientID = nil; + } else if ([method isEqualToString:kSetEditingStateMethod]) { + NSDictionary* state = call.arguments; + self.activeModel.state = state; + } else { + handled = NO; + NSLog(@"Unhandled text input method '%@'", method); + } + result(handled ? nil : FlutterMethodNotImplemented); +} + +/** + * Informs the Flutter framework of changes to the text input model's state. + */ +- (void)updateEditState { + if (self.activeModel == nil) { + return; + } + + [_channel invokeMethod:kUpdateEditStateResponseMethod + arguments:@[ _activeClientID, _textInputModels[_activeClientID].state ]]; +} + +#pragma mark - +#pragma mark NSResponder + +/** + * Note, the Apple docs suggest that clients should override essentially all the + * mouse and keyboard event-handling methods of NSResponder. However, experimentation + * indicates that only key events are processed by the native layer; Flutter processes + * mouse events. Additionally, processing both keyUp and keyDown results in duplicate + * processing of the same keys. So for now, limit processing to just keyDown. + */ +- (void)keyDown:(NSEvent*)event { + [_textInputContext handleEvent:event]; +} + +#pragma mark - +#pragma mark NSStandardKeyBindingMethods + +/** + * Note, experimentation indicates that moveRight and moveLeft are called rather + * than the supposedly more RTL-friendly moveForward and moveBackward. + */ +- (void)moveLeft:(nullable id)sender { + NSRange selection = self.activeModel.selectedRange; + if (selection.length == 0) { + if (selection.location > 0) { + // Move to previous location + self.activeModel.selectedRange = NSMakeRange(selection.location - 1, 0); + [self updateEditState]; + } + } else { + // Collapse current selection + self.activeModel.selectedRange = NSMakeRange(selection.location, 0); + [self updateEditState]; + } +} + +- (void)moveRight:(nullable id)sender { + NSRange selection = self.activeModel.selectedRange; + if (selection.length == 0) { + if (selection.location < self.activeModel.text.length) { + // Move to next location + self.activeModel.selectedRange = NSMakeRange(selection.location + 1, 0); + [self updateEditState]; + } + } else { + // Collapse current selection + self.activeModel.selectedRange = NSMakeRange(selection.location + selection.length, 0); + [self updateEditState]; + } +} + +- (void)deleteBackward:(id)sender { + NSRange selection = self.activeModel.selectedRange; + if (selection.location == 0) + return; + NSRange range = selection; + if (selection.length == 0) { + NSUInteger location = (selection.location == NSNotFound) ? self.activeModel.text.length - 1 + : selection.location - 1; + range = NSMakeRange(location, 1); + } + self.activeModel.selectedRange = NSMakeRange(range.location, 0); + [self insertText:@"" replacementRange:range]; // Updates edit state +} + +#pragma mark - +#pragma mark NSTextInputClient + +- (void)insertText:(id)string replacementRange:(NSRange)range { + if (self.activeModel != nil) { + if (range.location == NSNotFound && range.length == 0) { + // Use selection + range = self.activeModel.selectedRange; + } + if (range.location > self.activeModel.text.length) + range.location = self.activeModel.text.length; + if (range.length > (self.activeModel.text.length - range.location)) + range.length = self.activeModel.text.length - range.location; + [self.activeModel.text replaceCharactersInRange:range withString:string]; + self.activeModel.selectedRange = NSMakeRange(range.location + ((NSString*)string).length, 0); + [self updateEditState]; + } +} + +- (void)doCommandBySelector:(SEL)selector { + if ([self respondsToSelector:selector]) { + // Note: The more obvious [self performSelector...] doesn't give ARC enough information to + // handle retain semantics properly. See https://stackoverflow.com/questions/7017281/ for more + // information. + IMP imp = [self methodForSelector:selector]; + void (*func)(id, SEL, id) = reinterpret_cast(imp); + func(self, selector, nil); + } +} + +- (void)insertNewline:(id)sender { + if ([self.activeModel.inputType isEqualToString:kMultilineInputType]) { + [self insertText:@"\n" replacementRange:self.activeModel.selectedRange]; + } + [_channel invokeMethod:kPerformAction + arguments:@[ _activeClientID, self.activeModel.inputAction ]]; +} + +- (void)setMarkedText:(id)string + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange { + if (self.activeModel != nil) { + [self.activeModel.text replaceCharactersInRange:replacementRange withString:string]; + self.activeModel.selectedRange = selectedRange; + [self updateEditState]; + } +} + +- (void)unmarkText { + if (self.activeModel != nil) { + self.activeModel.markedRange = NSMakeRange(NSNotFound, 0); + [self updateEditState]; + } +} + +- (NSRange)selectedRange { + return (self.activeModel == nil) ? NSMakeRange(NSNotFound, 0) : self.activeModel.selectedRange; +} + +- (NSRange)markedRange { + return (self.activeModel == nil) ? NSMakeRange(NSNotFound, 0) : self.activeModel.markedRange; +} + +- (BOOL)hasMarkedText { + return (self.activeModel == nil) ? NO : self.activeModel.markedRange.location != NSNotFound; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange:(NSRangePointer)actualRange { + if (self.activeModel) { + if (actualRange != nil) + *actualRange = range; + NSString* substring = [self.activeModel.text substringWithRange:range]; + return [[NSAttributedString alloc] initWithString:substring attributes:nil]; + } else { + return nil; + } +} + +- (NSArray*)validAttributesForMarkedText { + return @[]; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { + // TODO: Implement. + // Note: This function can't easily be implemented under the system-message architecture. + return CGRectZero; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point { + // TODO: Implement. + // Note: This function can't easily be implemented under the system-message architecture. + return 0; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLEView.mm b/shell/platform/darwin/macos/framework/Source/FLEView.mm new file mode 100644 index 0000000000000..7d67fc33c0abc --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLEView.mm @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter 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/shell/platform/darwin/macos/framework/Headers/FLEView.h" + +@implementation FLEView + +#pragma mark - +#pragma mark FLEContextHandlingProtocol + +- (void)makeCurrentContext { + [self.openGLContext makeCurrentContext]; +} + +- (void)onPresent { + [self.openGLContext flushBuffer]; +} + +#pragma mark - +#pragma mark Implementation + +/** + * Declares that the view uses a flipped coordinate system, consistent with Flutter conventions. + */ +- (BOOL)isFlipped { + return YES; +} + +- (BOOL)isOpaque { + return YES; +} + +- (void)reshape { + [super reshape]; + [_reshapeListener viewDidReshape:self]; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm new file mode 100644 index 0000000000000..b0f732e31015b --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm @@ -0,0 +1,586 @@ +// Copyright 2013 The Flutter 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/shell/platform/darwin/macos/framework/Headers/FLEViewController.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h" + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEView.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h" +#import "flutter/shell/platform/embedder/embedder.h" + +static NSString* const kICUBundlePath = @"icudtl.dat"; + +static const int kDefaultWindowFramebuffer = 0; + +#pragma mark - Private interface declaration. + +/** + * Private interface declaration for FLEViewController. + */ +@interface FLEViewController () + +/** + * A list of additional responders to keyboard events. Keybord events are forwarded to all of them. + */ +@property(nonatomic) NSMutableOrderedSet* additionalKeyResponders; + +/** + * The tracking area used to generate hover events, if enabled. + */ +@property(nonatomic) NSTrackingArea* trackingArea; + +/** + * Whether or not a kAdd event has been sent for the mouse (or sent again since + * the last kRemove was sent if tracking is enabled). Used to determine whether + * to send an Add event before sending an incoming mouse event, since Flutter + * expects a pointers to be added before events are sent for them. + */ +@property(nonatomic) BOOL mouseCurrentlyAdded; + +/** + * Updates |trackingArea| for the current tracking settings, creating it with + * the correct mode if tracking is enabled, or removing it if not. + */ +- (void)configureTrackingArea; + +/** + * Creates and registers plugins used by this view controller. + */ +- (void)addInternalPlugins; + +/** + * Shared implementation of the regular and headless public APIs. + */ +- (BOOL)launchEngineInternalWithAssetsPath:(nonnull NSURL*)assets + headless:(BOOL)headless + commandLineArguments:(nullable NSArray*)arguments; + +/** + * Creates a render config with callbacks based on whether the embedder is being run as a headless + * server. + */ ++ (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless; + +/** + * Creates the OpenGL context used as the resource context by the engine. + */ +- (void)createResourceContext; + +/** + * Makes the OpenGL context used by the engine for rendering optimization the + * current context. + */ +- (void)makeResourceContextCurrent; + +/** + * Responds to system messages sent to this controller from the Flutter engine. + */ +- (void)handlePlatformMessage:(const FlutterPlatformMessage*)message; + +/** + * Converts |event| to a FlutterPointerEvent with the given phase, and sends it to the engine. + */ +- (void)dispatchMouseEvent:(nonnull NSEvent*)event phase:(FlutterPointerPhase)phase; + +/** + * Converts |event| to a key event channel message, and sends it to the engine. + */ +- (void)dispatchKeyEvent:(NSEvent*)event ofType:(NSString*)type; + +@end + +#pragma mark - Static methods provided to engine configuration + +/** + * Makes the owned FlutterView the current context. + */ +static bool OnMakeCurrent(FLEViewController* controller) { + [controller.view makeCurrentContext]; + return true; +} + +/** + * Clears the current context. + */ +static bool OnClearCurrent(FLEViewController* controller) { + [NSOpenGLContext clearCurrentContext]; + return true; +} + +/** + * Flushes the GL context as part of the Flutter rendering pipeline. + */ +static bool OnPresent(FLEViewController* controller) { + [controller.view onPresent]; + return true; +} + +/** + * Returns the framebuffer object whose color attachment the engine should render into. + */ +static uint32_t OnFBO(FLEViewController* controller) { + return kDefaultWindowFramebuffer; +} + +/** + * Handles the given platform message by dispatching to the controller. + */ +static void OnPlatformMessage(const FlutterPlatformMessage* message, + FLEViewController* controller) { + [controller handlePlatformMessage:message]; +} + +/** + * Makes the resource context the current context. + */ +static bool OnMakeResourceCurrent(FLEViewController* controller) { + [controller makeResourceContextCurrent]; + return true; +} + +#pragma mark Static methods provided for headless engine configuration + +static bool HeadlessOnMakeCurrent(FLEViewController* controller) { + return false; +} + +static bool HeadlessOnClearCurrent(FLEViewController* controller) { + return false; +} + +static bool HeadlessOnPresent(FLEViewController* controller) { + return false; +} + +static uint32_t HeadlessOnFBO(FLEViewController* controller) { + return kDefaultWindowFramebuffer; +} + +static bool HeadlessOnMakeResourceCurrent(FLEViewController* controller) { + return false; +} + +#pragma mark - FLEViewController implementation. + +@implementation FLEViewController { + FlutterEngine _engine; + + // The additional context provided to the Flutter engine for resource loading. + NSOpenGLContext* _resourceContext; + + // A mapping of channel names to the registered handlers for those channels. + NSMutableDictionary* _messageHandlers; + + // The plugin used to handle text input. This is not an FLEPlugin, so must be owned separately. + FLETextInputPlugin* _textInputPlugin; + + // A message channel for passing key events to the Flutter engine. This should be replaced with + // an embedding API; see Issue #47. + FlutterBasicMessageChannel* _keyEventChannel; +} + +@dynamic view; + +/** + * Performs initialization that's common between the different init paths. + */ +static void CommonInit(FLEViewController* controller) { + controller->_messageHandlers = [[NSMutableDictionary alloc] init]; + controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; +} + +- (instancetype)initWithCoder:(NSCoder*)coder { + self = [super initWithCoder:coder]; + if (self != nil) { + CommonInit(self); + } + return self; +} + +- (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self != nil) { + CommonInit(self); + } + return self; +} + +- (void)dealloc { + if (FlutterEngineShutdown(_engine) == kSuccess) { + _engine = NULL; + } +} + +- (void)setView:(NSView*)view { + if (_trackingArea) { + [self.view removeTrackingArea:_trackingArea]; + } + [super setView:view]; + [self configureTrackingArea]; +} + +- (void)loadView { + self.view = [[FLEView alloc] init]; +} + +#pragma mark - Public methods + +- (void)setMouseTrackingMode:(FlutterMouseTrackingMode)mode { + if (_mouseTrackingMode == mode) { + return; + } + _mouseTrackingMode = mode; + [self configureTrackingArea]; +} + +- (BOOL)launchEngineWithAssetsPath:(NSURL*)assets + commandLineArguments:(NSArray*)arguments { + return [self launchEngineInternalWithAssetsPath:assets + headless:NO + commandLineArguments:arguments]; +} + +- (BOOL)launchHeadlessEngineWithAssetsPath:(NSURL*)assets + commandLineArguments:(NSArray*)arguments { + return [self launchEngineInternalWithAssetsPath:assets + headless:YES + commandLineArguments:arguments]; +} + +- (id)registrarForPlugin:(NSString*)pluginName { + // Currently, the view controller acts as the registrar for all plugins, so the + // name is ignored. It is part of the API to reduce churn in the future when + // aligning more closely with the Flutter registrar system. + return self; +} + +#pragma mark - Framework-internal methods + +- (void)addKeyResponder:(NSResponder*)responder { + [self.additionalKeyResponders addObject:responder]; +} + +- (void)removeKeyResponder:(NSResponder*)responder { + [self.additionalKeyResponders removeObject:responder]; +} + +#pragma mark - Private methods + +- (void)configureTrackingArea { + if (_mouseTrackingMode != FlutterMouseTrackingModeNone && self.view) { + NSTrackingAreaOptions options = + NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingInVisibleRect; + switch (_mouseTrackingMode) { + case FlutterMouseTrackingModeInKeyWindow: + options |= NSTrackingActiveInKeyWindow; + break; + case FlutterMouseTrackingModeInActiveApp: + options |= NSTrackingActiveInActiveApp; + break; + case FlutterMouseTrackingModeAlways: + options |= NSTrackingActiveAlways; + break; + default: + NSLog(@"Error: Unrecognized mouse tracking mode: %ld", _mouseTrackingMode); + return; + } + _trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect + options:options + owner:self + userInfo:nil]; + [self.view addTrackingArea:_trackingArea]; + } else if (_trackingArea) { + [self.view removeTrackingArea:_trackingArea]; + _trackingArea = nil; + } +} + +- (void)addInternalPlugins { + _textInputPlugin = [[FLETextInputPlugin alloc] initWithViewController:self]; + _keyEventChannel = + [FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent" + binaryMessenger:self + codec:[FlutterJSONMessageCodec sharedInstance]]; +} + +- (BOOL)launchEngineInternalWithAssetsPath:(NSURL*)assets + headless:(BOOL)headless + commandLineArguments:(NSArray*)arguments { + if (_engine != NULL) { + return NO; + } + + // Set up the resource context. This is done here rather than in viewDidLoad as there's no + // guarantee that viewDidLoad will be called before the engine is started, and the context must + // be valid by that point. + [self createResourceContext]; + + const FlutterRendererConfig config = [FLEViewController createRenderConfigHeadless:headless]; + + // Register internal plugins before starting the engine. + [self addInternalPlugins]; + + // FlutterProjectArgs is expecting a full argv, so when processing it for flags the first + // item is treated as the executable and ignored. Add a dummy value so that all provided arguments + // are used. + const unsigned long argc = arguments.count + 1; + const char** argv = (const char**)malloc(argc * sizeof(const char*)); + argv[0] = "placeholder"; + for (NSUInteger i = 0; i < arguments.count; ++i) { + argv[i + 1] = [arguments[i] UTF8String]; + } + + NSString* icuData = [[NSBundle bundleForClass:[self class]] pathForResource:kICUBundlePath + ofType:nil]; + + FlutterProjectArgs flutterArguments = {}; + flutterArguments.struct_size = sizeof(FlutterProjectArgs); + flutterArguments.assets_path = assets.fileSystemRepresentation; + flutterArguments.icu_data_path = icuData.UTF8String; + flutterArguments.command_line_argc = (int)(argc); + flutterArguments.command_line_argv = argv; + flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; + + FlutterEngineResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, + (__bridge void*)(self), &_engine); + free(argv); + if (result != kSuccess) { + NSLog(@"Failed to start Flutter engine: error %d", result); + return NO; + } + return YES; +} + ++ (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless { + if (headless) { + const FlutterRendererConfig config = { + .type = kOpenGL, + .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), + .open_gl.make_current = (BoolCallback)HeadlessOnMakeCurrent, + .open_gl.clear_current = (BoolCallback)HeadlessOnClearCurrent, + .open_gl.present = (BoolCallback)HeadlessOnPresent, + .open_gl.fbo_callback = (UIntCallback)HeadlessOnFBO, + .open_gl.make_resource_current = (BoolCallback)HeadlessOnMakeResourceCurrent}; + return config; + } else { + const FlutterRendererConfig config = { + .type = kOpenGL, + .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), + .open_gl.make_current = (BoolCallback)OnMakeCurrent, + .open_gl.clear_current = (BoolCallback)OnClearCurrent, + .open_gl.present = (BoolCallback)OnPresent, + .open_gl.fbo_callback = (UIntCallback)OnFBO, + .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent}; + return config; + } +} + +- (void)createResourceContext { + NSOpenGLContext* viewContext = ((NSOpenGLView*)self.view).openGLContext; + _resourceContext = [[NSOpenGLContext alloc] initWithFormat:viewContext.pixelFormat + shareContext:viewContext]; +} + +- (void)makeResourceContextCurrent { + [_resourceContext makeCurrentContext]; +} + +- (void)handlePlatformMessage:(const FlutterPlatformMessage*)message { + NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message + length:message->message_size + freeWhenDone:NO]; + NSString* channel = @(message->channel); + __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle; + + FlutterBinaryReply binaryResponseHandler = ^(NSData* response) { + if (responseHandle) { + FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, + static_cast(response.bytes), + response.length); + responseHandle = NULL; + } else { + NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response " + "on channel '%@'.", + channel); + } + }; + + FlutterBinaryMessageHandler channelHandler = _messageHandlers[channel]; + if (channelHandler) { + channelHandler(messageData, binaryResponseHandler); + } else { + binaryResponseHandler(nil); + } +} + +- (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase { + // If a pointer added event hasn't been sent, synthesize one using this event for the basic + // information. + if (!_mouseCurrentlyAdded && phase != kAdd) { + // Only the values extracted for use in flutterEvent below matter, the rest are dummy values. + NSEvent* addEvent = [NSEvent enterExitEventWithType:NSEventTypeMouseEntered + location:event.locationInWindow + modifierFlags:0 + timestamp:event.timestamp + windowNumber:event.windowNumber + context:nil + eventNumber:0 + trackingNumber:0 + userData:NULL]; + [self dispatchMouseEvent:addEvent phase:kAdd]; + } + + NSPoint locationInView = [self.view convertPoint:event.locationInWindow fromView:nil]; + NSPoint locationInBackingCoordinates = [self.view convertPointToBacking:locationInView]; + FlutterPointerEvent flutterEvent = { + .struct_size = sizeof(flutterEvent), + .phase = phase, + .x = locationInBackingCoordinates.x, + .y = -locationInBackingCoordinates.y, // convertPointToBacking makes this negative. + .timestamp = static_cast(event.timestamp * NSEC_PER_MSEC), + }; + + if (event.type == NSEventTypeScrollWheel) { + flutterEvent.signal_kind = kFlutterPointerSignalKindScroll; + + double pixelsPerLine = 1.0; + if (!event.hasPreciseScrollingDeltas) { + CGEventSourceRef source = CGEventCreateSourceFromEvent(event.CGEvent); + pixelsPerLine = CGEventSourceGetPixelsPerLine(source); + if (source) { + CFRelease(source); + } + } + double scaleFactor = self.view.layer.contentsScale; + flutterEvent.scroll_delta_x = event.scrollingDeltaX * pixelsPerLine * scaleFactor; + flutterEvent.scroll_delta_y = -event.scrollingDeltaY * pixelsPerLine * scaleFactor; + } + FlutterEngineSendPointerEvent(_engine, &flutterEvent, 1); + + if (phase == kAdd) { + _mouseCurrentlyAdded = YES; + } else if (phase == kRemove) { + _mouseCurrentlyAdded = NO; + } +} + +- (void)dispatchKeyEvent:(NSEvent*)event ofType:(NSString*)type { + [_keyEventChannel sendMessage:@{ + @"keymap" : @"macos", + @"type" : type, + @"keyCode" : @(event.keyCode), + @"modifiers" : @(event.modifierFlags), + @"characters" : event.characters, + @"charactersIgnoringModifiers" : event.charactersIgnoringModifiers, + }]; +} + +#pragma mark - FLEReshapeListener + +/** + * Responds to view reshape by notifying the engine of the change in dimensions. + */ +- (void)viewDidReshape:(NSOpenGLView*)view { + CGRect scaledBounds = [view convertRectToBacking:view.bounds]; + const FlutterWindowMetricsEvent event = { + .struct_size = sizeof(event), + .width = static_cast(scaledBounds.size.width), + .height = static_cast(scaledBounds.size.height), + .pixel_ratio = scaledBounds.size.width / view.bounds.size.width, + }; + FlutterEngineSendWindowMetricsEvent(_engine, &event); +} + +#pragma mark - FlutterBinaryMessenger + +- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { + FlutterPlatformMessage platformMessage = { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = [channel UTF8String], + .message = static_cast(message.bytes), + .message_size = message.length, + }; + + FlutterEngineResult result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); + if (result != kSuccess) { + NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, result); + } +} + +- (void)setMessageHandlerOnChannel:(nonnull NSString*)channel + binaryMessageHandler:(nullable FlutterBinaryMessageHandler)handler { + _messageHandlers[channel] = [handler copy]; +} + +#pragma mark - FLEPluginRegistrar + +- (id)messenger { + return self; +} + +- (void)addMethodCallDelegate:(nonnull id)delegate + channel:(nonnull FlutterMethodChannel*)channel { + [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [delegate handleMethodCall:call result:result]; + }]; +} + +#pragma mark - NSResponder + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (void)keyDown:(NSEvent*)event { + [self dispatchKeyEvent:event ofType:@"keydown"]; + for (NSResponder* responder in self.additionalKeyResponders) { + if ([responder respondsToSelector:@selector(keyDown:)]) { + [responder keyDown:event]; + } + } +} + +- (void)keyUp:(NSEvent*)event { + [self dispatchKeyEvent:event ofType:@"keyup"]; + for (NSResponder* responder in self.additionalKeyResponders) { + if ([responder respondsToSelector:@selector(keyUp:)]) { + [responder keyUp:event]; + } + } +} + +- (void)mouseDown:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kDown]; +} + +- (void)mouseUp:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kUp]; +} + +- (void)mouseDragged:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kMove]; +} + +- (void)mouseEntered:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kAdd]; +} + +- (void)mouseExited:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kRemove]; +} + +- (void)mouseMoved:(NSEvent*)event { + [self dispatchMouseEvent:event phase:kHover]; +} + +- (void)scrollWheel:(NSEvent*)event { + // TODO: Add gesture-based (trackpad) scroll support once it's supported by the engine rather + // than always using kHover. + [self dispatchMouseEvent:event phase:kHover]; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h new file mode 100644 index 0000000000000..8fd3fb26519c8 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter 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/shell/platform/darwin/macos/framework/Headers/FLEViewController.h" + +@interface FLEViewController () + +/** + * Adds a responder for keyboard events. Key up and key down events are forwarded to all added + * responders. + */ +- (void)addKeyResponder:(nonnull NSResponder*)responder; + +/** + * Removes a responder for keyboard events. + */ +- (void)removeKeyResponder:(nonnull NSResponder*)responder; + +@end diff --git a/shell/platform/darwin/macos/framework/module.modulemap b/shell/platform/darwin/macos/framework/module.modulemap new file mode 100644 index 0000000000000..3e70a88b7c220 --- /dev/null +++ b/shell/platform/darwin/macos/framework/module.modulemap @@ -0,0 +1,6 @@ +framework module FlutterMacOS { + umbrella header "FlutterMacOS.h" + + export * + module * { export * } +} diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index f00f6921968d4..5632f5f0fd5dd 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -1,10 +1,11 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_root/testing/testing.gni") +import("$flutter_root/common/config.gni") import("$flutter_root/shell/gpu/gpu.gni") import("$flutter_root/shell/platform/embedder/embedder.gni") +import("$flutter_root/testing/testing.gni") shell_gpu_configuration("embedder_gpu_configuration") { enable_software = true @@ -18,31 +19,47 @@ source_set("embedder") { "embedder.h", "embedder_engine.cc", "embedder_engine.h", + "embedder_external_texture_gl.cc", + "embedder_external_texture_gl.h", "embedder_include.c", + "embedder_safe_access.h", "embedder_surface.cc", "embedder_surface.h", "embedder_surface_gl.cc", "embedder_surface_gl.h", "embedder_surface_software.cc", "embedder_surface_software.h", + "embedder_task_runner.cc", + "embedder_task_runner.h", + "embedder_thread_host.cc", + "embedder_thread_host.h", "platform_view_embedder.cc", "platform_view_embedder.h", + "vsync_waiter_embedder.cc", + "vsync_waiter_embedder.h", ] deps = [ ":embedder_gpu_configuration", "$flutter_root/assets", "$flutter_root/common", + "$flutter_root/flow", "$flutter_root/fml", - "$flutter_root/lib/snapshot", "$flutter_root/shell/common", - "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/bin:dart_io_api", "//third_party/skia", - "//third_party/skia:gpu", "//third_party/tonic", ] + if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { + deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] + } else { + deps += [ + "$flutter_root/lib/snapshot", + "//third_party/dart/runtime:libdart_jit", + ] + } + public_configs = [ "$flutter_root:config" ] } @@ -56,13 +73,52 @@ executable("embedder_unittests") { include_dirs = [ "." ] sources = [ + "tests/embedder_config_builder.cc", + "tests/embedder_config_builder.h", + "tests/embedder_context.cc", + "tests/embedder_context.h", + "tests/embedder_test.cc", + "tests/embedder_test.h", + "tests/embedder_test_resolver.cc", + "tests/embedder_test_resolver.h", "tests/embedder_unittests.cc", ] deps = [ ":embedder", ":fixtures", + "$flutter_root/runtime", + "$flutter_root/testing", + "//third_party/skia", + "//third_party/tonic", + ] + + if (is_linux) { + ldflags = [ "-rdynamic" ] + } +} + +test_fixtures("fixtures_a11y") { + fixtures = [ "fixtures/a11y_main.dart" ] +} + +executable("embedder_a11y_unittests") { + testonly = true + + include_dirs = [ "." ] + + sources = [ + "tests/embedder_a11y_unittests.cc", + ] + + deps = [ + ":embedder", + ":fixtures_a11y", + "$flutter_root/lib/ui:ui", + "$flutter_root/shell/common", "$flutter_root/testing", + "//third_party/skia", + "//third_party/tonic", ] if (is_linux) { diff --git a/shell/platform/embedder/assets/EmbedderInfo.plist b/shell/platform/embedder/assets/EmbedderInfo.plist index 38352b319ff5b..02a4027bfb826 100644 --- a/shell/platform/embedder/assets/EmbedderInfo.plist +++ b/shell/platform/embedder/assets/EmbedderInfo.plist @@ -7,7 +7,7 @@ CFBundleExecutable FlutterEmbedder CFBundleIdentifier - io.flutter.flutter_embedder + io.flutter.flutter-embedder CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -23,6 +23,6 @@ CFBundleVersion 1 NSHumanReadableCopyright - Copyright 2017 The Flutter Authors. All rights reserved. + Copyright 2013 The Flutter Authors. All rights reserved. diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b8483616b1464..93c23b12207e1 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,9 +13,13 @@ #define FLUTTER_EXPORT __attribute__((visibility("default"))) #endif // OS_WIN -#include "flutter/shell/platform/embedder/embedder.h" - -#include +extern "C" { +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +// Used for debugging dart:* sources. +extern const uint8_t kPlatformStrongDill[]; +extern const intptr_t kPlatformStrongDillSize; +#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +} #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/task_runners.h" @@ -24,21 +28,30 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/paths.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder_engine.h" +#include "flutter/shell/platform/embedder/embedder_safe_access.h" +#include "flutter/shell/platform/embedder/embedder_task_runner.h" +#include "flutter/shell/platform/embedder/embedder_thread_host.h" #include "flutter/shell/platform/embedder/platform_view_embedder.h" -#define SAFE_ACCESS(pointer, member, default_value) \ - ([=]() { \ - if (offsetof(std::remove_pointer::type, member) + \ - sizeof(pointer->member) <= \ - pointer->struct_size) { \ - return pointer->member; \ - } \ - return static_castmember)>((default_value)); \ - })() +static FlutterEngineResult LogEmbedderError(FlutterEngineResult code, + const char* name, + const char* function, + const char* file, + int line) { + FML_LOG(ERROR) << "Returning error '" << name << "' (" << code + << ") from Flutter Embedder API call to '" << __FUNCTION__ + << "'. Origin: " << file << ":" << line; + return code; +} + +#define LOG_EMBEDDER_ERROR(code) \ + LogEmbedderError(code, #code, __FUNCTION__, __FILE__, __LINE__) static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) { if (config->type != kOpenGL) { @@ -90,14 +103,16 @@ static bool IsRendererValid(const FlutterRendererConfig* config) { } #if OS_LINUX || OS_WIN - static void* DefaultGLProcResolver(const char* name) { static fml::RefPtr proc_library = +#if OS_LINUX fml::NativeLibrary::CreateForCurrentProcess(); +#elif OS_WIN // OS_LINUX + fml::NativeLibrary::Create("opengl32.dll"); +#endif // OS_WIN return static_cast( const_cast(proc_library->ResolveSymbol(name))); } - #endif // OS_LINUX || OS_WIN static shell::Shell::CreateCallback @@ -246,32 +261,83 @@ struct _FlutterPlatformMessageResponseHandle { fml::RefPtr message; }; -FlutterResult FlutterEngineRun(size_t version, - const FlutterRendererConfig* config, - const FlutterProjectArgs* args, - void* user_data, - FlutterEngine* engine_out) { +void PopulateSnapshotMappingCallbacks(const FlutterProjectArgs* args, + blink::Settings& settings) { + // There are no ownership concerns here as all mappings are owned by the + // embedder and not the engine. + auto make_mapping_callback = [](const uint8_t* mapping, size_t size) { + return [mapping, size]() { + return std::make_unique(mapping, size); + }; + }; + + if (blink::DartVM::IsRunningPrecompiledCode()) { + if (SAFE_ACCESS(args, vm_snapshot_data_size, 0) != 0 && + SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) { + settings.vm_snapshot_data = make_mapping_callback( + args->vm_snapshot_data, args->vm_snapshot_data_size); + } + + if (SAFE_ACCESS(args, vm_snapshot_instructions_size, 0) != 0 && + SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) { + settings.vm_snapshot_instr = make_mapping_callback( + args->vm_snapshot_instructions, args->vm_snapshot_instructions_size); + } + + if (SAFE_ACCESS(args, isolate_snapshot_data_size, 0) != 0 && + SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) { + settings.isolate_snapshot_data = make_mapping_callback( + args->isolate_snapshot_data, args->isolate_snapshot_data_size); + } + + if (SAFE_ACCESS(args, isolate_snapshot_instructions_size, 0) != 0 && + SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) { + settings.isolate_snapshot_instr = + make_mapping_callback(args->isolate_snapshot_instructions, + args->isolate_snapshot_instructions_size); + } + } + +#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) + settings.dart_library_sources_kernel = + make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize); +#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) +} + +FlutterEngineResult FlutterEngineRun(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FlutterEngine* engine_out) { // Step 0: Figure out arguments for shell creation. if (version != FLUTTER_ENGINE_VERSION) { - return kInvalidLibraryVersion; + return LOG_EMBEDDER_ERROR(kInvalidLibraryVersion); } if (engine_out == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } if (args == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } - if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr || - SAFE_ACCESS(args, main_path, nullptr) == nullptr || - SAFE_ACCESS(args, packages_path, nullptr) == nullptr) { - return kInvalidArguments; + if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + if (SAFE_ACCESS(args, main_path__unused__, nullptr) != nullptr) { + FML_LOG(WARNING) + << "FlutterProjectArgs.main_path is deprecated and should be set null."; + } + + if (SAFE_ACCESS(args, packages_path__unused__, nullptr) != nullptr) { + FML_LOG(WARNING) << "FlutterProjectArgs.packages_path is deprecated and " + "should be set null."; } if (!IsRendererValid(config)) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } std::string icu_data_path; @@ -279,6 +345,16 @@ FlutterResult FlutterEngineRun(size_t version, icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr); } + if (SAFE_ACCESS(args, persistent_cache_path, nullptr) != nullptr) { + std::string persistent_cache_path = + SAFE_ACCESS(args, persistent_cache_path, nullptr); + shell::PersistentCache::SetCacheDirectoryPath(persistent_cache_path); + } + + if (SAFE_ACCESS(args, is_persistent_cache_read_only, false)) { + shell::PersistentCache::gIsReadOnly = true; + } + fml::CommandLine command_line; if (SAFE_ACCESS(args, command_line_argc, 0) != 0 && SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) { @@ -288,20 +364,23 @@ FlutterResult FlutterEngineRun(size_t version, } blink::Settings settings = shell::SettingsFromCommandLine(command_line); + + PopulateSnapshotMappingCallbacks(args, settings); + settings.icu_data_path = icu_data_path; settings.assets_path = args->assets_path; - // Check whether the assets path contains Dart 2 kernel assets. - const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin"; - std::string application_kernel_path = fml::paths::JoinPaths( - {settings.assets_path, kApplicationKernelSnapshotFileName}); - if (fml::IsFile(application_kernel_path)) { - // Run from a kernel snapshot. + if (!blink::DartVM::IsRunningPrecompiledCode()) { + // Verify the assets path contains Dart 2 kernel assets. + const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin"; + std::string application_kernel_path = fml::paths::JoinPaths( + {settings.assets_path, kApplicationKernelSnapshotFileName}); + if (!fml::IsFile(application_kernel_path)) { + FML_LOG(ERROR) << "Not running in AOT mode but could not resolve the " + "kernel binary."; + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } settings.application_kernel_asset = kApplicationKernelSnapshotFileName; - } else { - // Run from a main Dart file. - settings.main_dart_file_path = args->main_path; - settings.packages_file_path = args->packages_path; } settings.task_observer_add = [](intptr_t key, fml::closure callback) { @@ -310,20 +389,97 @@ FlutterResult FlutterEngineRun(size_t version, settings.task_observer_remove = [](intptr_t key) { fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); }; + if (SAFE_ACCESS(args, root_isolate_create_callback, nullptr) != nullptr) { + VoidCallback callback = + SAFE_ACCESS(args, root_isolate_create_callback, nullptr); + settings.root_isolate_create_callback = [callback, user_data]() { + callback(user_data); + }; + } - // Create a thread host with the current thread as the platform thread and all - // other threads managed. - shell::ThreadHost thread_host("io.flutter", shell::ThreadHost::Type::GPU | - shell::ThreadHost::Type::IO | - shell::ThreadHost::Type::UI); - fml::MessageLoop::EnsureInitializedForCurrentThread(); - blink::TaskRunners task_runners( - "io.flutter", - fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - thread_host.gpu_thread->GetTaskRunner(), // gpu - thread_host.ui_thread->GetTaskRunner(), // ui - thread_host.io_thread->GetTaskRunner() // io - ); + shell::PlatformViewEmbedder::UpdateSemanticsNodesCallback + update_semantics_nodes_callback = nullptr; + if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) { + update_semantics_nodes_callback = + [ptr = args->update_semantics_node_callback, + user_data](blink::SemanticsNodeUpdates update) { + for (const auto& value : update) { + const auto& node = value.second; + SkMatrix transform = static_cast(node.transform); + FlutterTransformation flutter_transform{ + transform.get(SkMatrix::kMScaleX), + transform.get(SkMatrix::kMSkewX), + transform.get(SkMatrix::kMTransX), + transform.get(SkMatrix::kMSkewY), + transform.get(SkMatrix::kMScaleY), + transform.get(SkMatrix::kMTransY), + transform.get(SkMatrix::kMPersp0), + transform.get(SkMatrix::kMPersp1), + transform.get(SkMatrix::kMPersp2)}; + const FlutterSemanticsNode embedder_node{ + sizeof(FlutterSemanticsNode), + node.id, + static_cast(node.flags), + static_cast(node.actions), + node.textSelectionBase, + node.textSelectionExtent, + node.scrollChildren, + node.scrollIndex, + node.scrollPosition, + node.scrollExtentMax, + node.scrollExtentMin, + node.elevation, + node.thickness, + node.label.c_str(), + node.hint.c_str(), + node.value.c_str(), + node.increasedValue.c_str(), + node.decreasedValue.c_str(), + static_cast(node.textDirection), + FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight, + node.rect.fBottom}, + flutter_transform, + node.childrenInTraversalOrder.size(), + &node.childrenInTraversalOrder[0], + &node.childrenInHitTestOrder[0], + node.customAccessibilityActions.size(), + &node.customAccessibilityActions[0], + }; + ptr(&embedder_node, user_data); + } + const FlutterSemanticsNode batch_end_sentinel = { + sizeof(FlutterSemanticsNode), + kFlutterSemanticsNodeIdBatchEnd, + }; + ptr(&batch_end_sentinel, user_data); + }; + } + + shell::PlatformViewEmbedder::UpdateSemanticsCustomActionsCallback + update_semantics_custom_actions_callback = nullptr; + if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) != + nullptr) { + update_semantics_custom_actions_callback = + [ptr = args->update_semantics_custom_action_callback, + user_data](blink::CustomAccessibilityActionUpdates actions) { + for (const auto& value : actions) { + const auto& action = value.second; + const FlutterSemanticsCustomAction embedder_action = { + sizeof(FlutterSemanticsCustomAction), + action.id, + static_cast(action.overrideId), + action.label.c_str(), + action.hint.c_str(), + }; + ptr(&embedder_action, user_data); + } + const FlutterSemanticsCustomAction batch_end_sentinel = { + sizeof(FlutterSemanticsCustomAction), + kFlutterSemanticsCustomActionIdBatchEnd, + }; + ptr(&batch_end_sentinel, user_data); + }; + } shell::PlatformViewEmbedder::PlatformMessageResponseCallback platform_message_response_callback = nullptr; @@ -344,15 +500,25 @@ FlutterResult FlutterEngineRun(size_t version, }; } + shell::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr; + if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) { + vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) { + return ptr(user_data, baton); + }; + } + shell::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table = { - platform_message_response_callback, // platform_message_response_callback + update_semantics_nodes_callback, // + update_semantics_custom_actions_callback, // + platform_message_response_callback, // + vsync_callback, // }; auto on_create_platform_view = InferPlatformViewCreationCallback( config, user_data, platform_dispatch_table); if (!on_create_platform_view) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } shell::Shell::CreateCallback on_create_rasterizer = @@ -360,27 +526,103 @@ FlutterResult FlutterEngineRun(size_t version, return std::make_unique(shell.GetTaskRunners()); }; + // TODO(chinmaygarde): This is the wrong spot for this. It belongs in the + // platform view jump table. + shell::EmbedderExternalTextureGL::ExternalTextureCallback + external_texture_callback; + if (config->type == kOpenGL) { + const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; + if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback, + nullptr) != nullptr) { + external_texture_callback = + [ptr = open_gl_config->gl_external_texture_frame_callback, user_data]( + int64_t texture_identifier, GrContext* context, + const SkISize& size) -> sk_sp { + FlutterOpenGLTexture texture = {}; + + if (!ptr(user_data, texture_identifier, size.width(), size.height(), + &texture)) { + return nullptr; + } + + GrGLTextureInfo gr_texture_info = {texture.target, texture.name, + texture.format}; + + GrBackendTexture gr_backend_texture(size.width(), size.height(), + GrMipMapped::kNo, gr_texture_info); + SkImage::TextureReleaseProc release_proc = texture.destruction_callback; + auto image = SkImage::MakeFromTexture( + context, // context + gr_backend_texture, // texture handle + kTopLeft_GrSurfaceOrigin, // origin + kRGBA_8888_SkColorType, // color type + kPremul_SkAlphaType, // alpha type + nullptr, // colorspace + release_proc, // texture release proc + texture.user_data // texture release context + ); + + if (!image) { + // In case Skia rejects the image, call the release proc so that + // embedders can perform collection of intermediates. + if (release_proc) { + release_proc(texture.user_data); + } + FML_LOG(ERROR) << "Could not create external texture."; + return nullptr; + } + + return image; + }; + } + } + + auto thread_host = + shell::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( + SAFE_ACCESS(args, custom_task_runners, nullptr)); + + if (!thread_host || !thread_host->IsValid()) { + FML_LOG(ERROR) << "Could not setup or infer thread configuration to run " + "the Flutter engine on."; + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + auto task_runners = thread_host->GetTaskRunners(); + + if (!task_runners.IsValid()) { + FML_LOG(ERROR) << "Task runner configuration specified is invalid."; + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + // Step 1: Create the engine. auto embedder_engine = - std::make_unique(std::move(thread_host), // - std::move(task_runners), // - settings, // - on_create_platform_view, // - on_create_rasterizer // + std::make_unique(std::move(thread_host), // + std::move(task_runners), // + settings, // + on_create_platform_view, // + on_create_rasterizer, // + external_texture_callback // ); if (!embedder_engine->IsValid()) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } // Step 2: Setup the rendering surface. if (!embedder_engine->NotifyCreated()) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } // Step 3: Run the engine. auto run_configuration = shell::RunConfiguration::InferFromSettings(settings); + if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) { + auto dart_entrypoint = std::string{args->custom_dart_entrypoint}; + if (dart_entrypoint.size() != 0) { + run_configuration.SetEntrypoint(std::move(dart_entrypoint)); + } + } + run_configuration.AddAssetResolver( std::make_unique( fml::Duplicate(settings.assets_dir))); @@ -388,9 +630,12 @@ FlutterResult FlutterEngineRun(size_t version, run_configuration.AddAssetResolver( std::make_unique(fml::OpenDirectory( settings.assets_path.c_str(), false, fml::FilePermission::kRead))); + if (!run_configuration.IsValid()) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } if (!embedder_engine->Run(std::move(run_configuration))) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } // Finally! Release the ownership of the embedder engine to the caller. @@ -398,9 +643,9 @@ FlutterResult FlutterEngineRun(size_t version, return kSuccess; } -FlutterResult FlutterEngineShutdown(FlutterEngine engine) { +FlutterEngineResult FlutterEngineShutdown(FlutterEngine engine) { if (engine == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto embedder_engine = reinterpret_cast(engine); embedder_engine->NotifyDestroyed(); @@ -408,11 +653,11 @@ FlutterResult FlutterEngineShutdown(FlutterEngine engine) { return kSuccess; } -FlutterResult FlutterEngineSendWindowMetricsEvent( +FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FlutterEngine engine, const FlutterWindowMetricsEvent* flutter_metrics) { if (engine == nullptr || flutter_metrics == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } blink::ViewportMetrics metrics; @@ -424,9 +669,10 @@ FlutterResult FlutterEngineSendWindowMetricsEvent( return reinterpret_cast(engine)->SetViewportMetrics( std::move(metrics)) ? kSuccess - : kInvalidArguments; + : LOG_EMBEDDER_ERROR(kInvalidArguments); } +// Returns the blink::PointerData::Change for the given FlutterPointerPhase. inline blink::PointerData::Change ToPointerDataChange( FlutterPointerPhase phase) { switch (phase) { @@ -438,15 +684,35 @@ inline blink::PointerData::Change ToPointerDataChange( return blink::PointerData::Change::kDown; case kMove: return blink::PointerData::Change::kMove; + case kAdd: + return blink::PointerData::Change::kAdd; + case kRemove: + return blink::PointerData::Change::kRemove; + case kHover: + return blink::PointerData::Change::kHover; } return blink::PointerData::Change::kCancel; } -FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, - const FlutterPointerEvent* pointers, - size_t events_count) { +// Returns the blink::PointerData::SignalKind for the given +// FlutterPointerSignaKind. +inline blink::PointerData::SignalKind ToPointerDataSignalKind( + FlutterPointerSignalKind kind) { + switch (kind) { + case kFlutterPointerSignalKindNone: + return blink::PointerData::SignalKind::kNone; + case kFlutterPointerSignalKindScroll: + return blink::PointerData::SignalKind::kScroll; + } + return blink::PointerData::SignalKind::kNone; +} + +FlutterEngineResult FlutterEngineSendPointerEvent( + FlutterEngine engine, + const FlutterPointerEvent* pointers, + size_t events_count) { if (engine == nullptr || pointers == nullptr || events_count == 0) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto packet = std::make_unique(events_count); @@ -462,6 +728,11 @@ FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, pointer_data.kind = blink::PointerData::DeviceKind::kMouse; pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0); pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0); + pointer_data.device = SAFE_ACCESS(current, device, 0); + pointer_data.signal_kind = ToPointerDataSignalKind( + SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone)); + pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0); + pointer_data.scroll_delta_y = SAFE_ACCESS(current, scroll_delta_y, 0.0); packet->SetPointerData(i, pointer_data); current = reinterpret_cast( reinterpret_cast(current) + current->struct_size); @@ -470,19 +741,19 @@ FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, return reinterpret_cast(engine) ->DispatchPointerDataPacket(std::move(packet)) ? kSuccess - : kInvalidArguments; + : LOG_EMBEDDER_ERROR(kInvalidArguments); } -FlutterResult FlutterEngineSendPlatformMessage( +FlutterEngineResult FlutterEngineSendPlatformMessage( FlutterEngine engine, const FlutterPlatformMessage* flutter_message) { if (engine == nullptr || flutter_message == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr || SAFE_ACCESS(flutter_message, message, nullptr) == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto message = fml::MakeRefCounted( @@ -495,16 +766,16 @@ FlutterResult FlutterEngineSendPlatformMessage( return reinterpret_cast(engine)->SendPlatformMessage( std::move(message)) ? kSuccess - : kInvalidArguments; + : LOG_EMBEDDER_ERROR(kInvalidArguments); } -FlutterResult FlutterEngineSendPlatformMessageResponse( +FlutterEngineResult FlutterEngineSendPlatformMessageResponse( FlutterEngine engine, const FlutterPlatformMessageResponseHandle* handle, const uint8_t* data, size_t data_length) { if (data_length != 0 && data == nullptr) { - return kInvalidArguments; + return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto response = handle->message->response(); @@ -521,7 +792,158 @@ FlutterResult FlutterEngineSendPlatformMessageResponse( return kSuccess; } -FlutterResult __FlutterEngineFlushPendingTasksNow() { +FlutterEngineResult __FlutterEngineFlushPendingTasksNow() { fml::MessageLoop::GetCurrent().RunExpiredTasksNow(); return kSuccess; } + +FlutterEngineResult FlutterEngineRegisterExternalTexture( + FlutterEngine engine, + int64_t texture_identifier) { + if (engine == nullptr || texture_identifier == 0) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + if (!reinterpret_cast(engine)->RegisterTexture( + texture_identifier)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + return kSuccess; +} + +FlutterEngineResult FlutterEngineUnregisterExternalTexture( + FlutterEngine engine, + int64_t texture_identifier) { + if (engine == nullptr || texture_identifier == 0) { + return kInvalidArguments; + } + + if (!reinterpret_cast(engine)->UnregisterTexture( + texture_identifier)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + + return kSuccess; +} + +FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable( + FlutterEngine engine, + int64_t texture_identifier) { + if (engine == nullptr || texture_identifier == 0) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + if (!reinterpret_cast(engine) + ->MarkTextureFrameAvailable(texture_identifier)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + return kSuccess; +} + +FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(FlutterEngine engine, + bool enabled) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + if (!reinterpret_cast(engine)->SetSemanticsEnabled( + enabled)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + return kSuccess; +} + +FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures( + FlutterEngine engine, + FlutterAccessibilityFeature flags) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + if (!reinterpret_cast(engine) + ->SetAccessibilityFeatures(flags)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + return kSuccess; +} + +FlutterEngineResult FlutterEngineDispatchSemanticsAction( + FlutterEngine engine, + uint64_t id, + FlutterSemanticsAction action, + const uint8_t* data, + size_t data_length) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + auto engine_action = static_cast(action); + if (!reinterpret_cast(engine) + ->DispatchSemanticsAction( + id, engine_action, + std::vector({data, data + data_length}))) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + return kSuccess; +} + +FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine, + intptr_t baton, + uint64_t frame_start_time_nanos, + uint64_t frame_target_time_nanos) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + TRACE_EVENT0("flutter", "FlutterEngineOnVsync"); + + auto start_time = fml::TimePoint::FromEpochDelta( + fml::TimeDelta::FromNanoseconds(frame_start_time_nanos)); + + auto target_time = fml::TimePoint::FromEpochDelta( + fml::TimeDelta::FromNanoseconds(frame_target_time_nanos)); + + if (!reinterpret_cast(engine)->OnVsyncEvent( + baton, start_time, target_time)) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + + return kSuccess; +} + +void FlutterEngineTraceEventDurationBegin(const char* name) { + fml::tracing::TraceEvent0("flutter", name); +} + +void FlutterEngineTraceEventDurationEnd(const char* name) { + fml::tracing::TraceEventEnd(name); +} + +void FlutterEngineTraceEventInstant(const char* name) { + fml::tracing::TraceEventInstant0("flutter", name); +} + +FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine, + VoidCallback callback, + void* baton) { + if (engine == nullptr || callback == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + auto task = [callback, baton]() { callback(baton); }; + + return reinterpret_cast(engine)->PostRenderThreadTask( + task) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInternalInconsistency); +} + +uint64_t FlutterEngineGetCurrentTime() { + return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds(); +} + +FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine, + const FlutterTask* task) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + return reinterpret_cast(engine)->RunTask(task) + ? kSuccess + : LOG_EMBEDDER_ERROR(kInvalidArguments); +} diff --git a/shell/platform/embedder/embedder.gni b/shell/platform/embedder/embedder.gni index 79d06f7c9151e..87a66cfd348b3 100644 --- a/shell/platform/embedder/embedder.gni +++ b/shell/platform/embedder/embedder.gni @@ -1,4 +1,4 @@ -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter 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/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 6a4928f0d0172..ae0348e7e0e19 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -23,13 +23,147 @@ typedef enum { kSuccess = 0, kInvalidLibraryVersion, kInvalidArguments, -} FlutterResult; + kInternalInconsistency, +} FlutterEngineResult; typedef enum { kOpenGL, kSoftware, } FlutterRendererType; +// Additional accessibility features that may be enabled by the platform. +// +// Must match the |AccessibilityFeatures| enum in window.dart. +typedef enum { + // Indicate there is a running accessibility service which is changing the + // interaction model of the device. + kFlutterAccessibilityFeatureAccessibleNavigation = 1 << 0, + // Indicate the platform is inverting the colors of the application. + kFlutterAccessibilityFeatureInvertColors = 1 << 1, + // Request that animations be disabled or simplified. + kFlutterAccessibilityFeatureDisableAnimations = 1 << 2, + // Request that text be rendered at a bold font weight. + kFlutterAccessibilityFeatureBoldText = 1 << 3, + // Request that certain animations be simplified and parallax effects + // removed. + kFlutterAccessibilityFeatureReduceMotion = 1 << 4, +} FlutterAccessibilityFeature; + +// The set of possible actions that can be conveyed to a semantics node. +// +// Must match the |SemanticsAction| enum in semantics.dart. +typedef enum { + // The equivalent of a user briefly tapping the screen with the finger without + // moving it. + kFlutterSemanticsActionTap = 1 << 0, + // The equivalent of a user pressing and holding the screen with the finger + // for a few seconds without moving it. + kFlutterSemanticsActionLongPress = 1 << 1, + // The equivalent of a user moving their finger across the screen from right + // to left. + kFlutterSemanticsActionScrollLeft = 1 << 2, + // The equivalent of a user moving their finger across the screen from left to + // right. + kFlutterSemanticsActionScrollRight = 1 << 3, + // The equivalent of a user moving their finger across the screen from bottom + // to top. + kFlutterSemanticsActionScrollUp = 1 << 4, + // The equivalent of a user moving their finger across the screen from top to + // bottom. + kFlutterSemanticsActionScrollDown = 1 << 5, + // Increase the value represented by the semantics node. + kFlutterSemanticsActionIncrease = 1 << 6, + // Decrease the value represented by the semantics node. + kFlutterSemanticsActionDecrease = 1 << 7, + // A request to fully show the semantics node on screen. + kFlutterSemanticsActionShowOnScreen = 1 << 8, + // Move the cursor forward by one character. + kFlutterSemanticsActionMoveCursorForwardByCharacter = 1 << 9, + // Move the cursor backward by one character. + kFlutterSemanticsActionMoveCursorBackwardByCharacter = 1 << 10, + // Set the text selection to the given range. + kFlutterSemanticsActionSetSelection = 1 << 11, + // Copy the current selection to the clipboard. + kFlutterSemanticsActionCopy = 1 << 12, + // Cut the current selection and place it in the clipboard. + kFlutterSemanticsActionCut = 1 << 13, + // Paste the current content of the clipboard. + kFlutterSemanticsActionPaste = 1 << 14, + // Indicate that the node has gained accessibility focus. + kFlutterSemanticsActionDidGainAccessibilityFocus = 1 << 15, + // Indicate that the node has lost accessibility focus. + kFlutterSemanticsActionDidLoseAccessibilityFocus = 1 << 16, + // Indicate that the user has invoked a custom accessibility action. + kFlutterSemanticsActionCustomAction = 1 << 17, + // A request that the node should be dismissed. + kFlutterSemanticsActionDismiss = 1 << 18, + // Move the cursor forward by one word. + kFlutterSemanticsActionMoveCursorForwardByWord = 1 << 19, + // Move the cursor backward by one word. + kFlutterSemanticsActionMoveCursorBackwardByWord = 1 << 20, +} FlutterSemanticsAction; + +// The set of properties that may be associated with a semantics node. +// +// Must match the |SemanticsFlag| enum in semantics.dart. +typedef enum { + // The semantics node has the quality of either being "checked" or + // "unchecked". + kFlutterSemanticsFlagHasCheckedState = 1 << 0, + // Whether a semantics node is checked. + kFlutterSemanticsFlagIsChecked = 1 << 1, + // Whether a semantics node is selected. + kFlutterSemanticsFlagIsSelected = 1 << 2, + // Whether the semantic node represents a button. + kFlutterSemanticsFlagIsButton = 1 << 3, + // Whether the semantic node represents a text field. + kFlutterSemanticsFlagIsTextField = 1 << 4, + // Whether the semantic node currently holds the user's focus. + kFlutterSemanticsFlagIsFocused = 1 << 5, + // The semantics node has the quality of either being "enabled" or "disabled". + kFlutterSemanticsFlagHasEnabledState = 1 << 6, + // Whether a semantic node that hasEnabledState is currently enabled. + kFlutterSemanticsFlagIsEnabled = 1 << 7, + // Whether a semantic node is in a mutually exclusive group. + kFlutterSemanticsFlagIsInMutuallyExclusiveGroup = 1 << 8, + // Whether a semantic node is a header that divides content into sections. + kFlutterSemanticsFlagIsHeader = 1 << 9, + // Whether the value of the semantics node is obscured. + kFlutterSemanticsFlagIsObscured = 1 << 10, + // Whether the semantics node is the root of a subtree for which a route name + // should be announced. + kFlutterSemanticsFlagScopesRoute = 1 << 11, + // Whether the semantics node label is the name of a visually distinct route. + kFlutterSemanticsFlagNamesRoute = 1 << 12, + // Whether the semantics node is considered hidden. + kFlutterSemanticsFlagIsHidden = 1 << 13, + // Whether the semantics node represents an image. + kFlutterSemanticsFlagIsImage = 1 << 14, + // Whether the semantics node is a live region. + kFlutterSemanticsFlagIsLiveRegion = 1 << 15, + // The semantics node has the quality of either being "on" or "off". + kFlutterSemanticsFlagHasToggledState = 1 << 16, + // If true, the semantics node is "on". If false, the semantics node is "off". + kFlutterSemanticsFlagIsToggled = 1 << 17, + // Whether the platform can scroll the semantics node when the user attempts + // to move the accessibility focus to an offscreen child. + // + // For example, a |ListView| widget has implicit scrolling so that users can + // easily move the accessibility focus to the next set of children. A + // |PageView| widget does not have implicit scrolling, so that users don't + // navigate to the next page when reaching the end of the current one. + kFlutterSemanticsFlagHasImplicitScrolling = 1 << 18, +} FlutterSemanticsFlag; + +typedef enum { + // Text has unknown text direction. + kFlutterTextDirectionUnknown = 0, + // Text is read from right to left. + kFlutterTextDirectionRTL = 1, + // Text is read from left to right. + kFlutterTextDirectionLTR = 2, +} FlutterTextDirection; + typedef struct _FlutterEngine* FlutterEngine; typedef struct { @@ -53,6 +187,22 @@ typedef struct { double pers2; } FlutterTransformation; +typedef void (*VoidCallback)(void* /* user data */); + +typedef struct { + // Target texture of the active texture unit (example GL_TEXTURE_2D). + uint32_t target; + // The name of the texture. + uint32_t name; + // The texture format (example GL_RGBA8). + uint32_t format; + // User data to be returned on the invocation of the destruction callback. + void* user_data; + // Callback invoked (on an engine managed thread) that asks the embedder to + // collect the texture. + VoidCallback destruction_callback; +} FlutterOpenGLTexture; + typedef bool (*BoolCallback)(void* /* user data */); typedef FlutterTransformation (*TransformationCallback)(void* /* user data */); typedef uint32_t (*UIntCallback)(void* /* user data */); @@ -61,6 +211,12 @@ typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */, size_t /* row bytes */, size_t /* height */); typedef void* (*ProcResolver)(void* /* user data */, const char* /* name */); +typedef bool (*TextureFrameCallback)(void* /* user data */, + int64_t /* texture identifier */, + size_t /* width */, + size_t /* height */, + FlutterOpenGLTexture* /* texture out */); +typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */); typedef struct { // The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig). @@ -69,6 +225,12 @@ typedef struct { BoolCallback clear_current; BoolCallback present; UIntCallback fbo_callback; + // This is an optional callback. Flutter will ask the emebdder to create a GL + // context current on a background thread. If the embedder is able to do so, + // Flutter will assume that this context is in the same sharegroup as the main + // rendering context and use this context for asynchronous texture uploads. + // Though optional, it is recommended that all embedders set this callback as + // it will lead to better performance in texture handling. BoolCallback make_resource_current; // By default, the renderer config assumes that the FBO does not change for // the duration of the engine run. If this argument is true, the @@ -79,6 +241,11 @@ typedef struct { // operations. This callback is optional. TransformationCallback surface_transformation; ProcResolver gl_proc_resolver; + // When the embedder specifies that a texture has a frame available, the + // engine will call this method (on an internal engine managed thread) so that + // external texture details can be supplied to the engine for subsequent + // composition. + TextureFrameCallback gl_external_texture_frame_callback; } FlutterOpenGLRendererConfig; typedef struct { @@ -110,13 +277,23 @@ typedef struct { double pixel_ratio; } FlutterWindowMetricsEvent; +// The phase of the pointer event. typedef enum { kCancel, kUp, kDown, kMove, + kAdd, + kRemove, + kHover, } FlutterPointerPhase; +// The type of a pointer signal. +typedef enum { + kFlutterPointerSignalKindNone, + kFlutterPointerSignalKindScroll, +} FlutterPointerSignalKind; + typedef struct { // The size of this struct. Must be sizeof(FlutterPointerEvent). size_t struct_size; @@ -124,6 +301,12 @@ typedef struct { size_t timestamp; // in microseconds. double x; double y; + // An optional device identifier. If this is not specified, it is assumed that + // the embedder has no multitouch capability. + int32_t device; + FlutterPointerSignalKind signal_kind; + double scroll_delta_x; + double scroll_delta_y; } FlutterPointerEvent; struct _FlutterPlatformMessageResponseHandle; @@ -151,6 +334,165 @@ typedef void (*FlutterPlatformMessageCallback)( const FlutterPlatformMessage* /* message*/, void* /* user data */); +typedef struct { + double left; + double top; + double right; + double bottom; +} FlutterRect; + +// |FlutterSemanticsNode| ID used as a sentinel to signal the end of a batch of +// semantics node updates. +const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; + +// A node that represents some semantic data. +// +// The semantics tree is maintained during the semantics phase of the pipeline +// (i.e., during PipelineOwner.flushSemantics), which happens after +// compositing. Updates are then pushed to embedders via the registered +// |FlutterUpdateSemanticsNodeCallback|. +typedef struct { + // The size of this struct. Must be sizeof(FlutterSemanticsNode). + size_t struct_size; + // The unique identifier for this node. + int32_t id; + // The set of semantics flags associated with this node. + FlutterSemanticsFlag flags; + // The set of semantics actions applicable to this node. + FlutterSemanticsAction actions; + // The position at which the text selection originates. + int32_t text_selection_base; + // The position at which the text selection terminates. + int32_t text_selection_extent; + // The total number of scrollable children that contribute to semantics. + int32_t scroll_child_count; + // The index of the first visible semantic child of a scroll node. + int32_t scroll_index; + // The current scrolling position in logical pixels if the node is scrollable. + double scroll_position; + // The maximum in-range value for |scrollPosition| if the node is scrollable. + double scroll_extent_max; + // The minimum in-range value for |scrollPosition| if the node is scrollable. + double scroll_extent_min; + // The elevation along the z-axis at which the rect of this semantics node is + // located above its parent. + double elevation; + // Describes how much space the semantics node takes up along the z-axis. + double thickness; + // A textual description of the node. + const char* label; + // A brief description of the result of performing an action on the node. + const char* hint; + // A textual description of the current value of the node. + const char* value; + // A value that |value| will have after a kFlutterSemanticsActionIncrease| + // action has been performed. + const char* increased_value; + // A value that |value| will have after a kFlutterSemanticsActionDecrease| + // action has been performed. + const char* decreased_value; + // The reading direction for |label|, |value|, |hint|, |increasedValue|, and + // |decreasedValue|. + FlutterTextDirection text_direction; + // The bounding box for this node in its coordinate system. + FlutterRect rect; + // The transform from this node's coordinate system to its parent's coordinate + // system. + FlutterTransformation transform; + // The number of children this node has. + size_t child_count; + // Array of child node IDs in traversal order. Has length |child_count|. + const int32_t* children_in_traversal_order; + // Array of child node IDs in hit test order. Has length |child_count|. + const int32_t* children_in_hit_test_order; + // The number of custom accessibility action associated with this node. + size_t custom_accessibility_actions_count; + // Array of |FlutterSemanticsCustomAction| IDs associated with this node. + // Has length |custom_accessibility_actions_count|. + const int32_t* custom_accessibility_actions; +} FlutterSemanticsNode; + +// |FlutterSemanticsCustomAction| ID used as a sentinel to signal the end of a +// batch of semantics custom action updates. +const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; + +// A custom semantics action, or action override. +// +// Custom actions can be registered by applications in order to provide +// semantic actions other than the standard actions available through the +// |FlutterSemanticsAction| enum. +// +// Action overrides are custom actions that the application developer requests +// to be used in place of the standard actions in the |FlutterSemanticsAction| +// enum. +typedef struct { + // The size of the struct. Must be sizeof(FlutterSemanticsCustomAction). + size_t struct_size; + // The unique custom action or action override ID. + int32_t id; + // For overriden standard actions, corresponds to the + // |FlutterSemanticsAction| to override. + FlutterSemanticsAction override_action; + // The user-readable name of this custom semantics action. + const char* label; + // The hint description of this custom semantics action. + const char* hint; +} FlutterSemanticsCustomAction; + +typedef void (*FlutterUpdateSemanticsNodeCallback)( + const FlutterSemanticsNode* /* semantics node */, + void* /* user data */); + +typedef void (*FlutterUpdateSemanticsCustomActionCallback)( + const FlutterSemanticsCustomAction* /* semantics custom action */, + void* /* user data */); + +typedef struct _FlutterTaskRunner* FlutterTaskRunner; + +typedef struct { + FlutterTaskRunner runner; + uint64_t task; +} FlutterTask; + +typedef void (*FlutterTaskRunnerPostTaskCallback)( + FlutterTask /* task */, + uint64_t /* target time nanos */, + void* /* user data */); + +// An interface used by the Flutter engine to execute tasks at the target time +// on a specified thread. There should be a 1-1 relationship between a thread +// and a task runner. It is undefined behavior to run a task on a thread that is +// not associated with its task runner. +typedef struct { + // The size of this struct. Must be sizeof(FlutterTaskRunnerDescription). + size_t struct_size; + void* user_data; + // May be called from any thread. Should return true if tasks posted on the + // calling thread will be run on that same thread. + // + // This field is required. + BoolCallback runs_task_on_current_thread_callback; + // May be called from any thread. The given task should be executed by the + // embedder on the thread associated with that task runner by calling + // |FlutterEngineRunTask| at the given target time. The system monotonic clock + // should be used for the target time. The target time is the absolute time + // from epoch (NOT a delta) at which the task must be returned back to the + // engine on the correct thread. If the embedder needs to calculate a delta, + // |FlutterEngineGetCurrentTime| may be called and the difference used as the + // delta. + // + // This field is required. + FlutterTaskRunnerPostTaskCallback post_task_callback; +} FlutterTaskRunnerDescription; + +typedef struct { + // The size of this struct. Must be sizeof(FlutterCustomTaskRunners). + size_t struct_size; + // Specify the task runner for the thread on which the |FlutterEngineRun| call + // is made. + const FlutterTaskRunnerDescription* platform_task_runner; +} FlutterCustomTaskRunners; + typedef struct { // The size of this struct. Must be sizeof(FlutterProjectArgs). size_t struct_size; @@ -158,14 +500,24 @@ typedef struct { // string can be collected after the call to |FlutterEngineRun| returns. The // string must be NULL terminated. const char* assets_path; - // The path to the Dart file containing the |main| entry point. The string can - // be collected after the call to |FlutterEngineRun| returns. The string must - // be NULL terminated. - const char* main_path; + // The path to the Dart file containing the |main| entry point. + // The string can be collected after the call to |FlutterEngineRun| returns. + // The string must be NULL terminated. + // + // \deprecated As of Dart 2, running from Dart source is no longer supported. + // Dart code should now be compiled to kernel form and will be loaded by from + // |kernel_blob.bin| in the assets directory. This struct member is retained + // for ABI stability. + const char* main_path__unused__; // The path to the |.packages| for the project. The string can be collected // after the call to |FlutterEngineRun| returns. The string must be NULL // terminated. - const char* packages_path; + // + // \deprecated As of Dart 2, running from Dart source is no longer supported. + // Dart code should now be compiled to kernel form and will be loaded by from + // |kernel_blob.bin| in the assets directory. This struct member is retained + // for ABI stability. + const char* packages_path__unused__; // The path to the icudtl.dat file for the project. The string can be // collected after the call to |FlutterEngineRun| returns. The string must // be NULL terminated. @@ -175,40 +527,132 @@ typedef struct { // The command line arguments used to initialize the project. The strings can // be collected after the call to |FlutterEngineRun| returns. The strings must // be NULL terminated. + // Note: The first item in the command line (if specificed at all) is + // interpreted as the executable name. So if an engine flag needs to be passed + // into the same, it needs to not be the very first item in the list. The set + // of engine flags are only meant to control unstable features in the engine. + // Deployed applications should not pass any command line arguments at all as + // they may affect engine stability at runtime in the presence of unsanitized + // input. The list of currently recognized engine flags and their descriptions + // can be retrieved from the |switches.h| engine source file. const char* const* command_line_argv; // The callback invoked by the engine in order to give the embedder the chance // to respond to platform messages from the Dart application. The callback // will be invoked on the thread on which the |FlutterEngineRun| call is made. FlutterPlatformMessageCallback platform_message_callback; + // The VM snapshot data buffer used in AOT operation. This buffer must be + // mapped in as read-only. For more information refer to the documentation on + // the Wiki at + // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode + const uint8_t* vm_snapshot_data; + // The size of the VM snapshot data buffer. + size_t vm_snapshot_data_size; + // The VM snapshot instructions buffer used in AOT operation. This buffer must + // be mapped in as read-execute. For more information refer to the + // documentation on the Wiki at + // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode + const uint8_t* vm_snapshot_instructions; + // The size of the VM snapshot instructions buffer. + size_t vm_snapshot_instructions_size; + // The isolate snapshot data buffer used in AOT operation. This buffer must be + // mapped in as read-only. For more information refer to the documentation on + // the Wiki at + // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode + const uint8_t* isolate_snapshot_data; + // The size of the isolate snapshot data buffer. + size_t isolate_snapshot_data_size; + // The isolate snapshot instructions buffer used in AOT operation. This buffer + // must be mapped in as read-execute. For more information refer to the + // documentation on the Wiki at + // https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode + const uint8_t* isolate_snapshot_instructions; + // The size of the isolate snapshot instructions buffer. + size_t isolate_snapshot_instructions_size; + // The callback invoked by the engine in root isolate scope. Called + // immediately after the root isolate has been created and marked runnable. + VoidCallback root_isolate_create_callback; + // The callback invoked by the engine in order to give the embedder the + // chance to respond to semantics node updates from the Dart application. + // Semantics node updates are sent in batches terminated by a 'batch end' + // callback that is passed a sentinel |FlutterSemanticsNode| whose |id| field + // has the value |kFlutterSemanticsNodeIdBatchEnd|. + // + // The callback will be invoked on the thread on which the |FlutterEngineRun| + // call is made. + FlutterUpdateSemanticsNodeCallback update_semantics_node_callback; + // The callback invoked by the engine in order to give the embedder the + // chance to respond to updates to semantics custom actions from the Dart + // application. Custom action updates are sent in batches terminated by a + // 'batch end' callback that is passed a sentinel + // |FlutterSemanticsCustomAction| whose |id| field has the value + // |kFlutterSemanticsCustomActionIdBatchEnd|. + // + // The callback will be invoked on the thread on which the |FlutterEngineRun| + // call is made. + FlutterUpdateSemanticsCustomActionCallback + update_semantics_custom_action_callback; + // Path to a directory used to store data that is cached across runs of a + // Flutter application (such as compiled shader programs used by Skia). + // This is optional. The string must be NULL terminated. + const char* persistent_cache_path; + + // If true, we'll only read the existing cache, but not write new ones. + bool is_persistent_cache_read_only; + + // A callback that gets invoked by the engine when it attempts to wait for a + // platform vsync event. The engine will give the platform a baton that needs + // to be returned back to the engine via |FlutterEngineOnVsync|. All batons + // must be retured to the engine before initializing a + // |FlutterEngineShutdown|. Not doing the same will result in a memory leak. + // While the call to |FlutterEngineOnVsync| must occur on the thread that made + // the call to |FlutterEngineRun|, the engine will make this callback on an + // internal engine-managed thread. If the components accessed on the embedder + // are not thread safe, the appropriate re-threading must be done. + VsyncCallback vsync_callback; + + // The name of a custom Dart entrypoint. This is optional and specifying a + // null or empty entrypoint makes the engine look for a method named "main" in + // the root library of the application. + // + // Care must be taken to ensure that the custom entrypoint is not tree-shaken + // away. Usually, this is done using the `@pragma('vm:entry-point')` + // decoration. + const char* custom_dart_entrypoint; + + // Typically the Flutter engine create and manages its internal threads. This + // optional argument allows for the specification of task runner interfaces to + // event loops managed by the embedder on threads it creates. + const FlutterCustomTaskRunners* custom_task_runners; } FlutterProjectArgs; FLUTTER_EXPORT -FlutterResult FlutterEngineRun(size_t version, - const FlutterRendererConfig* config, - const FlutterProjectArgs* args, - void* user_data, - FlutterEngine* engine_out); +FlutterEngineResult FlutterEngineRun(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FlutterEngine* engine_out); FLUTTER_EXPORT -FlutterResult FlutterEngineShutdown(FlutterEngine engine); +FlutterEngineResult FlutterEngineShutdown(FlutterEngine engine); FLUTTER_EXPORT -FlutterResult FlutterEngineSendWindowMetricsEvent( +FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FlutterEngine engine, const FlutterWindowMetricsEvent* event); FLUTTER_EXPORT -FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, - const FlutterPointerEvent* events, - size_t events_count); +FlutterEngineResult FlutterEngineSendPointerEvent( + FlutterEngine engine, + const FlutterPointerEvent* events, + size_t events_count); FLUTTER_EXPORT -FlutterResult FlutterEngineSendPlatformMessage( +FlutterEngineResult FlutterEngineSendPlatformMessage( FlutterEngine engine, const FlutterPlatformMessage* message); FLUTTER_EXPORT -FlutterResult FlutterEngineSendPlatformMessageResponse( +FlutterEngineResult FlutterEngineSendPlatformMessageResponse( FlutterEngine engine, const FlutterPlatformMessageResponseHandle* handle, const uint8_t* data, @@ -218,7 +662,122 @@ FlutterResult FlutterEngineSendPlatformMessageResponse( // message loop not controlled by the Flutter engine. This API will be // deprecated soon. FLUTTER_EXPORT -FlutterResult __FlutterEngineFlushPendingTasksNow(); +FlutterEngineResult __FlutterEngineFlushPendingTasksNow(); + +// Register an external texture with a unique (per engine) identifier. Only +// rendering backends that support external textures accept external texture +// registrations. After the external texture is registered, the application can +// mark that a frame is available by calling +// |FlutterEngineMarkExternalTextureFrameAvailable|. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineRegisterExternalTexture( + FlutterEngine engine, + int64_t texture_identifier); + +// Unregister a previous texture registration. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineUnregisterExternalTexture( + FlutterEngine engine, + int64_t texture_identifier); + +// Mark that a new texture frame is available for a given texture identifier. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable( + FlutterEngine engine, + int64_t texture_identifier); + +// Enable or disable accessibility semantics. +// +// When enabled, changes to the semantic contents of the window are sent via +// the |FlutterUpdateSemanticsNodeCallback| registered to +// |update_semantics_node_callback| in |FlutterProjectArgs|; +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(FlutterEngine engine, + bool enabled); + +// Sets additional accessibility features. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures( + FlutterEngine engine, + FlutterAccessibilityFeature features); + +// Dispatch a semantics action to the specified semantics node. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineDispatchSemanticsAction( + FlutterEngine engine, + uint64_t id, + FlutterSemanticsAction action, + const uint8_t* data, + size_t data_length); + +// Notify the engine that a vsync event occurred. A baton passed to the +// platform via the vsync callback must be returned. This call must be made on +// the thread on which the call to |FlutterEngineRun| was made. +// +// |frame_start_time_nanos| is the point at which the vsync event occurred or +// will occur. If the time point is in the future, the engine will wait till +// that point to begin its frame workload. The system monotonic clock is used as +// the timebase. +// +// |frame_target_time_nanos| is the point at which the embedder anticipates the +// next vsync to occur. This is a hint the engine uses to schedule Dart VM +// garbage collection in periods in which the various threads are most likely to +// be idle. For example, for a 60Hz display, embedders should add 16.6 * 1e6 to +// the frame time field. The system monotonic clock is used as the timebase. +// +// That frame timepoints are in nanoseconds. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine, + intptr_t baton, + uint64_t frame_start_time_nanos, + uint64_t frame_target_time_nanos); + +// A profiling utility. Logs a trace duration begin event to the timeline. If +// the timeline is unavailable or disabled, this has no effect. Must be +// balanced with an duration end event (via +// |FlutterEngineTraceEventDurationEnd|) with the same name on the same thread. +// Can be called on any thread. Strings passed into the function will NOT be +// copied when added to the timeline. Only string literals may be passed in. +FLUTTER_EXPORT +void FlutterEngineTraceEventDurationBegin(const char* name); + +// A profiling utility. Logs a trace duration end event to the timeline. If the +// timeline is unavailable or disabled, this has no effect. This call must be +// preceded by a trace duration begin call (via +// |FlutterEngineTraceEventDurationBegin|) with the same name on the same +// thread. Can be called on any thread. Strings passed into the function will +// NOT be copied when added to the timeline. Only string literals may be passed +// in. +FLUTTER_EXPORT +void FlutterEngineTraceEventDurationEnd(const char* name); + +// A profiling utility. Logs a trace duration instant event to the timeline. If +// the timeline is unavailable or disabled, this has no effect. Can be called +// on any thread. Strings passed into the function will NOT be copied when added +// to the timeline. Only string literals may be passed in. +FLUTTER_EXPORT +void FlutterEngineTraceEventInstant(const char* name); + +// Posts a task onto the Flutter render thread. Typically, this may be called +// from any thread as long as a |FlutterEngineShutdown| on the specific engine +// has not already been initiated. +FLUTTER_EXPORT +FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine, + VoidCallback callback, + void* callback_data); + +// Get the current time in nanoseconds from the clock used by the flutter +// engine. This is the system monotonic clock. +FLUTTER_EXPORT +uint64_t FlutterEngineGetCurrentTime(); + +// Inform the engine to run the specified task. This task has been given to +// the engine via the |FlutterTaskRunnerDescription.post_task_callback|. This +// call must only be made at the target time specified in that callback. Running +// the task before that time is undefined behavior. +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine, + const FlutterTask* task); #if defined(__cplusplus) } // extern "C" diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 1f4dc3402b72c..836dfc645c1a7 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -1,29 +1,33 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/platform/embedder/embedder_engine.h" #include "flutter/fml/make_copyable.h" - -#ifdef ERROR -#undef ERROR -#endif +#include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" namespace shell { EmbedderEngine::EmbedderEngine( - ThreadHost thread_host, + std::unique_ptr thread_host, blink::TaskRunners task_runners, blink::Settings settings, Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer) + Shell::CreateCallback on_create_rasterizer, + EmbedderExternalTextureGL::ExternalTextureCallback + external_texture_callback) : thread_host_(std::move(thread_host)), shell_(Shell::Create(std::move(task_runners), std::move(settings), on_create_platform_view, - on_create_rasterizer)) { - is_valid_ = shell_ != nullptr; + on_create_rasterizer)), + external_texture_callback_(external_texture_callback) { + if (!shell_) { + return; + } + + is_valid_ = true; } EmbedderEngine::~EmbedderEngine() = default; @@ -51,7 +55,7 @@ bool EmbedderEngine::NotifyDestroyed() { } bool EmbedderEngine::Run(RunConfiguration run_configuration) { - if (!IsValid()) { + if (!IsValid() || !run_configuration.IsValid()) { return false; } @@ -90,12 +94,17 @@ bool EmbedderEngine::DispatchPointerDataPacket( return false; } + TRACE_EVENT0("flutter", "EmbedderEngine::DispatchPointerDataPacket"); + TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_); + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable( - [engine = shell_->GetEngine(), packet = std::move(packet)] { + [engine = shell_->GetEngine(), packet = std::move(packet), + flow_id = next_pointer_flow_id_] { if (engine) { - engine->DispatchPointerDataPacket(*packet); + engine->DispatchPointerDataPacket(*packet, flow_id); } })); + next_pointer_flow_id_++; return true; } @@ -116,4 +125,103 @@ bool EmbedderEngine::SendPlatformMessage( return true; } +bool EmbedderEngine::RegisterTexture(int64_t texture) { + if (!IsValid() || !external_texture_callback_) { + return false; + } + shell_->GetPlatformView()->RegisterTexture( + std::make_unique(texture, + external_texture_callback_)); + return true; +} + +bool EmbedderEngine::UnregisterTexture(int64_t texture) { + if (!IsValid() || !external_texture_callback_) { + return false; + } + shell_->GetPlatformView()->UnregisterTexture(texture); + return true; +} + +bool EmbedderEngine::MarkTextureFrameAvailable(int64_t texture) { + if (!IsValid() || !external_texture_callback_) { + return false; + } + shell_->GetPlatformView()->MarkTextureFrameAvailable(texture); + return true; +} + +bool EmbedderEngine::SetSemanticsEnabled(bool enabled) { + if (!IsValid()) { + return false; + } + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), enabled] { + if (engine) { + engine->SetSemanticsEnabled(enabled); + } + }); + return true; +} + +bool EmbedderEngine::SetAccessibilityFeatures(int32_t flags) { + if (!IsValid()) { + return false; + } + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), flags] { + if (engine) { + engine->SetAccessibilityFeatures(flags); + } + }); + return true; +} + +bool EmbedderEngine::DispatchSemanticsAction(int id, + blink::SemanticsAction action, + std::vector args) { + if (!IsValid()) { + return false; + } + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + fml::MakeCopyable([engine = shell_->GetEngine(), // engine + id, // id + action, // action + args = std::move(args) // args + ]() mutable { + if (engine) { + engine->DispatchSemanticsAction(id, action, std::move(args)); + } + })); + return true; +} + +bool EmbedderEngine::OnVsyncEvent(intptr_t baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time) { + if (!IsValid()) { + return false; + } + + return VsyncWaiterEmbedder::OnEmbedderVsync(baton, frame_start_time, + frame_target_time); +} + +bool EmbedderEngine::PostRenderThreadTask(fml::closure task) { + if (!IsValid()) { + return false; + } + + shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(task); + return true; +} + +bool EmbedderEngine::RunTask(const FlutterTask* task) { + if (!IsValid() || task == nullptr) { + return false; + } + return thread_host_->PostTask(reinterpret_cast(task->runner), + task->task); +} + } // namespace shell diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index cd21e701d5c01..bd2ff92d16d8b 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,11 +6,15 @@ #define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ #include +#include #include "flutter/fml/macros.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_engine.h" +#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" +#include "flutter/shell/platform/embedder/embedder_thread_host.h" namespace shell { @@ -18,11 +22,13 @@ namespace shell { // instance of the Flutter engine. class EmbedderEngine { public: - EmbedderEngine(ThreadHost thread_host, + EmbedderEngine(std::unique_ptr thread_host, blink::TaskRunners task_runners, blink::Settings settings, Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer); + Shell::CreateCallback on_create_rasterizer, + EmbedderExternalTextureGL::ExternalTextureCallback + external_texture_callback); ~EmbedderEngine(); @@ -41,10 +47,35 @@ class EmbedderEngine { bool SendPlatformMessage(fml::RefPtr message); + bool RegisterTexture(int64_t texture); + + bool UnregisterTexture(int64_t texture); + + bool MarkTextureFrameAvailable(int64_t texture); + + bool SetSemanticsEnabled(bool enabled); + + bool SetAccessibilityFeatures(int32_t flags); + + bool DispatchSemanticsAction(int id, + blink::SemanticsAction action, + std::vector args); + + bool OnVsyncEvent(intptr_t baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time); + + bool PostRenderThreadTask(fml::closure task); + + bool RunTask(const FlutterTask* task); + private: - const ThreadHost thread_host_; + const std::unique_ptr thread_host_; std::unique_ptr shell_; + const EmbedderExternalTextureGL::ExternalTextureCallback + external_texture_callback_; bool is_valid_ = false; + uint64_t next_pointer_flow_id_; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); }; diff --git a/shell/platform/embedder/embedder_external_texture_gl.cc b/shell/platform/embedder/embedder_external_texture_gl.cc new file mode 100644 index 0000000000000..d4c863c3766ca --- /dev/null +++ b/shell/platform/embedder/embedder_external_texture_gl.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" + +#include "flutter/fml/logging.h" + +namespace shell { + +EmbedderExternalTextureGL::EmbedderExternalTextureGL( + int64_t texture_identifier, + ExternalTextureCallback callback) + : Texture(texture_identifier), external_texture_callback_(callback) { + FML_DCHECK(external_texture_callback_); +} + +EmbedderExternalTextureGL::~EmbedderExternalTextureGL() = default; + +// |flow::Texture| +void EmbedderExternalTextureGL::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze) { + if (auto image = external_texture_callback_( + Id(), // + canvas.getGrContext(), // + SkISize::Make(bounds.width(), bounds.height()) // + )) { + last_image_ = image; + } + + if (last_image_) { + canvas.drawImage(last_image_, bounds.x(), bounds.y()); + } +} + +// |flow::Texture| +void EmbedderExternalTextureGL::OnGrContextCreated() {} + +// |flow::Texture| +void EmbedderExternalTextureGL::OnGrContextDestroyed() {} + +// |flow::Texture| +void EmbedderExternalTextureGL::MarkNewFrameAvailable() {} + +} // namespace shell diff --git a/shell/platform/embedder/embedder_external_texture_gl.h b/shell/platform/embedder/embedder_external_texture_gl.h new file mode 100644 index 0000000000000..73017daf195ff --- /dev/null +++ b/shell/platform/embedder/embedder_external_texture_gl.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_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_GL_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_GL_H_ + +#include "flutter/flow/texture.h" +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace shell { + +class EmbedderExternalTextureGL : public flow::Texture { + public: + using ExternalTextureCallback = std::function< + sk_sp(int64_t texture_identifier, GrContext*, const SkISize&)>; + + EmbedderExternalTextureGL(int64_t texture_identifier, + ExternalTextureCallback callback); + + ~EmbedderExternalTextureGL(); + + private: + ExternalTextureCallback external_texture_callback_; + sk_sp last_image_; + + // |flow::Texture| + void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze) override; + + // |flow::Texture| + void OnGrContextCreated() override; + + // |flow::Texture| + void OnGrContextDestroyed() override; + + // |flow::Texture| + void MarkNewFrameAvailable() override; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureGL); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_GL_H_ diff --git a/shell/platform/embedder/embedder_include.c b/shell/platform/embedder/embedder_include.c index 4c45bcb1addff..10d21dc1b44e7 100644 --- a/shell/platform/embedder/embedder_include.c +++ b/shell/platform/embedder/embedder_include.c @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/embedder/embedder_safe_access.h b/shell/platform/embedder/embedder_safe_access.h new file mode 100644 index 0000000000000..783eb44a88bd2 --- /dev/null +++ b/shell/platform/embedder/embedder_safe_access.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter 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_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_ + +#include + +#define SAFE_ACCESS(pointer, member, default_value) \ + ([=]() { \ + if (offsetof(std::remove_pointer::type, member) + \ + sizeof(pointer->member) <= \ + pointer->struct_size) { \ + return pointer->member; \ + } \ + return static_castmember)>((default_value)); \ + })() + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_ diff --git a/shell/platform/embedder/embedder_surface.cc b/shell/platform/embedder/embedder_surface.cc index d14b114e7410c..b61802cbce9ac 100644 --- a/shell/platform/embedder/embedder_surface.cc +++ b/shell/platform/embedder/embedder_surface.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/embedder/embedder_surface.h b/shell/platform/embedder/embedder_surface.h index 5db1394875b29..74af9462b1af4 100644 --- a/shell/platform/embedder/embedder_surface.h +++ b/shell/platform/embedder/embedder_surface.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 1ee37c0615564..487111d698423 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,10 +6,6 @@ #include "flutter/shell/common/io_manager.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { EmbedderSurfaceGL::EmbedderSurfaceGL(GLDispatchTable gl_dispatch_table, @@ -84,9 +80,22 @@ std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { sk_sp EmbedderSurfaceGL::CreateResourceContext() const { auto callback = gl_dispatch_table_.gl_make_resource_current_callback; if (callback && callback()) { - return IOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend); + if (auto context = IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GetGLInterface())) { + return context; + } else { + FML_LOG(ERROR) + << "Internal error: Resource context available but could not create " + "a compatible Skia context."; + return nullptr; + } } + + // The callback was not available or failed. + FML_LOG(ERROR) + << "Could not create a resource context for async texture uploads. " + "Expect degraded performance. Set a valid make_resource_current " + "callback on FlutterOpenGLRendererConfig."; return nullptr; } diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 8fd40885ee3b6..949997b687ac4 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/embedder/embedder_surface_software.cc b/shell/platform/embedder/embedder_surface_software.cc index 103257745b359..7b7192dc99a56 100644 --- a/shell/platform/embedder/embedder_surface_software.cc +++ b/shell/platform/embedder/embedder_surface_software.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,10 +7,6 @@ #include "flutter/fml/trace_event.h" #include "third_party/skia/include/gpu/GrContext.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { EmbedderSurfaceSoftware::EmbedderSurfaceSoftware( @@ -65,8 +61,8 @@ sk_sp EmbedderSurfaceSoftware::AcquireBackingStore( return sk_surface_; } - SkImageInfo info = - SkImageInfo::MakeN32(size.fWidth, size.fHeight, kPremul_SkAlphaType); + SkImageInfo info = SkImageInfo::MakeN32( + size.fWidth, size.fHeight, kPremul_SkAlphaType, SkColorSpace::MakeSRGB()); sk_surface_ = SkSurface::MakeRaster(info, nullptr); if (sk_surface_ == nullptr) { diff --git a/shell/platform/embedder/embedder_surface_software.h b/shell/platform/embedder/embedder_surface_software.h index 852a77b62a2e4..6bc5d98f89247 100644 --- a/shell/platform/embedder/embedder_surface_software.h +++ b/shell/platform/embedder/embedder_surface_software.h @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/platform/embedder/embedder_task_runner.cc b/shell/platform/embedder/embedder_task_runner.cc new file mode 100644 index 0000000000000..c0b5ab862bdf4 --- /dev/null +++ b/shell/platform/embedder/embedder_task_runner.cc @@ -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. + +#include "flutter/shell/platform/embedder/embedder_task_runner.h" + +#include "flutter/fml/message_loop_impl.h" + +namespace shell { + +EmbedderTaskRunner::EmbedderTaskRunner(DispatchTable table) + : TaskRunner(nullptr /* loop implemenation*/), + dispatch_table_(std::move(table)) { + FML_DCHECK(dispatch_table_.post_task_callback); + FML_DCHECK(dispatch_table_.runs_task_on_current_thread_callback); +} + +EmbedderTaskRunner::~EmbedderTaskRunner() = default; + +void EmbedderTaskRunner::PostTask(fml::closure task) { + PostTaskForTime(task, fml::TimePoint::Now()); +} + +void EmbedderTaskRunner::PostTaskForTime(fml::closure task, + fml::TimePoint target_time) { + if (!task) { + return; + } + + uint64_t baton = 0; + + { + // Release the lock before the jump via the dispatch table. + std::lock_guard lock(tasks_mutex_); + baton = ++last_baton_; + pending_tasks_[baton] = task; + } + + dispatch_table_.post_task_callback(this, baton, target_time); +} + +void EmbedderTaskRunner::PostDelayedTask(fml::closure task, + fml::TimeDelta delay) { + PostTaskForTime(task, fml::TimePoint::Now() + delay); +} + +bool EmbedderTaskRunner::RunsTasksOnCurrentThread() { + return dispatch_table_.runs_task_on_current_thread_callback(); +} + +bool EmbedderTaskRunner::PostTask(uint64_t baton) { + fml::closure task; + + { + std::lock_guard lock(tasks_mutex_); + auto found = pending_tasks_.find(baton); + if (found == pending_tasks_.end()) { + FML_LOG(ERROR) << "Embedder attempted to post an unknown task."; + return false; + } + task = found->second; + pending_tasks_.erase(found); + + // Let go of the tasks mutex befor executing the task. + } + + FML_DCHECK(task); + task(); + return true; +} + +} // namespace shell diff --git a/shell/platform/embedder/embedder_task_runner.h b/shell/platform/embedder/embedder_task_runner.h new file mode 100644 index 0000000000000..084efbf148a45 --- /dev/null +++ b/shell/platform/embedder/embedder_task_runner.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/synchronization/thread_annotations.h" +#include "flutter/fml/task_runner.h" + +namespace shell { + +class EmbedderTaskRunner final : public fml::TaskRunner { + public: + struct DispatchTable { + std::function + post_task_callback; + std::function runs_task_on_current_thread_callback; + }; + + EmbedderTaskRunner(DispatchTable table); + + ~EmbedderTaskRunner() override; + + bool PostTask(uint64_t baton); + + // |fml::TaskRunner| + void PostTask(fml::closure task) override; + + // |fml::TaskRunner| + void PostTaskForTime(fml::closure task, fml::TimePoint target_time) override; + + // |fml::TaskRunner| + void PostDelayedTask(fml::closure task, fml::TimeDelta delay) override; + + // |fml::TaskRunner| + bool RunsTasksOnCurrentThread() override; + + private: + DispatchTable dispatch_table_; + std::mutex tasks_mutex_; + uint64_t last_baton_ FML_GUARDED_BY(tasks_mutex_); + std::unordered_map pending_tasks_ + FML_GUARDED_BY(tasks_mutex_); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTaskRunner); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_TASK_RUNNER_H_ diff --git a/shell/platform/embedder/embedder_thread_host.cc b/shell/platform/embedder/embedder_thread_host.cc new file mode 100644 index 0000000000000..925b596d8a18d --- /dev/null +++ b/shell/platform/embedder/embedder_thread_host.cc @@ -0,0 +1,206 @@ +// Copyright 2013 The Flutter 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 why we can't yet export the UI thread to embedders. +#define FML_USED_ON_EMBEDDER + +#include "flutter/shell/platform/embedder/embedder_thread_host.h" + +#include "flutter/fml/message_loop.h" +#include "flutter/shell/platform/embedder/embedder_safe_access.h" + +namespace shell { + +static fml::RefPtr CreateEmbedderTaskRunner( + const FlutterTaskRunnerDescription* description) { + if (description == nullptr) { + return {}; + } + + if (SAFE_ACCESS(description, runs_task_on_current_thread_callback, nullptr) == + nullptr) { + FML_LOG(ERROR) << "FlutterTaskRunnerDescription.runs_task_on_current_" + "thread_callback was nullptr."; + return {}; + } + + if (SAFE_ACCESS(description, post_task_callback, nullptr) == nullptr) { + FML_LOG(ERROR) + << "FlutterTaskRunnerDescription.post_task_callback was nullptr."; + return {}; + } + + auto user_data = SAFE_ACCESS(description, user_data, nullptr); + + // ABI safety checks have been completed. + auto post_task_callback_c = description->post_task_callback; + auto runs_task_on_current_thread_callback_c = + description->runs_task_on_current_thread_callback; + + EmbedderTaskRunner::DispatchTable task_runner_dispatch_table = { + // .post_task_callback + [post_task_callback_c, user_data](EmbedderTaskRunner* task_runner, + uint64_t task_baton, + fml::TimePoint target_time) -> void { + FlutterTask task = { + // runner + reinterpret_cast(task_runner), + // task + task_baton, + }; + post_task_callback_c(task, target_time.ToEpochDelta().ToNanoseconds(), + user_data); + }, + // runs_task_on_current_thread_callback + [runs_task_on_current_thread_callback_c, user_data]() -> bool { + return runs_task_on_current_thread_callback_c(user_data); + }}; + + return fml::MakeRefCounted(task_runner_dispatch_table); +} + +std::unique_ptr +EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( + const FlutterCustomTaskRunners* custom_task_runners) { + { + auto host = CreateEmbedderManagedThreadHost(custom_task_runners); + if (host && host->IsValid()) { + return host; + } + } + + // Only attempt to create the engine managed host if the embedder did not + // specify a custom configuration. We don't want to fallback to the engine + // managed configuration if the embedder attempted to specify a configuration + // but messed up with an incorrect configuration. + if (custom_task_runners == nullptr) { + auto host = CreateEngineManagedThreadHost(); + if (host && host->IsValid()) { + return host; + } + } + + return nullptr; +} + +constexpr const char* kFlutterThreadName = "io.flutter"; + +// static +std::unique_ptr +EmbedderThreadHost::CreateEmbedderManagedThreadHost( + const FlutterCustomTaskRunners* custom_task_runners) { + if (custom_task_runners == nullptr) { + return nullptr; + } + + const auto platform_task_runner = CreateEmbedderTaskRunner( + SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr)); + + // TODO(chinmaygarde): Add more here as we allow more threads to be controlled + // by the embedder. Create fallbacks as necessary. + + if (!platform_task_runner) { + return nullptr; + } + + ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU | + ThreadHost::Type::IO | + ThreadHost::Type::UI); + + blink::TaskRunners task_runners( + kFlutterThreadName, + platform_task_runner, // platform + thread_host.gpu_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + + if (!task_runners.IsValid()) { + return nullptr; + } + + std::set> embedder_task_runners; + embedder_task_runners.insert(platform_task_runner); + + auto embedder_host = std::make_unique( + std::move(thread_host), std::move(task_runners), + std::move(embedder_task_runners)); + + if (embedder_host->IsValid()) { + return embedder_host; + } + + return nullptr; +} + +// static +std::unique_ptr +EmbedderThreadHost::CreateEngineManagedThreadHost() { + // Create a thread host with the current thread as the platform thread and all + // other threads managed. + ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU | + ThreadHost::Type::IO | + ThreadHost::Type::UI); + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + // For embedder platforms that don't have native message loop interop, this + // will reference a task runner that points to a null message loop + // implementation. + auto platform_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + + blink::TaskRunners task_runners( + kFlutterThreadName, + platform_task_runner, // platform + thread_host.gpu_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + + if (!task_runners.IsValid()) { + return nullptr; + } + + std::set> empty_embedder_task_runners; + + auto embedder_host = std::make_unique( + std::move(thread_host), std::move(task_runners), + empty_embedder_task_runners); + + if (embedder_host->IsValid()) { + return embedder_host; + } + + return nullptr; +} + +EmbedderThreadHost::EmbedderThreadHost( + ThreadHost host, + blink::TaskRunners runners, + std::set> embedder_task_runners) + : host_(std::move(host)), runners_(std::move(runners)) { + for (const auto& runner : embedder_task_runners) { + runners_map_[reinterpret_cast(runner.get())] = runner; + } +} + +EmbedderThreadHost::~EmbedderThreadHost() = default; + +bool EmbedderThreadHost::IsValid() const { + return runners_.IsValid(); +} + +const blink::TaskRunners& EmbedderThreadHost::GetTaskRunners() const { + return runners_; +} + +bool EmbedderThreadHost::PostTask(int64_t runner, uint64_t task) const { + auto found = runners_map_.find(runner); + if (found == runners_map_.end()) { + return false; + } + return found->second->PostTask(task); +} + +} // namespace shell diff --git a/shell/platform/embedder/embedder_thread_host.h b/shell/platform/embedder/embedder_thread_host.h new file mode 100644 index 0000000000000..1ae9c309ed72f --- /dev/null +++ b/shell/platform/embedder/embedder_thread_host.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter 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_EMBEDDER_EMBEDDER_THREAD_HOST_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_THREAD_HOST_H_ + +#include +#include +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_task_runner.h" + +namespace shell { + +class EmbedderThreadHost { + public: + static std::unique_ptr + CreateEmbedderOrEngineManagedThreadHost( + const FlutterCustomTaskRunners* custom_task_runners); + + EmbedderThreadHost( + ThreadHost host, + blink::TaskRunners runners, + std::set> embedder_task_runners); + + ~EmbedderThreadHost(); + + bool IsValid() const; + + const blink::TaskRunners& GetTaskRunners() const; + + bool PostTask(int64_t runner, uint64_t task) const; + + private: + ThreadHost host_; + blink::TaskRunners runners_; + std::map> runners_map_; + + static std::unique_ptr CreateEmbedderManagedThreadHost( + const FlutterCustomTaskRunners* custom_task_runners); + + static std::unique_ptr CreateEngineManagedThreadHost(); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderThreadHost); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_THREAD_HOST_H_ diff --git a/shell/platform/embedder/fixtures/a11y_main.dart b/shell/platform/embedder/fixtures/a11y_main.dart new file mode 100644 index 0000000000000..578bd471c0d0a --- /dev/null +++ b/shell/platform/embedder/fixtures/a11y_main.dart @@ -0,0 +1,117 @@ +import 'dart:async'; +import 'dart:typed_data'; +import 'dart:ui'; + +Float64List kTestTransform = () { + final Float64List values = Float64List(16); + values[0] = 1.0; // scaleX + values[4] = 2.0; // skewX + values[12] = 3.0; // transX + values[1] = 4.0; // skewY + values[5] = 5.0; // scaleY + values[13] = 6.0; // transY + values[3] = 7.0; // pers0 + values[7] = 8.0; // pers1 + values[15] = 9.0; // pers2 + return values; +}(); + +void signalNativeTest() native 'SignalNativeTest'; +void notifySemanticsEnabled(bool enabled) native 'NotifyTestData1'; +void notifyAccessibilityFeatures(bool reduceMotion) native 'NotifyTestData1'; +void notifySemanticsAction(int nodeId, int action, List data) native 'NotifyTestData3'; + +/// Returns a future that completes when `window.onSemanticsEnabledChanged` +/// fires. +Future get semanticsChanged { + final Completer semanticsChanged = Completer(); + window.onSemanticsEnabledChanged = semanticsChanged.complete; + return semanticsChanged.future; +} + +/// Returns a future that completes when `window.onAccessibilityFeaturesChanged` +/// fires. +Future get accessibilityFeaturesChanged { + final Completer featuresChanged = Completer(); + window.onAccessibilityFeaturesChanged = featuresChanged.complete; + return featuresChanged.future; +} + +class SemanticsActionData { + const SemanticsActionData(this.id, this.action, this.args); + final int id; + final SemanticsAction action; + final ByteData args; +} + +Future get semanticsAction { + final Completer actionReceived = Completer(); + window.onSemanticsAction = (int id, SemanticsAction action, ByteData args) { + actionReceived.complete(SemanticsActionData(id, action, args)); + }; + return actionReceived.future; +} + +main() async { + // Return initial state (semantics disabled). + notifySemanticsEnabled(window.semanticsEnabled); + + // Await semantics enabled from embedder. + await semanticsChanged; + notifySemanticsEnabled(window.semanticsEnabled); + + // Return initial state of accessibility features. + notifyAccessibilityFeatures(window.accessibilityFeatures.reduceMotion); + + // Await accessibility features changed from embedder. + await accessibilityFeaturesChanged; + notifyAccessibilityFeatures(window.accessibilityFeatures.reduceMotion); + + // Fire semantics update. + final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder() + ..updateNode( + id: 42, + label: 'A: root', + rect: Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), + transform: kTestTransform, + childrenInTraversalOrder: Int32List.fromList([84, 96]), + childrenInHitTestOrder: Int32List.fromList([96, 84]), + ) + ..updateNode( + id: 84, + label: 'B: leaf', + rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + ) + ..updateNode( + id: 96, + label: 'C: branch', + rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + childrenInTraversalOrder: Int32List.fromList([128]), + childrenInHitTestOrder: Int32List.fromList([128]), + ) + ..updateNode( + id: 128, + label: 'D: leaf', + rect: Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + additionalActions: Int32List.fromList([21]), + ) + ..updateCustomAction( + id: 21, + label: 'Archive', + hint: 'archive message', + ); + window.updateSemantics(builder.build()); + signalNativeTest(); + + // Await semantics action from embedder. + final SemanticsActionData data = await semanticsAction; + final List actionArgs = [data.args.getInt8(0), data.args.getInt8(1)]; + notifySemanticsAction(data.id, data.action.index, actionArgs); + + // Await semantics disabled from embedder. + await semanticsChanged; + notifySemanticsEnabled(window.semanticsEnabled); +} diff --git a/shell/platform/embedder/fixtures/simple_main.dart b/shell/platform/embedder/fixtures/simple_main.dart index 05a37e98348ad..853e136f96ed8 100644 --- a/shell/platform/embedder/fixtures/simple_main.dart +++ b/shell/platform/embedder/fixtures/simple_main.dart @@ -1 +1,28 @@ -void main() {} \ No newline at end of file +import 'dart:ui'; + +void main() {} + +@pragma('vm:entry-point') +void customEntrypoint() { + sayHiFromCustomEntrypoint(); +} + +void sayHiFromCustomEntrypoint() native "SayHiFromCustomEntrypoint"; + + +@pragma('vm:entry-point') +void customEntrypoint1() { + sayHiFromCustomEntrypoint1(); + sayHiFromCustomEntrypoint2(); + sayHiFromCustomEntrypoint3(); +} + +void sayHiFromCustomEntrypoint1() native "SayHiFromCustomEntrypoint1"; +void sayHiFromCustomEntrypoint2() native "SayHiFromCustomEntrypoint2"; +void sayHiFromCustomEntrypoint3() native "SayHiFromCustomEntrypoint3"; + + +@pragma('vm:entry-point') +void invokePlatformTaskRunner() { + window.sendPlatformMessage('OhHi', null, null); +} diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 07f6c8dc1ff95..bdb45c072dbe0 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -1,13 +1,9 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/platform/embedder/platform_view_embedder.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { PlatformViewEmbedder::PlatformViewEmbedder( @@ -34,6 +30,19 @@ PlatformViewEmbedder::PlatformViewEmbedder( PlatformViewEmbedder::~PlatformViewEmbedder() = default; +void PlatformViewEmbedder::UpdateSemantics( + blink::SemanticsNodeUpdates update, + blink::CustomAccessibilityActionUpdates actions) { + if (platform_dispatch_table_.update_semantics_nodes_callback != nullptr) { + platform_dispatch_table_.update_semantics_nodes_callback(std::move(update)); + } + if (platform_dispatch_table_.update_semantics_custom_actions_callback != + nullptr) { + platform_dispatch_table_.update_semantics_custom_actions_callback( + std::move(actions)); + } +} + void PlatformViewEmbedder::HandlePlatformMessage( fml::RefPtr message) { if (!message) { @@ -71,4 +80,15 @@ sk_sp PlatformViewEmbedder::CreateResourceContext() const { return embedder_surface_->CreateResourceContext(); } +// |shell::PlatformView| +std::unique_ptr PlatformViewEmbedder::CreateVSyncWaiter() { + if (!platform_dispatch_table_.vsync_callback) { + // Superclass implementation creates a timer based fallback. + return PlatformView::CreateVSyncWaiter(); + } + + return std::make_unique( + platform_dispatch_table_.vsync_callback, task_runners_); +} + } // namespace shell diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index af28dd83625c6..377dd337ddee8 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,17 +13,26 @@ #include "flutter/shell/platform/embedder/embedder_surface.h" #include "flutter/shell/platform/embedder/embedder_surface_gl.h" #include "flutter/shell/platform/embedder/embedder_surface_software.h" +#include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" namespace shell { class PlatformViewEmbedder final : public PlatformView { public: + using UpdateSemanticsNodesCallback = + std::function; + using UpdateSemanticsCustomActionsCallback = + std::function; using PlatformMessageResponseCallback = std::function)>; struct PlatformDispatchTable { + UpdateSemanticsNodesCallback update_semantics_nodes_callback; // optional + UpdateSemanticsCustomActionsCallback + update_semantics_custom_actions_callback; // optional PlatformMessageResponseCallback - platform_message_response_callback; // optional + platform_message_response_callback; // optional + VsyncWaiterEmbedder::VsyncCallback vsync_callback; // optional }; // Creates a platform view that sets up an OpenGL rasterizer. @@ -42,6 +51,11 @@ class PlatformViewEmbedder final : public PlatformView { ~PlatformViewEmbedder() override; + // |shell::PlatformView| + void UpdateSemantics( + blink::SemanticsNodeUpdates update, + blink::CustomAccessibilityActionUpdates actions) override; + // |shell::PlatformView| void HandlePlatformMessage( fml::RefPtr message) override; @@ -56,6 +70,9 @@ class PlatformViewEmbedder final : public PlatformView { // |shell::PlatformView| sk_sp CreateResourceContext() const override; + // |shell::PlatformView| + std::unique_ptr CreateVSyncWaiter() override; + FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder); }; diff --git a/shell/platform/embedder/tests/embedder_a11y_unittests.cc b/shell/platform/embedder/tests/embedder_a11y_unittests.cc new file mode 100644 index 0000000000000..cfb15db76dc68 --- /dev/null +++ b/shell/platform/embedder/tests/embedder_a11y_unittests.cc @@ -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. + +// Allow access to fml::MessageLoop::GetCurrent() in order to flush platform +// thread tasks. +#define FML_USED_ON_EMBEDDER + +#include +#include "embedder.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/shell/platform/embedder/embedder_engine.h" +#include "flutter/testing/testing.h" +#include "third_party/dart/runtime/include/dart_api.h" +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_library_natives.h" + +#define REGISTER_FUNCTION(name, count) {"" #name, name, count, true}, +#define DECLARE_FUNCTION(name, count) \ + extern void name(Dart_NativeArguments args); +#define BUILTIN_NATIVE_LIST(V) \ + V(SignalNativeTest, 0) \ + V(NotifyTestData1, 1) \ + V(NotifyTestData3, 3) + +BUILTIN_NATIVE_LIST(DECLARE_FUNCTION); + +static tonic::DartLibraryNatives* g_natives; + +Dart_NativeFunction GetNativeFunction(Dart_Handle name, + int argument_count, + bool* auto_setup_scope) { + return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope); +} + +const uint8_t* GetSymbol(Dart_NativeFunction native_function) { + return g_natives->GetSymbol(native_function); +} + +using OnTestDataCallback = std::function; + +fml::AutoResetWaitableEvent g_latch; +OnTestDataCallback g_test_data_callback = [](Dart_NativeArguments) {}; + +// Called by the Dart text fixture on the UI thread to signal that the C++ +// unittest should resume. +void SignalNativeTest(Dart_NativeArguments args) { + g_latch.Signal(); +} + +// Called by test fixture on UI thread to pass data back to this test. +// 1 parameter version. +void NotifyTestData1(Dart_NativeArguments args) { + g_test_data_callback(args); +} + +// Called by test fixture on UI thread to pass data back to this test. +// 3 parameter version. +void NotifyTestData3(Dart_NativeArguments args) { + g_test_data_callback(args); +} + +TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { + FlutterSoftwareRendererConfig renderer; + renderer.struct_size = sizeof(FlutterSoftwareRendererConfig); + renderer.surface_present_callback = [](void*, const void*, size_t, size_t) { + return false; + }; + + FlutterRendererConfig config = {}; + config.type = FlutterRendererType::kSoftware; + config.software = renderer; + + FlutterProjectArgs args = {}; + args.struct_size = sizeof(FlutterProjectArgs); + args.assets_path = testing::GetFixturesPath(); + + // Register native functions to be called from test fixture. + g_natives = new tonic::DartLibraryNatives(); + g_natives->Register({BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)}); + args.root_isolate_create_callback = [](void*) { + Dart_SetNativeResolver(Dart_RootLibrary(), GetNativeFunction, GetSymbol); + }; + + typedef struct { + std::function on_semantics_update; + std::function + on_custom_action_update; + } TestData; + auto test_data = TestData{}; + args.update_semantics_node_callback = [](const FlutterSemanticsNode* node, + void* data) { + auto test_data = reinterpret_cast(data); + test_data->on_semantics_update(node); + }; + args.update_semantics_custom_action_callback = + [](const FlutterSemanticsCustomAction* action, void* data) { + auto test_data = reinterpret_cast(data); + test_data->on_custom_action_update(action); + }; + + // Start the engine, run text fixture. + FlutterEngine engine = nullptr; + FlutterEngineResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, + &args, &test_data, &engine); + ASSERT_EQ(result, FlutterEngineResult::kSuccess); + + // Wait for initial NotifySemanticsEnabled(false). + g_test_data_callback = [](Dart_NativeArguments args) { + bool enabled; + Dart_GetNativeBooleanArgument(args, 0, &enabled); + ASSERT_FALSE(enabled); + g_latch.Signal(); + }; + g_latch.Wait(); + + // Enable semantics. Wait for NotifySemanticsEnabled(true). + g_test_data_callback = [](Dart_NativeArguments args) { + bool enabled; + Dart_GetNativeBooleanArgument(args, 0, &enabled); + ASSERT_TRUE(enabled); + g_latch.Signal(); + }; + result = FlutterEngineUpdateSemanticsEnabled(engine, true); + ASSERT_EQ(result, FlutterEngineResult::kSuccess); + g_latch.Wait(); + + // Wait for initial accessibility features (reduce_motion == false) + g_test_data_callback = [](Dart_NativeArguments args) { + bool enabled; + Dart_GetNativeBooleanArgument(args, 0, &enabled); + ASSERT_FALSE(enabled); + g_latch.Signal(); + }; + g_latch.Wait(); + + // Set accessibility features: (reduce_motion == true) + g_test_data_callback = [](Dart_NativeArguments args) { + bool enabled; + Dart_GetNativeBooleanArgument(args, 0, &enabled); + ASSERT_TRUE(enabled); + g_latch.Signal(); + }; + result = FlutterEngineUpdateAccessibilityFeatures( + engine, kFlutterAccessibilityFeatureReduceMotion); + ASSERT_EQ(result, FlutterEngineResult::kSuccess); + g_latch.Wait(); + + // Wait for UpdateSemantics callback on platform (current) thread. + int node_count = 0; + int node_batch_end_count = 0; + test_data.on_semantics_update = + [&node_count, &node_batch_end_count](const FlutterSemanticsNode* node) { + if (node->id == kFlutterSemanticsNodeIdBatchEnd) { + ++node_batch_end_count; + } else { + ++node_count; + ASSERT_EQ(1.0, node->transform.scaleX); + ASSERT_EQ(2.0, node->transform.skewX); + ASSERT_EQ(3.0, node->transform.transX); + ASSERT_EQ(4.0, node->transform.skewY); + ASSERT_EQ(5.0, node->transform.scaleY); + ASSERT_EQ(6.0, node->transform.transY); + ASSERT_EQ(7.0, node->transform.pers0); + ASSERT_EQ(8.0, node->transform.pers1); + ASSERT_EQ(9.0, node->transform.pers2); + } + }; + int action_count = 0; + int action_batch_end_count = 0; + test_data.on_custom_action_update = + [&action_count, + &action_batch_end_count](const FlutterSemanticsCustomAction* action) { + if (action->id == kFlutterSemanticsCustomActionIdBatchEnd) { + ++action_batch_end_count; + } else { + ++action_count; + } + }; + g_latch.Wait(); + fml::MessageLoop::GetCurrent().RunExpiredTasksNow(); + ASSERT_EQ(4, node_count); + ASSERT_EQ(1, node_batch_end_count); + ASSERT_EQ(1, action_count); + ASSERT_EQ(1, action_batch_end_count); + + // Dispatch a tap to semantics node 42. Wait for NotifySemanticsAction. + g_test_data_callback = [](Dart_NativeArguments args) { + int64_t node_id; + Dart_GetNativeIntegerArgument(args, 0, &node_id); + ASSERT_EQ(42, node_id); + + int64_t action_id; + Dart_GetNativeIntegerArgument(args, 1, &action_id); + ASSERT_EQ(static_cast(blink::SemanticsAction::kTap), action_id); + + Dart_Handle semantic_args = Dart_GetNativeArgument(args, 2); + int64_t data; + Dart_Handle dart_int = Dart_ListGetAt(semantic_args, 0); + Dart_IntegerToInt64(dart_int, &data); + ASSERT_EQ(2, data); + + dart_int = Dart_ListGetAt(semantic_args, 1); + Dart_IntegerToInt64(dart_int, &data); + ASSERT_EQ(1, data); + g_latch.Signal(); + }; + std::vector bytes({2, 1}); + result = FlutterEngineDispatchSemanticsAction( + engine, 42, kFlutterSemanticsActionTap, &bytes[0], bytes.size()); + g_latch.Wait(); + + // Disable semantics. Wait for NotifySemanticsEnabled(false). + g_test_data_callback = [](Dart_NativeArguments args) { + bool enabled; + Dart_GetNativeBooleanArgument(args, 0, &enabled); + ASSERT_FALSE(enabled); + g_latch.Signal(); + }; + result = FlutterEngineUpdateSemanticsEnabled(engine, false); + ASSERT_EQ(result, FlutterEngineResult::kSuccess); + g_latch.Wait(); + + result = FlutterEngineShutdown(engine); + ASSERT_EQ(result, FlutterEngineResult::kSuccess); +} diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc new file mode 100644 index 0000000000000..546f95a78ea1a --- /dev/null +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" + +namespace shell { +namespace testing { + +EmbedderConfigBuilder::EmbedderConfigBuilder( + EmbedderContext& context, + InitializationPreference preference) + : context_(context) { + project_args_.struct_size = sizeof(project_args_); + software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig); + custom_task_runners_.struct_size = sizeof(FlutterCustomTaskRunners); + software_renderer_config_.surface_present_callback = + [](void*, const void*, size_t, size_t) { return true; }; + + if (preference == InitializationPreference::kInitialize) { + SetSoftwareRendererConfig(); + SetAssetsPath(); + SetSnapshots(); + SetIsolateCreateCallbackHook(); + } +} + +EmbedderConfigBuilder::~EmbedderConfigBuilder() = default; + +void EmbedderConfigBuilder::SetSoftwareRendererConfig() { + renderer_config_.type = FlutterRendererType::kSoftware; + renderer_config_.software = software_renderer_config_; +} + +void EmbedderConfigBuilder::SetAssetsPath() { + project_args_.assets_path = context_.GetAssetsPath().c_str(); +} + +void EmbedderConfigBuilder::SetSnapshots() { + if (auto mapping = context_.GetVMSnapshotData()) { + project_args_.vm_snapshot_data = mapping->GetMapping(); + project_args_.vm_snapshot_data_size = mapping->GetSize(); + } + + if (auto mapping = context_.GetVMSnapshotInstructions()) { + project_args_.vm_snapshot_instructions = mapping->GetMapping(); + project_args_.vm_snapshot_instructions_size = mapping->GetSize(); + } + + if (auto mapping = context_.GetIsolateSnapshotData()) { + project_args_.isolate_snapshot_data = mapping->GetMapping(); + project_args_.isolate_snapshot_data_size = mapping->GetSize(); + } + + if (auto mapping = context_.GetIsolateSnapshotInstructions()) { + project_args_.isolate_snapshot_instructions = mapping->GetMapping(); + project_args_.isolate_snapshot_instructions_size = mapping->GetSize(); + } +} + +void EmbedderConfigBuilder::SetIsolateCreateCallbackHook() { + project_args_.root_isolate_create_callback = + EmbedderContext::GetIsolateCreateCallbackHook(); +} + +void EmbedderConfigBuilder::SetDartEntrypoint(std::string entrypoint) { + if (entrypoint.size() == 0) { + return; + } + + dart_entrypoint_ = std::move(entrypoint); + project_args_.custom_dart_entrypoint = dart_entrypoint_.c_str(); +} + +void EmbedderConfigBuilder::AddCommandLineArgument(std::string arg) { + if (arg.size() == 0) { + return; + } + + command_line_arguments_.emplace_back(std::move(arg)); +} + +void EmbedderConfigBuilder::SetPlatformTaskRunner( + const FlutterTaskRunnerDescription* runner) { + if (runner == nullptr) { + return; + } + custom_task_runners_.platform_task_runner = runner; + project_args_.custom_task_runners = &custom_task_runners_; +} + +UniqueEngine EmbedderConfigBuilder::LaunchEngine() { + FlutterEngine engine = nullptr; + + std::vector args; + args.reserve(command_line_arguments_.size()); + + for (const auto& arg : command_line_arguments_) { + args.push_back(arg.c_str()); + } + + if (args.size() > 0) { + project_args_.command_line_argv = args.data(); + project_args_.command_line_argc = args.size(); + } else { + // Clear it out in case this is not the first engine launch from the + // embedder config builder. + project_args_.command_line_argv = nullptr; + project_args_.command_line_argc = 0; + } + + auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_, + &project_args_, &context_, &engine); + + if (result != kSuccess) { + return {}; + } + + return UniqueEngine{engine}; +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h new file mode 100644 index 0000000000000..28c057d11441b --- /dev/null +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONFIG_BUILDER_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONFIG_BUILDER_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_object.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/tests/embedder_context.h" +#include "flutter/shell/platform/embedder/tests/embedder_test.h" + +namespace shell { +namespace testing { + +struct UniqueEngineTraits { + static FlutterEngine InvalidValue() { return nullptr; } + + static bool IsValid(const FlutterEngine& value) { return value != nullptr; } + + static void Free(FlutterEngine& engine) { + auto result = FlutterEngineShutdown(engine); + FML_CHECK(result == kSuccess); + } +}; + +using UniqueEngine = fml::UniqueObject; + +class EmbedderConfigBuilder { + public: + enum class InitializationPreference { + kInitialize, + kNoInitialize, + }; + + EmbedderConfigBuilder(EmbedderContext& context, + InitializationPreference preference = + InitializationPreference::kInitialize); + + ~EmbedderConfigBuilder(); + + void SetSoftwareRendererConfig(); + + void SetAssetsPath(); + + void SetSnapshots(); + + void SetIsolateCreateCallbackHook(); + + void SetDartEntrypoint(std::string entrypoint); + + void AddCommandLineArgument(std::string arg); + + void SetPlatformTaskRunner(const FlutterTaskRunnerDescription* runner); + + UniqueEngine LaunchEngine(); + + private: + EmbedderContext& context_; + FlutterProjectArgs project_args_ = {}; + FlutterRendererConfig renderer_config_ = {}; + FlutterSoftwareRendererConfig software_renderer_config_ = {}; + std::string dart_entrypoint_; + FlutterCustomTaskRunners custom_task_runners_ = {}; + std::vector command_line_arguments_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderConfigBuilder); +}; + +} // namespace testing +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONFIG_BUILDER_H_ diff --git a/shell/platform/embedder/tests/embedder_context.cc b/shell/platform/embedder/tests/embedder_context.cc new file mode 100644 index 0000000000000..edb72ff4c2f24 --- /dev/null +++ b/shell/platform/embedder/tests/embedder_context.cc @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/tests/embedder_context.h" + +#include "flutter/runtime/dart_vm.h" + +namespace shell { +namespace testing { + +static std::unique_ptr GetMapping(const fml::UniqueFD& directory, + const char* path, + bool executable) { + fml::UniqueFD file = fml::OpenFile(directory, path, false /* create */, + fml::FilePermission::kRead); + if (!file.is_valid()) { + return nullptr; + } + + using Prot = fml::FileMapping::Protection; + std::unique_ptr mapping; + if (executable) { + mapping = std::make_unique( + file, std::initializer_list{Prot::kRead, Prot::kExecute}); + } else { + mapping = std::make_unique( + file, std::initializer_list{Prot::kRead}); + } + + if (mapping->GetSize() == 0 || mapping->GetMapping() == nullptr) { + return nullptr; + } + + return mapping; +} + +EmbedderContext::EmbedderContext(std::string assets_path) + : assets_path_(std::move(assets_path)), + native_resolver_(std::make_shared()) { + auto assets_dir = fml::OpenDirectory(assets_path_.c_str(), false, + fml::FilePermission::kRead); + vm_snapshot_data_ = GetMapping(assets_dir, "vm_snapshot_data", false); + isolate_snapshot_data_ = + GetMapping(assets_dir, "isolate_snapshot_data", false); + + if (blink::DartVM::IsRunningPrecompiledCode()) { + vm_snapshot_instructions_ = + GetMapping(assets_dir, "vm_snapshot_instr", true); + isolate_snapshot_instructions_ = + GetMapping(assets_dir, "isolate_snapshot_instr", true); + } + + isolate_create_callbacks_.push_back( + [weak_resolver = + std::weak_ptr{native_resolver_}]() { + if (auto resolver = weak_resolver.lock()) { + resolver->SetNativeResolverForIsolate(); + } + }); +} + +EmbedderContext::~EmbedderContext() = default; + +const std::string& EmbedderContext::GetAssetsPath() const { + return assets_path_; +} + +const fml::Mapping* EmbedderContext::GetVMSnapshotData() const { + return vm_snapshot_data_.get(); +} + +const fml::Mapping* EmbedderContext::GetVMSnapshotInstructions() const { + return vm_snapshot_instructions_.get(); +} + +const fml::Mapping* EmbedderContext::GetIsolateSnapshotData() const { + return isolate_snapshot_data_.get(); +} + +const fml::Mapping* EmbedderContext::GetIsolateSnapshotInstructions() const { + return isolate_snapshot_instructions_.get(); +} + +void EmbedderContext::AddIsolateCreateCallback(fml::closure closure) { + if (closure) { + isolate_create_callbacks_.push_back(closure); + } +} + +VoidCallback EmbedderContext::GetIsolateCreateCallbackHook() { + return [](void* user_data) { + reinterpret_cast(user_data)->FireIsolateCreateCallbacks(); + }; +} + +void EmbedderContext::FireIsolateCreateCallbacks() { + for (auto closure : isolate_create_callbacks_) { + closure(); + } +} + +void EmbedderContext::AddNativeCallback(const char* name, + Dart_NativeFunction function) { + native_resolver_->AddNativeCallback({name}, function); +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/tests/embedder_context.h b/shell/platform/embedder/tests/embedder_context.h new file mode 100644 index 0000000000000..cf31140d40029 --- /dev/null +++ b/shell/platform/embedder/tests/embedder_context.h @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter 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_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ + +#include +#include +#include +#include + +#include "flutter/fml/closure.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h" + +#define CREATE_NATIVE_ENTRY(native_entry) \ + ([&]() { \ + static ::shell::testing::EmbedderContext::NativeEntry closure; \ + static Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) { \ + closure(args); \ + }; \ + closure = (native_entry); \ + return entrypoint; \ + })() + +namespace shell { +namespace testing { + +class EmbedderContext { + public: + using NativeEntry = std::function; + + EmbedderContext(std::string assets_path = ""); + + ~EmbedderContext(); + + const std::string& GetAssetsPath() const; + + const fml::Mapping* GetVMSnapshotData() const; + + const fml::Mapping* GetVMSnapshotInstructions() const; + + const fml::Mapping* GetIsolateSnapshotData() const; + + const fml::Mapping* GetIsolateSnapshotInstructions() const; + + void AddIsolateCreateCallback(fml::closure closure); + + void AddNativeCallback(const char* name, Dart_NativeFunction function); + + private: + // This allows the builder to access the hooks. + friend class EmbedderConfigBuilder; + + std::string assets_path_; + std::unique_ptr vm_snapshot_data_; + std::unique_ptr vm_snapshot_instructions_; + std::unique_ptr isolate_snapshot_data_; + std::unique_ptr isolate_snapshot_instructions_; + std::vector isolate_create_callbacks_; + std::shared_ptr native_resolver_; + + static VoidCallback GetIsolateCreateCallbackHook(); + + void FireIsolateCreateCallbacks(); + + void SetNativeResolver(); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderContext); +}; + +} // namespace testing +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_CONTEXT_H_ diff --git a/shell/platform/embedder/tests/embedder_test.cc b/shell/platform/embedder/tests/embedder_test.cc new file mode 100644 index 0000000000000..b23e79596745f --- /dev/null +++ b/shell/platform/embedder/tests/embedder_test.cc @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/tests/embedder_test.h" + +namespace shell { +namespace testing { + +EmbedderTest::EmbedderTest() = default; + +EmbedderTest::~EmbedderTest() = default; + +std::string EmbedderTest::GetFixturesDirectory() const { + return ::testing::GetFixturesPath(); +} + +EmbedderContext& EmbedderTest::GetEmbedderContext() { + // Setup the embedder context lazily instead of in the SetUp method because we + // don't to do all the work if the test won't end up using context. + if (!embedder_context_) { + embedder_context_ = + std::make_unique(GetFixturesDirectory()); + } + return *embedder_context_; +} + +// |testing::Test| +void EmbedderTest::SetUp() { + // Nothing to do here since we will lazily setup the context when asked. +} + +// |testing::Test| +void EmbedderTest::TearDown() { + embedder_context_.reset(); +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/tests/embedder_test.h b/shell/platform/embedder/tests/embedder_test.h new file mode 100644 index 0000000000000..f0850bf6f8a7d --- /dev/null +++ b/shell/platform/embedder/tests/embedder_test.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter 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_EMBEDDER_TESTS_EMBEDDER_TEST_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/embedder/tests/embedder_context.h" +#include "flutter/testing/testing.h" + +namespace shell { +namespace testing { + +class EmbedderTest : public ::testing::Test { + public: + EmbedderTest(); + + ~EmbedderTest() override; + + std::string GetFixturesDirectory() const; + + EmbedderContext& GetEmbedderContext(); + + private: + std::unique_ptr embedder_context_; + + // |testing::Test| + void SetUp() override; + + // |testing::Test| + void TearDown() override; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTest); +}; + +} // namespace testing +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_H_ diff --git a/shell/platform/embedder/tests/embedder_test_resolver.cc b/shell/platform/embedder/tests/embedder_test_resolver.cc new file mode 100644 index 0000000000000..49171a91df23c --- /dev/null +++ b/shell/platform/embedder/tests/embedder_test_resolver.cc @@ -0,0 +1,90 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/tests/embedder_test_resolver.h" + +#include +#include + +#include "flutter/fml/synchronization/thread_annotations.h" +#include "tonic/converter/dart_converter.h" + +namespace shell { +namespace testing { + +EmbedderTestResolver::EmbedderTestResolver() = default; + +EmbedderTestResolver::~EmbedderTestResolver() = default; + +void EmbedderTestResolver::AddNativeCallback(std::string name, + Dart_NativeFunction callback) { + native_callbacks_[name] = callback; +} + +Dart_NativeFunction EmbedderTestResolver::ResolveCallback( + std::string name) const { + auto found = native_callbacks_.find(name); + if (found == native_callbacks_.end()) { + return nullptr; + } + + return found->second; +} + +static std::mutex gIsolateResolversMutex; +static std::map> + gIsolateResolvers FML_GUARDED_BY(gIsolateResolversMutex); + +Dart_NativeFunction EmbedderTestResolver::DartNativeEntryResolverCallback( + Dart_Handle dart_name, + int num_of_arguments, + bool* auto_setup_scope) { + auto name = tonic::StdStringFromDart(dart_name); + + std::lock_guard lock(gIsolateResolversMutex); + auto found = gIsolateResolvers.find(Dart_CurrentIsolate()); + if (found == gIsolateResolvers.end()) { + return nullptr; + } + + if (auto resolver = found->second.lock()) { + return resolver->ResolveCallback(std::move(name)); + } else { + gIsolateResolvers.erase(found); + } + + return nullptr; +} + +static const uint8_t* DartNativeEntrySymbolCallback( + Dart_NativeFunction function) { + return reinterpret_cast("¯\\_(ツ)_/¯"); +} + +void EmbedderTestResolver::SetNativeResolverForIsolate() { + auto result = Dart_SetNativeResolver(Dart_RootLibrary(), + DartNativeEntryResolverCallback, + DartNativeEntrySymbolCallback); + + if (Dart_IsError(result)) { + return; + } + + std::lock_guard lock(gIsolateResolversMutex); + gIsolateResolvers[Dart_CurrentIsolate()] = shared_from_this(); + + std::vector isolates_with_dead_resolvers; + for (const auto& entry : gIsolateResolvers) { + if (!entry.second.lock()) { + isolates_with_dead_resolvers.push_back(entry.first); + } + } + + for (const auto& dead_isolate : isolates_with_dead_resolvers) { + gIsolateResolvers.erase(dead_isolate); + } +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/tests/embedder_test_resolver.h b/shell/platform/embedder/tests/embedder_test_resolver.h new file mode 100644 index 0000000000000..69d08efe1042d --- /dev/null +++ b/shell/platform/embedder/tests/embedder_test_resolver.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 FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_ + +#include +#include + +#include "flutter/fml/macros.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace shell { +namespace testing { + +class EmbedderTestResolver + : public std::enable_shared_from_this { + public: + EmbedderTestResolver(); + + ~EmbedderTestResolver(); + + void AddNativeCallback(std::string name, Dart_NativeFunction callback); + + private: + // Friend so that the context can set the native resolver. + friend class EmbedderContext; + + std::map native_callbacks_; + + void SetNativeResolverForIsolate(); + + Dart_NativeFunction ResolveCallback(std::string name) const; + + static Dart_NativeFunction DartNativeEntryResolverCallback( + Dart_Handle dart_name, + int num_of_arguments, + bool* auto_setup_scope); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestResolver); +}; + +} // namespace testing +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_TESTS_EMBEDDER_TEST_RESOLVER_H_ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index ebf059f591e29..357a4e71114ab 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1,46 +1,214 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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 + #include "embedder.h" +#include "flutter/fml/file.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/thread.h" +#include "flutter/shell/platform/embedder/tests/embedder_config_builder.h" +#include "flutter/shell/platform/embedder/tests/embedder_test.h" #include "flutter/testing/testing.h" -TEST(EmbedderTest, MustNotRunWithInvalidArgs) { - FlutterEngine engine = nullptr; - FlutterRendererConfig config = {}; - FlutterProjectArgs args = {}; - FlutterResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION + 1, &config, - &args, NULL, &engine); - ASSERT_NE(result, FlutterResult::kSuccess); +namespace shell { +namespace testing { + +using EmbedderTest = testing::EmbedderTest; + +TEST(EmbedderTestNoFixture, MustNotRunWithInvalidArgs) { + EmbedderContext context; + EmbedderConfigBuilder builder( + context, EmbedderConfigBuilder::InitializationPreference::kNoInitialize); + auto engine = builder.LaunchEngine(); + ASSERT_FALSE(engine.is_valid()); +} + +TEST_F(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { + auto& context = GetEmbedderContext(); + fml::AutoResetWaitableEvent latch; + context.AddIsolateCreateCallback([&latch]() { latch.Signal(); }); + EmbedderConfigBuilder builder(context); + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + // Wait for the root isolate to launch. + latch.Wait(); + engine.reset(); +} + +TEST_F(EmbedderTest, CanLaunchAndShutdownMultipleTimes) { + EmbedderConfigBuilder builder(GetEmbedderContext()); + for (size_t i = 0; i < 3; ++i) { + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + FML_LOG(INFO) << "Engine launch count: " << i + 1; + } +} + +TEST_F(EmbedderTest, CanInvokeCustomEntrypoint) { + auto& context = GetEmbedderContext(); + static fml::AutoResetWaitableEvent latch; + Dart_NativeFunction entrypoint = [](Dart_NativeArguments args) { + latch.Signal(); + }; + context.AddNativeCallback("SayHiFromCustomEntrypoint", entrypoint); + EmbedderConfigBuilder builder(context); + builder.SetDartEntrypoint("customEntrypoint"); + auto engine = builder.LaunchEngine(); + latch.Wait(); + ASSERT_TRUE(engine.is_valid()); } -TEST(EmbedderTest, CanLaunchAndShutdownWithValidProjectArgs) { - FlutterOpenGLRendererConfig renderer = {}; - renderer.struct_size = sizeof(FlutterOpenGLRendererConfig); - renderer.make_current = [](void*) { return false; }; - renderer.clear_current = [](void*) { return false; }; - renderer.present = [](void*) { return false; }; - renderer.fbo_callback = [](void*) -> uint32_t { return 0; }; - - std::string main = - std::string(testing::GetFixturesPath()) + "/simple_main.dart"; - - FlutterRendererConfig config = {}; - config.type = FlutterRendererType::kOpenGL; - config.open_gl = renderer; - - FlutterProjectArgs args = {}; - args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = ""; - args.main_path = main.c_str(); - args.packages_path = ""; - - FlutterEngine engine = nullptr; - FlutterResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, - &args, nullptr, &engine); - ASSERT_EQ(result, FlutterResult::kSuccess); - - result = FlutterEngineShutdown(engine); - ASSERT_EQ(result, FlutterResult::kSuccess); +TEST_F(EmbedderTest, CanInvokeCustomEntrypointMacro) { + auto& context = GetEmbedderContext(); + + fml::AutoResetWaitableEvent latch1; + fml::AutoResetWaitableEvent latch2; + fml::AutoResetWaitableEvent latch3; + + // Can be defined separately. + auto entry1 = [&latch1](Dart_NativeArguments args) { + FML_LOG(INFO) << "In Callback 1"; + latch1.Signal(); + }; + auto native_entry1 = CREATE_NATIVE_ENTRY(entry1); + context.AddNativeCallback("SayHiFromCustomEntrypoint1", native_entry1); + + // Can be wrapped in in the args. + auto entry2 = [&latch2](Dart_NativeArguments args) { + FML_LOG(INFO) << "In Callback 2"; + latch2.Signal(); + }; + context.AddNativeCallback("SayHiFromCustomEntrypoint2", + CREATE_NATIVE_ENTRY(entry2)); + + // Everything can be inline. + context.AddNativeCallback( + "SayHiFromCustomEntrypoint3", + CREATE_NATIVE_ENTRY([&latch3](Dart_NativeArguments args) { + FML_LOG(INFO) << "In Callback 3"; + latch3.Signal(); + })); + + EmbedderConfigBuilder builder(context); + builder.SetDartEntrypoint("customEntrypoint1"); + auto engine = builder.LaunchEngine(); + latch1.Wait(); + latch2.Wait(); + latch3.Wait(); + ASSERT_TRUE(engine.is_valid()); } + +class EmbedderTestTaskRunner { + public: + EmbedderTestTaskRunner(std::function on_forward_task) + : on_forward_task_(on_forward_task) {} + + void SetForwardingTaskRunner(fml::RefPtr runner) { + forwarding_target_ = std::move(runner); + } + + FlutterTaskRunnerDescription GetEmbedderDescription() { + FlutterTaskRunnerDescription desc; + desc.struct_size = sizeof(desc); + desc.user_data = this; + desc.runs_task_on_current_thread_callback = [](void* user_data) -> bool { + return reinterpret_cast(user_data) + ->forwarding_target_->RunsTasksOnCurrentThread(); + }; + desc.post_task_callback = [](FlutterTask task, uint64_t target_time_nanos, + void* user_data) -> void { + auto runner = reinterpret_cast(user_data); + + auto target_time = fml::TimePoint::FromEpochDelta( + fml::TimeDelta::FromNanoseconds(target_time_nanos)); + + runner->forwarding_target_->PostTaskForTime( + [task, forwarder = runner->on_forward_task_]() { forwarder(task); }, + target_time); + }; + return desc; + } + + private: + fml::RefPtr forwarding_target_; + std::function on_forward_task_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTestTaskRunner); +}; + +TEST_F(EmbedderTest, CanSpecifyCustomTaskRunner) { + auto& context = GetEmbedderContext(); + fml::AutoResetWaitableEvent latch; + + // Run the test on its own thread with a message loop so that it san safely + // pump its event loop while we wait for all the conditions to be checked. + fml::Thread thread; + UniqueEngine engine; + bool signalled = false; + + EmbedderTestTaskRunner runner([&](FlutterTask task) { + // There may be multiple tasks posted but we only need to check assertions + // once. + if (signalled) { + // Since we have the baton, return it back to the engine. We don't care + // about the return value because the engine could be shutting down an it + // may not actually be able to accept the same. + FlutterEngineRunTask(engine.get(), &task); + return; + } + + signalled = true; + FML_LOG(INFO) << "Checking assertions."; + ASSERT_TRUE(engine.is_valid()); + ASSERT_EQ(FlutterEngineRunTask(engine.get(), &task), kSuccess); + latch.Signal(); + }); + + thread.GetTaskRunner()->PostTask([&]() { + EmbedderConfigBuilder builder(context); + builder.AddCommandLineArgument("--verbose-logging"); + const auto task_runner_description = runner.GetEmbedderDescription(); + runner.SetForwardingTaskRunner( + fml::MessageLoop::GetCurrent().GetTaskRunner()); + builder.SetPlatformTaskRunner(&task_runner_description); + builder.SetDartEntrypoint("invokePlatformTaskRunner"); + engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + }); + + // Signalled when all the assertions are checked. + latch.Wait(); + FML_LOG(INFO) << "Assertions checked. Killing engine."; + ASSERT_TRUE(engine.is_valid()); + + // Since the engine was started on its own thread, it must be killed there as + // well. + fml::AutoResetWaitableEvent kill_latch; + thread.GetTaskRunner()->PostTask( + fml::MakeCopyable([&engine, &kill_latch]() mutable { + engine.reset(); + FML_LOG(INFO) << "Engine killed."; + kill_latch.Signal(); + })); + kill_latch.Wait(); + + ASSERT_TRUE(signalled); +} + +TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) { + auto point1 = fml::TimePoint::FromEpochDelta( + fml::TimeDelta::FromNanoseconds(FlutterEngineGetCurrentTime())); + auto point2 = fml::TimePoint::Now(); + + ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1)); +} + +} // namespace testing +} // namespace shell diff --git a/shell/platform/embedder/vsync_waiter_embedder.cc b/shell/platform/embedder/vsync_waiter_embedder.cc new file mode 100644 index 0000000000000..3c3509ecc7d0d --- /dev/null +++ b/shell/platform/embedder/vsync_waiter_embedder.cc @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" + +namespace shell { + +VsyncWaiterEmbedder::VsyncWaiterEmbedder(VsyncCallback vsync_callback, + blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), vsync_callback_(vsync_callback) { + FML_DCHECK(vsync_callback_); +} + +VsyncWaiterEmbedder::~VsyncWaiterEmbedder() = default; + +// |shell::VsyncWaiter| +void VsyncWaiterEmbedder::AwaitVSync() { + auto* weak_waiter = new std::weak_ptr(shared_from_this()); + vsync_callback_(reinterpret_cast(weak_waiter)); +} + +// static +bool VsyncWaiterEmbedder::OnEmbedderVsync(intptr_t baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time) { + if (baton == 0) { + return false; + } + + auto* weak_waiter = reinterpret_cast*>(baton); + auto strong_waiter = weak_waiter->lock(); + delete weak_waiter; + + if (!strong_waiter) { + return false; + } + + strong_waiter->FireCallback(frame_start_time, frame_target_time); + return true; +} + +} // namespace shell diff --git a/shell/platform/embedder/vsync_waiter_embedder.h b/shell/platform/embedder/vsync_waiter_embedder.h new file mode 100644 index 0000000000000..fc7700417e0d7 --- /dev/null +++ b/shell/platform/embedder/vsync_waiter_embedder.h @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_ +#define SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/common/vsync_waiter.h" + +namespace shell { + +class VsyncWaiterEmbedder final : public VsyncWaiter { + public: + using VsyncCallback = std::function; + + VsyncWaiterEmbedder(VsyncCallback callback, blink::TaskRunners task_runners); + + ~VsyncWaiterEmbedder() override; + + static bool OnEmbedderVsync(intptr_t baton, + fml::TimePoint frame_start_time, + fml::TimePoint frame_target_time); + + private: + const VsyncCallback vsync_callback_; + + // |shell::VsyncWaiter| + void AwaitVSync() override; + + FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterEmbedder); +}; + +} // namespace shell + +#endif // SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_ diff --git a/shell/platform/glfw/BUILD.gn b/shell/platform/glfw/BUILD.gn new file mode 100644 index 0000000000000..478d2e80e3255 --- /dev/null +++ b/shell/platform/glfw/BUILD.gn @@ -0,0 +1,79 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +_public_headers = [ "public/flutter_glfw.h" ] + +# Any files that are built by clients (client_wrapper code, library headers for +# implementations using this shared code, etc.) include the public headers +# assuming they are in the include path. This configuration should be added to +# any such code that is also built by GN to make the includes work. +config("relative_flutter_glfw_headers") { + include_dirs = [ "public" ] +} + +# The headers are a separate source set since the client wrapper is allowed +# to depend on the public headers, but none of the rest of the code. +source_set("flutter_glfw_headers") { + public = _public_headers + + public_deps = [ + "$flutter_root/shell/platform/common/cpp:common_cpp_library_headers", + ] + + configs += [ + "$flutter_root/shell/platform/common/cpp:desktop_library_implementation", + ] + + public_configs = [ + "$flutter_root/shell/platform/common/cpp:relative_flutter_library_headers", + ] +} + +source_set("flutter_glfw") { + sources = [ + "flutter_glfw.cc", + "key_event_handler.cc", + "key_event_handler.h", + "keyboard_hook_handler.h", + "text_input_plugin.cc", + "text_input_plugin.h", + ] + + defines = [ "USE_RAPID_JSON" ] + + configs += [ + "$flutter_root/shell/platform/common/cpp:desktop_library_implementation", + ] + + deps = [ + ":flutter_glfw_headers", + "$flutter_root/shell/platform/common/cpp:common_cpp", + "$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper", + "$flutter_root/shell/platform/embedder:embedder", + "$flutter_root/shell/platform/glfw/client_wrapper:client_wrapper_glfw", + "//build/secondary/third_party/glfw", + "//third_party/rapidjson", + ] + + if (is_linux) { + libs = [ "GL" ] + + configs += [ + "$flutter_root/shell/platform/linux/config:gtk3", + "$flutter_root/shell/platform/linux/config:x11", + ] + } +} + +copy("publish_headers_glfw") { + sources = _public_headers + outputs = [ + "$root_out_dir/{{source_file_part}}", + ] + + # The GLFW header assumes the presence of the common headers. + deps = [ + "$flutter_root/shell/platform/common/cpp:publish_headers", + ] +} diff --git a/shell/platform/glfw/client_wrapper/BUILD.gn b/shell/platform/glfw/client_wrapper/BUILD.gn new file mode 100644 index 0000000000000..d9e1b482dbaf6 --- /dev/null +++ b/shell/platform/glfw/client_wrapper/BUILD.gn @@ -0,0 +1,78 @@ +# Copyright 2013 The Flutter 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_root/shell/platform/common/cpp/client_wrapper/publish.gni") + +_wrapper_includes = [ "include/flutter/flutter_window_controller.h" ] + +_wrapper_sources = [ "flutter_window_controller.cc" ] + +# This code will be merged into .../common/cpp/client_wrapper for client use, +# so uses header paths that assume the merged state. Include the header +# header directory of the core wrapper files so these includes will work. +config("relative_core_wrapper_headers") { + include_dirs = [ + "$flutter_root/shell/platform/common/cpp/client_wrapper/include/flutter", + ] +} + +# GLFW client wrapper build for internal use by the shell implementation. +source_set("client_wrapper_glfw") { + sources = _wrapper_sources + public = _wrapper_includes + + deps = [ + "$flutter_root/shell/platform/common/cpp:common_cpp_library_headers", + "$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper", + "$flutter_root/shell/platform/glfw:flutter_glfw_headers", + ] + + configs += [ + "$flutter_root/shell/platform/common/cpp:desktop_library_implementation", + ] + + public_configs = [ + ":relative_core_wrapper_headers", + "$flutter_root/shell/platform/common/cpp:relative_flutter_library_headers", + "$flutter_root/shell/platform/glfw:relative_flutter_glfw_headers", + ] +} + +# Copies the GLFW client wrapper code to the output directory, merging it into +# the core wrapper. +publish_client_wrapper("publish_wrapper_glfw") { + public = _wrapper_includes + sources = _wrapper_sources +} + +source_set("client_wrapper_library_stubs_glfw") { + sources = [ + "testing/stub_flutter_glfw_api.cc", + "testing/stub_flutter_glfw_api.h", + ] + + public_deps = [ + "$flutter_root/shell/platform/glfw:flutter_glfw_headers", + ] +} + +executable("client_wrapper_glfw_unittests") { + testonly = true + + # TODO: Add more unit tests. + sources = [ + "flutter_window_controller_unittests.cc", + ] + + deps = [ + ":client_wrapper_glfw", + ":client_wrapper_library_stubs_glfw", + "$flutter_root/shell/platform/common/cpp/client_wrapper:client_wrapper_library_stubs", + "$flutter_root/testing", + + # TODO: Consider refactoring flutter_root/testing so that there's a testing + # target that doesn't require a Dart runtime to be linked in. + "//third_party/dart/runtime:libdart_jit", + ] +} diff --git a/shell/platform/glfw/client_wrapper/flutter_window_controller.cc b/shell/platform/glfw/client_wrapper/flutter_window_controller.cc new file mode 100644 index 0000000000000..c73d6e7078699 --- /dev/null +++ b/shell/platform/glfw/client_wrapper/flutter_window_controller.cc @@ -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 "include/flutter/flutter_window_controller.h" + +#include +#include + +namespace flutter { + +FlutterWindowController::FlutterWindowController( + const std::string& icu_data_path) + : icu_data_path_(icu_data_path) { + init_succeeded_ = FlutterDesktopInit(); +} + +FlutterWindowController::~FlutterWindowController() { + if (init_succeeded_) { + FlutterDesktopTerminate(); + } +} + +bool FlutterWindowController::CreateWindow( + int width, + int height, + const std::string& assets_path, + const std::vector& arguments) { + if (!init_succeeded_) { + std::cerr << "Could not create window; FlutterDesktopInit failed." + << std::endl; + return false; + } + + if (window_) { + std::cerr << "Only one Flutter window can exist at a time." << std::endl; + return false; + } + + std::vector engine_arguments; + std::transform( + arguments.begin(), arguments.end(), std::back_inserter(engine_arguments), + [](const std::string& arg) -> const char* { return arg.c_str(); }); + size_t arg_count = engine_arguments.size(); + + window_ = FlutterDesktopCreateWindow( + width, height, assets_path.c_str(), icu_data_path_.c_str(), + arg_count > 0 ? &engine_arguments[0] : nullptr, arg_count); + if (!window_) { + std::cerr << "Failed to create window." << std::endl; + return false; + } + return true; +} + +FlutterDesktopPluginRegistrarRef FlutterWindowController::GetRegistrarForPlugin( + const std::string& plugin_name) { + if (!window_) { + std::cerr << "Cannot get plugin registrar without a window; call " + "CreateWindow first." + << std::endl; + return nullptr; + } + return FlutterDesktopGetPluginRegistrar(window_, plugin_name.c_str()); +} + +void FlutterWindowController::SetHoverEnabled(bool enabled) { + FlutterDesktopSetHoverEnabled(window_, enabled); +} + +void FlutterWindowController::RunEventLoop() { + if (window_) { + FlutterDesktopRunWindowLoop(window_); + } +} + +} // namespace flutter diff --git a/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc b/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc new file mode 100644 index 0000000000000..d0e90fcb5056c --- /dev/null +++ b/shell/platform/glfw/client_wrapper/flutter_window_controller_unittests.cc @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h" + +#include +#include + +#include "flutter/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h" +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Stub implementation to validate calls to the API. +class TestGlfwApi : public testing::StubFlutterGlfwApi { + public: + // |flutter::testing::StubFlutterGlfwApi| + bool Init() override { + init_called_ = true; + return true; + } + + // |flutter::testing::StubFlutterGlfwApi| + void Terminate() override { terminate_called_ = true; } + + bool init_called() { return init_called_; } + + bool terminate_called() { return terminate_called_; } + + private: + bool init_called_ = false; + bool terminate_called_ = false; +}; + +} // namespace + +TEST(FlutterViewControllerTest, CreateDestroy) { + const std::string icu_data_path = "fake/path/to/icu"; + testing::ScopedStubFlutterGlfwApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + { + FlutterWindowController controller(icu_data_path); + EXPECT_EQ(test_api->init_called(), true); + EXPECT_EQ(test_api->terminate_called(), false); + } + EXPECT_EQ(test_api->init_called(), true); + EXPECT_EQ(test_api->terminate_called(), true); +} + +} // namespace flutter diff --git a/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h b/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h new file mode 100644 index 0000000000000..24496252c887c --- /dev/null +++ b/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ + +#include +#include + +#include + +#include "plugin_registrar.h" + +namespace flutter { + +// A controller for a window displaying Flutter content. +// +// This is the primary wrapper class for the desktop C API. +// If you use this class, you should not call any of the setup or teardown +// methods in the C API directly, as this class will do that internally. +// +// Note: This is an early implementation (using GLFW internally) which +// requires control of the application's event loop, and is thus useful +// primarily for building a simple one-window shell hosting a Flutter +// application. The final implementation and API will be very different. +class FlutterWindowController { + public: + // There must be only one instance of this class in an application at any + // given time, as Flutter does not support multiple engines in one process, + // or multiple views in one engine. + explicit FlutterWindowController(const std::string& icu_data_path); + + ~FlutterWindowController(); + + // Creates and displays a window for displaying Flutter content. + // + // The |assets_path| is the path to the flutter_assets folder for the Flutter + // application to be run. |icu_data_path| is the path to the icudtl.dat file + // for the version of Flutter you are using. + // + // The |arguments| are passed to the Flutter engine. See: + // https://github.com/flutter/engine/blob/master/shell/common/switches.h for + // for details. Not all arguments will apply to desktop. + // + // Only one Flutter window can exist at a time; see constructor comment. + bool CreateWindow(int width, + int height, + const std::string& assets_path, + const std::vector& arguments); + + // Returns the FlutterDesktopPluginRegistrarRef to register a plugin with the + // given name. + // + // The name must be unique across the application. + FlutterDesktopPluginRegistrarRef GetRegistrarForPlugin( + const std::string& plugin_name); + + // Enables or disables hover tracking. + // + // If hover is enabled, mouse movement will send hover events to the Flutter + // engine, rather than only tracking the mouse while the button is pressed. + // Defaults to off. + void SetHoverEnabled(bool enabled); + + // Loops on Flutter window events until the window closes. + void RunEventLoop(); + + private: + // The path to the ICU data file. Set at creation time since it is the same + // for any window created. + std::string icu_data_path_; + + // Whether or not FlutterDesktopInit succeeded at creation time. + bool init_succeeded_ = false; + + // The curent Flutter window, if any. + FlutterDesktopWindowRef window_ = nullptr; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ diff --git a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.cc new file mode 100644 index 0000000000000..f3a5e9a436469 --- /dev/null +++ b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.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 "flutter/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h" + +static flutter::testing::StubFlutterGlfwApi* s_stub_implementation; + +namespace flutter { +namespace testing { + +// static +void StubFlutterGlfwApi::SetTestStub(StubFlutterGlfwApi* stub) { + s_stub_implementation = stub; +} + +// static +StubFlutterGlfwApi* StubFlutterGlfwApi::GetTestStub() { + return s_stub_implementation; +} + +ScopedStubFlutterGlfwApi::ScopedStubFlutterGlfwApi( + std::unique_ptr stub) + : stub_(std::move(stub)) { + previous_stub_ = StubFlutterGlfwApi::GetTestStub(); + StubFlutterGlfwApi::SetTestStub(stub_.get()); +} + +ScopedStubFlutterGlfwApi::~ScopedStubFlutterGlfwApi() { + StubFlutterGlfwApi::SetTestStub(previous_stub_); +} + +} // namespace testing +} // namespace flutter + +// Forwarding dummy implementations of the C API. + +bool FlutterDesktopInit() { + if (s_stub_implementation) { + s_stub_implementation->Init(); + } + return true; +} + +void FlutterDesktopTerminate() { + if (s_stub_implementation) { + s_stub_implementation->Terminate(); + } +} + +FlutterDesktopWindowRef FlutterDesktopCreateWindow(int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + if (s_stub_implementation) { + return s_stub_implementation->CreateWindow(initial_width, initial_height, + assets_path, icu_data_path, + arguments, argument_count); + } + return nullptr; +} + +void FlutterDesktopSetHoverEnabled(FlutterDesktopWindowRef flutter_window, + bool enabled) { + if (s_stub_implementation) { + s_stub_implementation->SetHoverEnabled(enabled); + } +} + +void FlutterDesktopRunWindowLoop(FlutterDesktopWindowRef flutter_window) { + if (s_stub_implementation) { + s_stub_implementation->RunWindowLoop(); + } +} + +FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + if (s_stub_implementation) { + return s_stub_implementation->RunEngine(assets_path, icu_data_path, + arguments, argument_count); + } + return nullptr; +} + +bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) { + if (s_stub_implementation) { + return s_stub_implementation->ShutDownEngine(); + } + return true; +} + +FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar( + FlutterDesktopWindowRef flutter_window, + const char* plugin_name) { + // The stub ignores this, so just return an arbitrary non-zero value. + return reinterpret_cast(1); +} diff --git a/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.h new file mode 100644 index 0000000000000..70f993646d3c0 --- /dev/null +++ b/shell/platform/glfw/client_wrapper/testing/stub_flutter_glfw_api.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_SHELL_PLATFORM_GLFW_WRAPPER_TESTING_STUB_FLUTTER_GLFW_API_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_WRAPPER_TESTING_STUB_FLUTTER_GLFW_API_H_ + +#include + +#include "flutter/shell/platform/glfw/public/flutter_glfw.h" + +namespace flutter { +namespace testing { + +// Base class for a object that provides test implementations of the APIs in +// the headers in platform/glfw/public/. + +// Linking this class into a test binary will provide dummy forwarding +// implementantions of that C API, so that the wrapper can be tested separately +// from the actual library. +class StubFlutterGlfwApi { + public: + // Sets |stub| as the instance to which calls to the Flutter library C APIs + // will be forwarded. + static void SetTestStub(StubFlutterGlfwApi* stub); + + // Returns the current stub, as last set by SetTestFluttterStub. + static StubFlutterGlfwApi* GetTestStub(); + + virtual ~StubFlutterGlfwApi() {} + + // Called for FlutterDesktopInit. + virtual bool Init() { return true; } + + // Called for FlutterDesktopTerminate. + virtual void Terminate() {} + + // Called for FlutterDesktopCreateWindow. + virtual FlutterDesktopWindowRef CreateWindow(int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + return nullptr; + } + + // Called for FlutterDesktopSetHoverEnabled + virtual void SetHoverEnabled(bool enabled) {} + + // Called for FlutterDesktopRunWindowLoop. + virtual void RunWindowLoop() {} + + // Called for FlutterDesktopRunEngine. + virtual FlutterDesktopEngineRef RunEngine(const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + return nullptr; + } + + // Called for FlutterDesktopShutDownEngine. + virtual bool ShutDownEngine() { return true; } +}; + +// A test helper that owns a stub implementation, making it the test stub for +// the lifetime of the object, then restoring the previous value. +class ScopedStubFlutterGlfwApi { + public: + // Calls SetTestFlutterStub with |stub|. + ScopedStubFlutterGlfwApi(std::unique_ptr stub); + + // Restores the previous test stub. + ~ScopedStubFlutterGlfwApi(); + + StubFlutterGlfwApi* stub() { return stub_.get(); } + + private: + std::unique_ptr stub_; + // The previous stub. + StubFlutterGlfwApi* previous_stub_; +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_TESTING_STUB_FLUTTER_GLFW_API_H_ diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc new file mode 100644 index 0000000000000..ab03cac69f9c5 --- /dev/null +++ b/shell/platform/glfw/flutter_glfw.cc @@ -0,0 +1,590 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/glfw/public/flutter_glfw.h" + +#include +#include +#include +#include +#include + +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h" +#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/glfw/key_event_handler.h" +#include "flutter/shell/platform/glfw/keyboard_hook_handler.h" +#include "flutter/shell/platform/glfw/text_input_plugin.h" + +#ifdef __linux__ +// For plugin-compatible event handling (e.g., modal windows). +#include +#include +#endif + +// GLFW_TRUE & GLFW_FALSE are introduced since libglfw-3.3, +// add definitions here to compile under the old versions. +#ifndef GLFW_TRUE +#define GLFW_TRUE 1 +#endif +#ifndef GLFW_FALSE +#define GLFW_FALSE 0 +#endif + +static_assert(FLUTTER_ENGINE_VERSION == 1, ""); + +static constexpr double kDpPerInch = 160.0; + +// Struct for storing state within an instance of the GLFW Window. +struct FlutterDesktopWindowState { + // The GLFW window that owns this state object. + GLFWwindow* window; + + // The handle to the Flutter engine instance. + FlutterEngine engine; + + // The plugin registrar handle given to API clients. + std::unique_ptr plugin_registrar; + + // Message dispatch manager for messages from the Flutter engine. + std::unique_ptr message_dispatcher; + + // The plugin registrar managing internal plugins. + std::unique_ptr internal_plugin_registrar; + + // Handlers for keyboard events from GLFW. + std::vector> + keyboard_hook_handlers; + + // Whether or not to track mouse movements to send kHover events. + bool hover_tracking_enabled = false; + + // Whether or not the pointer has been added (or if tracking is enabled, has + // been added since it was last removed). + bool pointer_currently_added = false; + + // The screen coordinates per inch on the primary monitor. Defaults to a sane + // value based on pixel_ratio 1.0. + double monitor_screen_coordinates_per_inch = kDpPerInch; + // The ratio of pixels per screen coordinate for the window. + double window_pixels_per_screen_coordinate = 1.0; +}; + +// Struct for storing state of a Flutter engine instance. +struct FlutterDesktopEngineState { + // The handle to the Flutter engine instance. + FlutterEngine engine; +}; + +// State associated with the plugin registrar. +struct FlutterDesktopPluginRegistrar { + // The plugin messenger handle given to API clients. + std::unique_ptr messenger; +}; + +// State associated with the messenger used to communicate with the engine. +struct FlutterDesktopMessenger { + // The Flutter engine this messenger sends outgoing messages to. + FlutterEngine engine; + + // The message dispatcher for handling incoming messages. + shell::IncomingMessageDispatcher* dispatcher; +}; + +static constexpr char kDefaultWindowTitle[] = "Flutter"; + +// Retrieves state bag for the window in question from the GLFWWindow. +static FlutterDesktopWindowState* GetSavedWindowState(GLFWwindow* window) { + return reinterpret_cast( + glfwGetWindowUserPointer(window)); +} + +// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage. +static FlutterDesktopMessage ConvertToDesktopMessage( + const FlutterPlatformMessage& engine_message) { + FlutterDesktopMessage message = {}; + message.struct_size = sizeof(message); + message.channel = engine_message.channel; + message.message = engine_message.message; + message.message_size = engine_message.message_size; + message.response_handle = engine_message.response_handle; + return message; +} + +// Returns the number of screen coordinates per inch for the main monitor. +// If the information is unavailable, returns a default value that assumes +// that a screen coordinate is one dp. +static double GetScreenCoordinatesPerInch() { + auto* primary_monitor = glfwGetPrimaryMonitor(); + auto* primary_monitor_mode = glfwGetVideoMode(primary_monitor); + int primary_monitor_width_mm; + glfwGetMonitorPhysicalSize(primary_monitor, &primary_monitor_width_mm, + nullptr); + if (primary_monitor_width_mm == 0) { + return kDpPerInch; + } + return primary_monitor_mode->width / (primary_monitor_width_mm / 25.4); +} + +// When GLFW calls back to the window with a framebuffer size change, notify +// FlutterEngine about the new window metrics. +// The Flutter pixel_ratio is defined as DPI/dp. +static void GLFWFramebufferSizeCallback(GLFWwindow* window, + int width_px, + int height_px) { + int width; + glfwGetWindowSize(window, &width, nullptr); + + auto state = GetSavedWindowState(window); + state->window_pixels_per_screen_coordinate = width_px / width; + + double dpi = state->window_pixels_per_screen_coordinate * + state->monitor_screen_coordinates_per_inch; + // Limit the ratio to 1 to avoid rendering a smaller UI in standard resolution + // monitors. + double pixel_ratio = std::max(dpi / kDpPerInch, 1.0); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = width_px; + event.height = height_px; + event.pixel_ratio = pixel_ratio; + FlutterEngineSendWindowMetricsEvent(state->engine, &event); +} + +// Sends a pointer event to the Flutter engine with the given phase. +static void SendPointerEventWithPhase(GLFWwindow* window, + FlutterPointerPhase phase, + double x, + double y) { + auto state = GetSavedWindowState(window); + // If sending anything other than an add, and the pointer isn't already added, + // synthesize an add to satisfy Flutter's expectations about events. + if (!state->pointer_currently_added && phase != FlutterPointerPhase::kAdd) { + SendPointerEventWithPhase(window, FlutterPointerPhase::kAdd, x, y); + } + // Don't double-add (e.g., if events are delivered out of order, so an add has + // already been synthesized). + if (state->pointer_currently_added && phase == FlutterPointerPhase::kAdd) { + return; + } + + FlutterPointerEvent event = {}; + event.struct_size = sizeof(event); + event.phase = phase; + event.x = x * state->window_pixels_per_screen_coordinate; + event.y = y * state->window_pixels_per_screen_coordinate; + event.timestamp = + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + FlutterEngineSendPointerEvent(state->engine, &event, 1); + + if (phase == FlutterPointerPhase::kAdd) { + state->pointer_currently_added = true; + } else if (phase == FlutterPointerPhase::kRemove) { + state->pointer_currently_added = false; + } +} + +// Reports the mouse entering or leaving the Flutter view. +static void GLFWCursorEnterCallback(GLFWwindow* window, int entered) { + double x, y; + glfwGetCursorPos(window, &x, &y); + FlutterPointerPhase phase = + entered ? FlutterPointerPhase::kAdd : FlutterPointerPhase::kRemove; + SendPointerEventWithPhase(window, phase, x, y); +} + +// Reports mouse movement to the Flutter engine. +static void GLFWCursorPositionCallback(GLFWwindow* window, double x, double y) { + bool button_down = + glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS; + FlutterPointerPhase phase = + button_down ? FlutterPointerPhase::kMove : FlutterPointerPhase::kHover; + SendPointerEventWithPhase(window, phase, x, y); +} + +// Reports mouse button press to the Flutter engine. +static void GLFWMouseButtonCallback(GLFWwindow* window, + int key, + int action, + int mods) { + // Flutter currently doesn't understand other buttons, so ignore anything + // other than left. + if (key != GLFW_MOUSE_BUTTON_LEFT) { + return; + } + + double x, y; + glfwGetCursorPos(window, &x, &y); + FlutterPointerPhase phase = (action == GLFW_PRESS) + ? FlutterPointerPhase::kDown + : FlutterPointerPhase::kUp; + SendPointerEventWithPhase(window, phase, x, y); + + // If mouse tracking isn't already enabled, turn it on for the duration of + // the drag to generate kMove events. + bool hover_enabled = GetSavedWindowState(window)->hover_tracking_enabled; + if (!hover_enabled) { + glfwSetCursorPosCallback( + window, (action == GLFW_PRESS) ? GLFWCursorPositionCallback : nullptr); + } + // Disable enter/exit events while the mouse button is down; GLFW will send + // an exit event when the mouse button is released, and the pointer should + // stay valid until then. + if (hover_enabled) { + glfwSetCursorEnterCallback( + window, (action == GLFW_PRESS) ? nullptr : GLFWCursorEnterCallback); + } +} + +// Passes character input events to registered handlers. +static void GLFWCharCallback(GLFWwindow* window, unsigned int code_point) { + for (const auto& handler : + GetSavedWindowState(window)->keyboard_hook_handlers) { + handler->CharHook(window, code_point); + } +} + +// Passes raw key events to registered handlers. +static void GLFWKeyCallback(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) { + for (const auto& handler : + GetSavedWindowState(window)->keyboard_hook_handlers) { + handler->KeyboardHook(window, key, scancode, action, mods); + } +} + +// Enables/disables the callbacks related to mouse tracking. +static void SetHoverCallbacksEnabled(GLFWwindow* window, bool enabled) { + glfwSetCursorEnterCallback(window, + enabled ? GLFWCursorEnterCallback : nullptr); + glfwSetCursorPosCallback(window, + enabled ? GLFWCursorPositionCallback : nullptr); +} + +// Flushes event queue and then assigns default window callbacks. +static void GLFWAssignEventCallbacks(GLFWwindow* window) { + glfwPollEvents(); + glfwSetKeyCallback(window, GLFWKeyCallback); + glfwSetCharCallback(window, GLFWCharCallback); + glfwSetMouseButtonCallback(window, GLFWMouseButtonCallback); + if (GetSavedWindowState(window)->hover_tracking_enabled) { + SetHoverCallbacksEnabled(window, true); + } +} + +// Clears default window events. +static void GLFWClearEventCallbacks(GLFWwindow* window) { + glfwSetKeyCallback(window, nullptr); + glfwSetCharCallback(window, nullptr); + glfwSetMouseButtonCallback(window, nullptr); + SetHoverCallbacksEnabled(window, false); +} + +// The Flutter Engine calls out to this function when new platform messages are +// available +static void GLFWOnFlutterPlatformMessage( + const FlutterPlatformMessage* engine_message, + void* user_data) { + if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) { + std::cerr << "Invalid message size received. Expected: " + << sizeof(FlutterPlatformMessage) << " but received " + << engine_message->struct_size << std::endl; + return; + } + + GLFWwindow* window = reinterpret_cast(user_data); + auto state = GetSavedWindowState(window); + + auto message = ConvertToDesktopMessage(*engine_message); + state->message_dispatcher->HandleMessage( + message, [window] { GLFWClearEventCallbacks(window); }, + [window] { GLFWAssignEventCallbacks(window); }); +} + +static bool GLFWMakeContextCurrent(void* user_data) { + GLFWwindow* window = reinterpret_cast(user_data); + glfwMakeContextCurrent(window); + return true; +} + +static bool GLFWClearContext(void* user_data) { + glfwMakeContextCurrent(nullptr); + return true; +} + +static bool GLFWPresent(void* user_data) { + GLFWwindow* window = reinterpret_cast(user_data); + glfwSwapBuffers(window); + return true; +} + +static uint32_t GLFWGetActiveFbo(void* user_data) { + return 0; +} + +// Clears the GLFW window to Material Blue-Grey. +// +// This function is primarily to fix an issue when the Flutter Engine is +// spinning up, wherein artifacts of existing windows are rendered onto the +// canvas for a few moments. +// +// This function isn't necessary, but makes starting the window much easier on +// the eyes. +static void GLFWClearCanvas(GLFWwindow* window) { + glfwMakeContextCurrent(window); + // This color is Material Blue Grey. + glClearColor(236.0f / 255.0f, 239.0f / 255.0f, 241.0f / 255.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glFlush(); + glfwSwapBuffers(window); + glfwMakeContextCurrent(nullptr); +} + +// Resolves the address of the specified OpenGL or OpenGL ES +// core or extension function, if it is supported by the current context. +static void* GLFWProcResolver(void* user_data, const char* name) { + return reinterpret_cast(glfwGetProcAddress(name)); +} + +static void GLFWErrorCallback(int error_code, const char* description) { + std::cerr << "GLFW error " << error_code << ": " << description << std::endl; +} + +// Spins up an instance of the Flutter Engine. +// +// This function launches the Flutter Engine in a background thread, supplying +// the necessary callbacks for rendering within a GLFWwindow (if one is +// provided). +// +// Returns a caller-owned pointer to the engine. +static FlutterEngine RunFlutterEngine(GLFWwindow* window, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t arguments_count) { + // FlutterProjectArgs is expecting a full argv, so when processing it for + // flags the first item is treated as the executable and ignored. Add a dummy + // value so that all provided arguments are used. + std::vector argv = {"placeholder"}; + if (arguments_count > 0) { + argv.insert(argv.end(), &arguments[0], &arguments[arguments_count]); + } + + FlutterRendererConfig config = {}; + if (window == nullptr) { + config.type = kOpenGL; + config.open_gl.struct_size = sizeof(config.open_gl); + config.open_gl.make_current = [](void* data) -> bool { return false; }; + config.open_gl.clear_current = [](void* data) -> bool { return false; }; + config.open_gl.present = [](void* data) -> bool { return false; }; + config.open_gl.fbo_callback = [](void* data) -> uint32_t { return 0; }; + } else { + // Provide the necessary callbacks for rendering within a GLFWwindow. + config.type = kOpenGL; + config.open_gl.struct_size = sizeof(config.open_gl); + config.open_gl.make_current = GLFWMakeContextCurrent; + config.open_gl.clear_current = GLFWClearContext; + config.open_gl.present = GLFWPresent; + config.open_gl.fbo_callback = GLFWGetActiveFbo; + config.open_gl.gl_proc_resolver = GLFWProcResolver; + } + FlutterProjectArgs args = {}; + args.struct_size = sizeof(FlutterProjectArgs); + args.assets_path = assets_path; + args.icu_data_path = icu_data_path; + args.command_line_argc = static_cast(argv.size()); + args.command_line_argv = &argv[0]; + args.platform_message_callback = GLFWOnFlutterPlatformMessage; + FlutterEngine engine = nullptr; + auto result = + FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, window, &engine); + if (result != kSuccess || engine == nullptr) { + std::cerr << "Failed to start Flutter engine: error " << result + << std::endl; + return nullptr; + } + return engine; +} + +bool FlutterDesktopInit() { + // Before making any GLFW calls, set up a logging error handler. + glfwSetErrorCallback(GLFWErrorCallback); + return glfwInit(); +} + +void FlutterDesktopTerminate() { + glfwTerminate(); +} + +FlutterDesktopWindowRef FlutterDesktopCreateWindow(int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { +#ifdef __linux__ + gtk_init(0, nullptr); +#endif + // Create the window. + auto window = glfwCreateWindow(initial_width, initial_height, + kDefaultWindowTitle, NULL, NULL); + if (window == nullptr) { + return nullptr; + } + GLFWClearCanvas(window); + + // Start the engine. + auto engine = RunFlutterEngine(window, assets_path, icu_data_path, arguments, + argument_count); + if (engine == nullptr) { + glfwDestroyWindow(window); + return nullptr; + } + + // Create a state object attached to the window. + FlutterDesktopWindowState* state = new FlutterDesktopWindowState(); + state->window = window; + glfwSetWindowUserPointer(window, state); + state->engine = engine; + + // TODO: Restructure the internals to follow the structure of the C++ API, so + // that this isn't a tangle of references. + auto messenger = std::make_unique(); + state->message_dispatcher = + std::make_unique(messenger.get()); + messenger->engine = engine; + messenger->dispatcher = state->message_dispatcher.get(); + + state->plugin_registrar = std::make_unique(); + state->plugin_registrar->messenger = std::move(messenger); + + state->internal_plugin_registrar = + std::make_unique(state->plugin_registrar.get()); + + // Set up the keyboard handlers. + auto internal_plugin_messenger = + state->internal_plugin_registrar->messenger(); + state->keyboard_hook_handlers.push_back( + std::make_unique(internal_plugin_messenger)); + state->keyboard_hook_handlers.push_back( + std::make_unique(internal_plugin_messenger)); + + // Trigger an initial size callback to send size information to Flutter. + state->monitor_screen_coordinates_per_inch = GetScreenCoordinatesPerInch(); + int width_px, height_px; + glfwGetFramebufferSize(window, &width_px, &height_px); + GLFWFramebufferSizeCallback(window, width_px, height_px); + + // Set up GLFW callbacks for the window. + glfwSetFramebufferSizeCallback(window, GLFWFramebufferSizeCallback); + GLFWAssignEventCallbacks(window); + + return state; +} + +void FlutterDesktopSetHoverEnabled(FlutterDesktopWindowRef flutter_window, + bool enabled) { + flutter_window->hover_tracking_enabled = enabled; + SetHoverCallbacksEnabled(flutter_window->window, enabled); +} + +void FlutterDesktopRunWindowLoop(FlutterDesktopWindowRef flutter_window) { + GLFWwindow* window = flutter_window->window; +#ifdef __linux__ + // Necessary for GTK thread safety. + XInitThreads(); +#endif + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); +#ifdef __linux__ + if (gtk_events_pending()) { + gtk_main_iteration(); + } +#endif + // TODO(awdavies): This will be deprecated soon. + __FlutterEngineFlushPendingTasksNow(); + } + FlutterEngineShutdown(flutter_window->engine); + delete flutter_window; + glfwDestroyWindow(window); +} + +FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + auto engine = RunFlutterEngine(nullptr, assets_path, icu_data_path, arguments, + argument_count); + if (engine == nullptr) { + return nullptr; + } + auto engine_state = new FlutterDesktopEngineState(); + engine_state->engine = engine; + return engine_state; +} + +bool FlutterDesktopShutDownEngine(FlutterDesktopEngineRef engine_ref) { + std::cout << "Shutting down flutter engine process." << std::endl; + auto result = FlutterEngineShutdown(engine_ref->engine); + delete engine_ref; + return (result == kSuccess); +} + +FlutterDesktopPluginRegistrarRef FlutterDesktopGetPluginRegistrar( + FlutterDesktopWindowRef flutter_window, + const char* plugin_name) { + // Currently, one registrar acts as the registrar for all plugins, so the + // name is ignored. It is part of the API to reduce churn in the future when + // aligning more closely with the Flutter registrar system. + return flutter_window->plugin_registrar.get(); +} + +void FlutterDesktopRegistrarEnableInputBlocking( + FlutterDesktopPluginRegistrarRef registrar, + const char* channel) { + registrar->messenger->dispatcher->EnableInputBlockingForChannel(channel); +} + +FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( + FlutterDesktopPluginRegistrarRef registrar) { + return registrar->messenger.get(); +} + +void FlutterDesktopMessengerSend(FlutterDesktopMessengerRef messenger, + const char* channel, + const uint8_t* message, + const size_t message_size) { + FlutterPlatformMessage platform_message = { + sizeof(FlutterPlatformMessage), + channel, + message, + message_size, + }; + + FlutterEngineSendPlatformMessage(messenger->engine, &platform_message); +} + +void FlutterDesktopMessengerSendResponse( + FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length) { + FlutterEngineSendPlatformMessageResponse(messenger->engine, handle, data, + data_length); +} + +void FlutterDesktopMessengerSetCallback(FlutterDesktopMessengerRef messenger, + const char* channel, + FlutterDesktopMessageCallback callback, + void* user_data) { + messenger->dispatcher->SetMessageCallback(channel, callback, user_data); +} diff --git a/shell/platform/glfw/key_event_handler.cc b/shell/platform/glfw/key_event_handler.cc new file mode 100644 index 0000000000000..7e370d0cdc76e --- /dev/null +++ b/shell/platform/glfw/key_event_handler.cc @@ -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. + +#include "flutter/shell/platform/glfw/key_event_handler.h" + +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h" + +static constexpr char kChannelName[] = "flutter/keyevent"; + +static constexpr char kKeyCodeKey[] = "keyCode"; +static constexpr char kKeyMapKey[] = "keymap"; +static constexpr char kTypeKey[] = "type"; + +static constexpr char kAndroidKeyMap[] = "android"; +static constexpr char kKeyUp[] = "keyup"; +static constexpr char kKeyDown[] = "keydown"; + +namespace shell { + +KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger) + : channel_( + std::make_unique>( + messenger, + kChannelName, + &flutter::JsonMessageCodec::GetInstance())) {} + +KeyEventHandler::~KeyEventHandler() = default; + +void KeyEventHandler::CharHook(GLFWwindow* window, unsigned int code_point) {} + +void KeyEventHandler::KeyboardHook(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) { + // TODO: Translate to a cross-platform key code system rather than passing + // the native key code. + rapidjson::Document event(rapidjson::kObjectType); + auto& allocator = event.GetAllocator(); + event.AddMember(kKeyCodeKey, key, allocator); + event.AddMember(kKeyMapKey, kAndroidKeyMap, allocator); + + switch (action) { + case GLFW_PRESS: + event.AddMember(kTypeKey, kKeyDown, allocator); + break; + case GLFW_RELEASE: + event.AddMember(kTypeKey, kKeyUp, allocator); + break; + default: + std::cerr << "Unknown key event action: " << action << std::endl; + return; + } + channel_->Send(event); +} + +} // namespace shell \ No newline at end of file diff --git a/shell/platform/glfw/key_event_handler.h b/shell/platform/glfw/key_event_handler.h new file mode 100644 index 0000000000000..171a17b6208cc --- /dev/null +++ b/shell/platform/glfw/key_event_handler.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_KEY_EVENT_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_KEY_EVENT_HANDLER_H_ + +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/glfw/keyboard_hook_handler.h" +#include "flutter/shell/platform/glfw/public/flutter_glfw.h" +#include "rapidjson/document.h" + +namespace shell { + +// Implements a KeyboardHookHandler +// +// Handles key events and forwards them to the Flutter engine. +class KeyEventHandler : public KeyboardHookHandler { + public: + explicit KeyEventHandler(flutter::BinaryMessenger* messenger); + + virtual ~KeyEventHandler(); + + // |shell::KeyboardHookHandler| + void KeyboardHook(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) override; + + // |shell::KeyboardHookHandler| + void CharHook(GLFWwindow* window, unsigned int code_point) override; + + private: + // The Flutter system channel for key event messages. + std::unique_ptr> channel_; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_KEY_EVENT_HANDLER_H_ diff --git a/shell/platform/glfw/keyboard_hook_handler.h b/shell/platform/glfw/keyboard_hook_handler.h new file mode 100644 index 0000000000000..5415105161e14 --- /dev/null +++ b/shell/platform/glfw/keyboard_hook_handler.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_SHELL_PLATFORM_GLFW_KEYBOARD_HOOK_HANDLER_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_KEYBOARD_HOOK_HANDLER_H_ + +#include + +#include "flutter/shell/platform/glfw/public/flutter_glfw.h" + +namespace shell { + +// Abstract class for handling keyboard input events. +class KeyboardHookHandler { + public: + virtual ~KeyboardHookHandler() = default; + + // A function for hooking into keyboard input. + virtual void KeyboardHook(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) = 0; + + // A function for hooking into unicode code point input. + virtual void CharHook(GLFWwindow* window, unsigned int code_point) = 0; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_KEYBOARD_HOOK_HANDLER_H_ diff --git a/shell/platform/glfw/public/flutter_glfw.h b/shell/platform/glfw/public/flutter_glfw.h new file mode 100644 index 0000000000000..c969581e161df --- /dev/null +++ b/shell/platform/glfw/public/flutter_glfw.h @@ -0,0 +1,109 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ + +#include +#include + +#include "flutter_export.h" +#include "flutter_messenger.h" +#include "flutter_plugin_registrar.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a Flutter window. +typedef struct FlutterDesktopWindowState* FlutterDesktopWindowRef; + +// Opaque reference to a Flutter engine instance. +typedef struct FlutterDesktopEngineState* FlutterDesktopEngineRef; + +// Sets up the library's graphic context. Must be called before any other +// methods. +// +// Note: Internally, this library uses GLFW, which does not support multiple +// copies within the same process. Internally this calls glfwInit, which will +// fail if you have called glfwInit elsewhere in the process. +FLUTTER_EXPORT bool FlutterDesktopInit(); + +// Tears down library state. Must be called before the process terminates. +FLUTTER_EXPORT void FlutterDesktopTerminate(); + +// Creates a Window running a Flutter Application. +// +// FlutterDesktopInit() must be called prior to this function. +// +// The |assets_path| is the path to the flutter_assets folder for the Flutter +// application to be run. |icu_data_path| is the path to the icudtl.dat file +// for the version of Flutter you are using. +// +// The |arguments| are passed to the Flutter engine. See: +// https://github.com/flutter/engine/blob/master/shell/common/switches.h for +// for details. Not all arguments will apply to desktop. +// +// Returns a null pointer in the event of an error. Otherwise, the pointer is +// valid until FlutterDesktopRunWindowLoop has been called and returned. +// Note that calling FlutterDesktopCreateWindow without later calling +// FlutterDesktopRunWindowLoop on the returned reference is a memory leak. +FLUTTER_EXPORT FlutterDesktopWindowRef +FlutterDesktopCreateWindow(int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count); + +// Enables or disables hover tracking. +// +// If hover is enabled, mouse movement will send hover events to the Flutter +// engine, rather than only tracking the mouse while the button is pressed. +// Defaults to off. +FLUTTER_EXPORT void FlutterDesktopSetHoverEnabled( + FlutterDesktopWindowRef flutter_window, + bool enabled); + +// Loops on Flutter window events until the window is closed. +// +// Once this function returns, FlutterDesktopWindowRef is no longer valid, and +// must not be used again. +FLUTTER_EXPORT void FlutterDesktopRunWindowLoop( + FlutterDesktopWindowRef flutter_window); + +// Runs an instance of a headless Flutter engine. +// +// The |assets_path| is the path to the flutter_assets folder for the Flutter +// application to be run. |icu_data_path| is the path to the icudtl.dat file +// for the version of Flutter you are using. +// +// The |arguments| are passed to the Flutter engine. See: +// https://github.com/flutter/engine/blob/master/shell/common/switches.h for +// for details. Not all arguments will apply to desktop. +// +// Returns a null pointer in the event of an error. +FLUTTER_EXPORT FlutterDesktopEngineRef +FlutterDesktopRunEngine(const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count); + +// Shuts down the given engine instance. Returns true if the shutdown was +// successful. |engine_ref| is no longer valid after this call. +FLUTTER_EXPORT bool FlutterDesktopShutDownEngine( + FlutterDesktopEngineRef engine_ref); + +// Returns the plugin registrar handle for the plugin with the given name. +// +// The name must be unique across the application. +FLUTTER_EXPORT FlutterDesktopPluginRegistrarRef +FlutterDesktopGetPluginRegistrar(FlutterDesktopWindowRef flutter_window, + const char* plugin_name); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ diff --git a/shell/platform/glfw/text_input_plugin.cc b/shell/platform/glfw/text_input_plugin.cc new file mode 100644 index 0000000000000..76dbf8d118798 --- /dev/null +++ b/shell/platform/glfw/text_input_plugin.cc @@ -0,0 +1,210 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/glfw/text_input_plugin.h" + +#include +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h" + +static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState"; +static constexpr char kClearClientMethod[] = "TextInput.clearClient"; +static constexpr char kSetClientMethod[] = "TextInput.setClient"; +static constexpr char kShowMethod[] = "TextInput.show"; +static constexpr char kHideMethod[] = "TextInput.hide"; + +static constexpr char kMultilineInputType[] = "TextInputType.multiline"; + +static constexpr char kUpdateEditingStateMethod[] = + "TextInputClient.updateEditingState"; +static constexpr char kPerformActionMethod[] = "TextInputClient.performAction"; + +static constexpr char kSelectionBaseKey[] = "selectionBase"; +static constexpr char kSelectionExtentKey[] = "selectionExtent"; + +static constexpr char kTextKey[] = "text"; + +static constexpr char kChannelName[] = "flutter/textinput"; + +static constexpr char kBadArgumentError[] = "Bad Arguments"; +static constexpr char kInternalConsistencyError[] = + "Internal Consistency Error"; + +static constexpr uint32_t kInputModelLimit = 256; + +namespace shell { + +void TextInputPlugin::CharHook(GLFWwindow* window, unsigned int code_point) { + if (active_model_ == nullptr) { + return; + } + // TODO(awdavies): Actually handle potential unicode characters. Probably + // requires some ICU data or something. + active_model_->AddCharacter(static_cast(code_point)); + SendStateUpdate(*active_model_); +} + +void TextInputPlugin::KeyboardHook(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) { + if (active_model_ == nullptr) { + return; + } + if (action == GLFW_PRESS || action == GLFW_REPEAT) { + switch (key) { + case GLFW_KEY_LEFT: + if (active_model_->MoveCursorBack()) { + SendStateUpdate(*active_model_); + } + break; + case GLFW_KEY_RIGHT: + if (active_model_->MoveCursorForward()) { + SendStateUpdate(*active_model_); + } + break; + case GLFW_KEY_END: + active_model_->MoveCursorToEnd(); + SendStateUpdate(*active_model_); + break; + case GLFW_KEY_HOME: + active_model_->MoveCursorToBeginning(); + SendStateUpdate(*active_model_); + break; + case GLFW_KEY_BACKSPACE: + if (active_model_->Backspace()) { + SendStateUpdate(*active_model_); + } + break; + case GLFW_KEY_DELETE: + if (active_model_->Delete()) { + SendStateUpdate(*active_model_); + } + break; + case GLFW_KEY_ENTER: + EnterPressed(active_model_); + default: + break; + } + } +} + +TextInputPlugin::TextInputPlugin(flutter::BinaryMessenger* messenger) + : channel_(std::make_unique>( + messenger, + kChannelName, + &flutter::JsonMethodCodec::GetInstance())), + active_model_(nullptr) { + channel_->SetMethodCallHandler( + [this]( + const flutter::MethodCall& call, + std::unique_ptr> result) { + HandleMethodCall(call, std::move(result)); + }); +} + +TextInputPlugin::~TextInputPlugin() = default; + +void TextInputPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + const std::string& method = method_call.method_name(); + + if (method.compare(kShowMethod) == 0 || method.compare(kHideMethod) == 0) { + // These methods are no-ops. + } else if (method.compare(kClearClientMethod) == 0) { + active_model_ = nullptr; + } else { + // Every following method requires args. + if (!method_call.arguments() || method_call.arguments()->IsNull()) { + result->Error(kBadArgumentError, "Method invoked without args"); + return; + } + const rapidjson::Document& args = *method_call.arguments(); + + if (method.compare(kSetClientMethod) == 0) { + // TODO(awdavies): There's quite a wealth of arguments supplied with this + // method, and they should be inspected/used. + const rapidjson::Value& client_id_json = args[0]; + const rapidjson::Value& client_config = args[1]; + if (client_id_json.IsNull()) { + result->Error(kBadArgumentError, "Could not set client, ID is null."); + return; + } + if (client_config.IsNull()) { + result->Error(kBadArgumentError, + "Could not set client, missing arguments."); + } + int client_id = client_id_json.GetInt(); + if (input_models_.find(client_id) == input_models_.end()) { + // Skips out on adding a new input model once over the limit. + if (input_models_.size() > kInputModelLimit) { + result->Error( + kInternalConsistencyError, + "Input models over limit. Aborting creation of new text model."); + return; + } + input_models_.insert(std::make_pair( + client_id, + std::make_unique(client_id, client_config))); + } + active_model_ = input_models_[client_id].get(); + } else if (method.compare(kSetEditingStateMethod) == 0) { + if (active_model_ == nullptr) { + result->Error( + kInternalConsistencyError, + "Set editing state has been invoked, but no client is set."); + return; + } + auto text = args.FindMember(kTextKey); + if (text == args.MemberEnd() || text->value.IsNull()) { + result->Error(kBadArgumentError, + "Set editing state has been invoked, but without text."); + return; + } + auto selection_base = args.FindMember(kSelectionBaseKey); + auto selection_extent = args.FindMember(kSelectionExtentKey); + if (selection_base == args.MemberEnd() || + selection_base->value.IsNull() || + selection_extent == args.MemberEnd() || + selection_extent->value.IsNull()) { + result->Error(kInternalConsistencyError, + "Selection base/extent values invalid."); + return; + } + active_model_->SetEditingState(selection_base->value.GetInt(), + selection_extent->value.GetInt(), + text->value.GetString()); + } else { + // Unhandled method. + result->NotImplemented(); + return; + } + } + // All error conditions return early, so if nothing has gone wrong indicate + // success. + result->Success(); +} + +void TextInputPlugin::SendStateUpdate(const TextInputModel& model) { + channel_->InvokeMethod(kUpdateEditingStateMethod, model.GetState()); +} + +void TextInputPlugin::EnterPressed(TextInputModel* model) { + if (model->input_type() == kMultilineInputType) { + model->AddCharacter('\n'); + SendStateUpdate(*model); + } + auto args = std::make_unique(rapidjson::kArrayType); + auto& allocator = args->GetAllocator(); + args->PushBack(model->client_id(), allocator); + args->PushBack(rapidjson::Value(model->input_action(), allocator).Move(), + allocator); + + channel_->InvokeMethod(kPerformActionMethod, std::move(args)); +} + +} // namespace shell diff --git a/shell/platform/glfw/text_input_plugin.h b/shell/platform/glfw/text_input_plugin.h new file mode 100644 index 0000000000000..a6d8a4ed5686d --- /dev/null +++ b/shell/platform/glfw/text_input_plugin.h @@ -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. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_TEXT_INPUT_PLUGIN_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_TEXT_INPUT_PLUGIN_H_ + +#include +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h" +#include "flutter/shell/platform/common/cpp/text_input_model.h" +#include "flutter/shell/platform/glfw/keyboard_hook_handler.h" +#include "flutter/shell/platform/glfw/public/flutter_glfw.h" + +namespace shell { + +// Implements a text input plugin. +// +// Specifically handles window events within GLFW. +class TextInputPlugin : public KeyboardHookHandler { + public: + explicit TextInputPlugin(flutter::BinaryMessenger* messenger); + + virtual ~TextInputPlugin(); + + // |shell::KeyboardHookHandler| + void KeyboardHook(GLFWwindow* window, + int key, + int scancode, + int action, + int mods) override; + + // |shell::KeyboardHookHandler| + void CharHook(GLFWwindow* window, unsigned int code_point) override; + + private: + // Sends the current state of the given model to the Flutter engine. + void SendStateUpdate(const TextInputModel& model); + + // Sends an action triggered by the Enter key to the Flutter engine. + void EnterPressed(TextInputModel* model); + + // Called when a method is called on |channel_|; + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + // The MethodChannel used for communication with the Flutter engine. + std::unique_ptr> channel_; + + // Mapping of client IDs to text input models. + std::map> input_models_; + + // The active model. nullptr if not set. + TextInputModel* active_model_; +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_TEXT_INPUT_PLUGIN_H_ diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index ed185e4541532..3d2c125baa84f 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -1,11 +1,22 @@ -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +assert(is_linux) + group("linux") { - testonly = true + deps = [ + ":flutter_linux", + "$flutter_root/shell/platform/glfw:publish_headers_glfw", + "$flutter_root/shell/platform/common/cpp/client_wrapper:publish_wrapper", + "$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw", + ] +} +shared_library("flutter_linux") { deps = [ - "$flutter_root/shell/testing", + "$flutter_root/shell/platform/glfw:flutter_glfw", ] + + public_configs = [ "$flutter_root:config" ] } diff --git a/shell/platform/linux/config/BUILD.gn b/shell/platform/linux/config/BUILD.gn new file mode 100644 index 0000000000000..f501bac61096e --- /dev/null +++ b/shell/platform/linux/config/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2013 The Flutter 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/config/linux/pkg_config.gni") + +pkg_config("gtk3") { + packages = [ "gtk+-3.0" ] +} + +pkg_config("x11") { + packages = [ "x11" ] +} diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn new file mode 100644 index 0000000000000..26fe53036b49a --- /dev/null +++ b/shell/platform/windows/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +assert(is_win) + +group("windows") { + deps = [ + ":flutter_windows", + "$flutter_root/shell/platform/glfw:publish_headers_glfw", + "$flutter_root/shell/platform/common/cpp/client_wrapper:publish_wrapper", + "$flutter_root/shell/platform/glfw/client_wrapper:publish_wrapper_glfw", + ] +} + +shared_library("flutter_windows") { + deps = [ + "$flutter_root/shell/platform/glfw:flutter_glfw", + ] + + public_configs = [ "$flutter_root:config" ] +} diff --git a/shell/testing/BUILD.gn b/shell/testing/BUILD.gn index cc3d835c42e04..f10d36b9808e8 100644 --- a/shell/testing/BUILD.gn +++ b/shell/testing/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -28,6 +28,7 @@ executable("testing") { if (is_fuchsia) { deps += [ "//garnet/public/lib/ui/scenic:client", + "//zircon/public/lib/trace", "//zircon/public/lib/trace-provider", ] } diff --git a/shell/testing/observatory/empty_main.dart b/shell/testing/observatory/empty_main.dart index 0d653a819f728..94e557c8cadc7 100644 --- a/shell/testing/observatory/empty_main.dart +++ b/shell/testing/observatory/empty_main.dart @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/testing/observatory/launcher.dart b/shell/testing/observatory/launcher.dart index 9a88555199538..82fdd108f3c74 100644 --- a/shell/testing/observatory/launcher.dart +++ b/shell/testing/observatory/launcher.dart @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/testing/observatory/service_client.dart b/shell/testing/observatory/service_client.dart index badcaf669102b..f24b628ddae78 100644 --- a/shell/testing/observatory/service_client.dart +++ b/shell/testing/observatory/service_client.dart @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/testing/observatory/test.dart b/shell/testing/observatory/test.dart index d753d1522c9b8..9d97dba0c9dd6 100644 --- a/shell/testing/observatory/test.dart +++ b/shell/testing/observatory/test.dart @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 207e35301ff5d..7aa9dee3c0794 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -1,4 +1,4 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -21,10 +21,6 @@ #include "flutter/shell/common/thread_host.h" #include "third_party/dart/runtime/include/bin/dart_io_api.h" -#ifdef ERROR -#undef ERROR -#endif - namespace shell { // Checks whether the engine's main Dart isolate has no pending work. If so, @@ -124,17 +120,30 @@ int RunTester(const blink::Settings& settings, bool run_forever) { return EXIT_FAILURE; } - if (settings.main_dart_file_path.empty()) { - FML_LOG(ERROR) << "Main dart file not specified."; + if (settings.application_kernel_asset.empty()) { + FML_LOG(ERROR) << "Dart kernel file not specified."; return EXIT_FAILURE; } + // Initialize default testing locales. There is no platform to + // pass locales on the tester, so to retain expected locale behavior, + // we emulate it in here by passing in 'en_US' and 'zh_CN' as test locales. + const char* locale_json = + "{\"method\":\"setLocale\",\"args\":[\"en\",\"US\",\"\",\"\",\"zh\"," + "\"CN\",\"\",\"\"]}"; + std::vector locale_bytes(locale_json, + locale_json + std::strlen(locale_json)); + fml::RefPtr response; + shell->GetPlatformView()->DispatchPlatformMessage( + fml::MakeRefCounted("flutter/localization", + locale_bytes, response)); + std::initializer_list protection = { fml::FileMapping::Protection::kRead}; auto main_dart_file_mapping = std::make_unique( fml::OpenFile( - fml::paths::AbsolutePath(settings.main_dart_file_path).c_str(), false, - fml::FilePermission::kRead), + fml::paths::AbsolutePath(settings.application_kernel_asset).c_str(), + false, fml::FilePermission::kRead), protection); auto isolate_configuration = @@ -145,7 +154,7 @@ int RunTester(const blink::Settings& settings, bool run_forever) { return EXIT_FAILURE; } - auto asset_manager = fml::MakeRefCounted(); + auto asset_manager = std::make_shared(); asset_manager->PushBack(std::make_unique( fml::Duplicate(settings.assets_dir))); asset_manager->PushBack( @@ -234,11 +243,11 @@ int main(int argc, char* argv[]) { if (command_line.positional_args().size() > 0) { // The tester may not use the switch for the main dart file path. Specifying // it as a positional argument instead. - settings.main_dart_file_path = command_line.positional_args()[0]; + settings.application_kernel_asset = command_line.positional_args()[0]; } - if (settings.main_dart_file_path.size() == 0) { - FML_LOG(ERROR) << "Main dart file path not specified."; + if (settings.application_kernel_asset.size() == 0) { + FML_LOG(ERROR) << "Dart kernel file not specified."; return EXIT_FAILURE; } diff --git a/shell/version/version.cc b/shell/version/version.cc deleted file mode 100644 index 86525de620191..0000000000000 --- a/shell/version/version.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/version/version.h" - -namespace shell { - -const char* GetFlutterEngineVersion() { - return SHELL_FLUTTER_ENGINE_VERSION; -} - -const char* GetSkiaVersion() { - return SHELL_SKIA_VERSION; -} - -const char* GetDartVersion() { - return SHELL_DART_VERSION; -} - -} // namespace shell diff --git a/shell/version/version.h b/shell/version/version.h deleted file mode 100644 index 1c9ca4d1d7c34..0000000000000 --- a/shell/version/version.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018 The Flutter 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_COMMON_VERSION_H_ -#define FLUTTER_SHELL_COMMON_VERSION_H_ - -namespace shell { - -const char* GetFlutterEngineVersion(); - -const char* GetSkiaVersion(); - -const char* GetDartVersion(); - -} // namespace shell - -#endif // FLUTTER_SHELL_COMMON_VERSION_H_ diff --git a/sky/BUILD.gn b/sky/BUILD.gn index 66045451407b5..b950560f5a080 100644 --- a/sky/BUILD.gn +++ b/sky/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/dist/BUILD.gn b/sky/dist/BUILD.gn index a7068e32ab65a..c1b564a03ecce 100644 --- a/sky/dist/BUILD.gn +++ b/sky/dist/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/packages/BUILD.gn b/sky/packages/BUILD.gn index f9dd45cd921a1..07766e6a4695c 100644 --- a/sky/packages/BUILD.gn +++ b/sky/packages/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/packages/flutter_services/BUILD.gn b/sky/packages/flutter_services/BUILD.gn index f326037901b3d..6ec4f4b2e4504 100644 --- a/sky/packages/flutter_services/BUILD.gn +++ b/sky/packages/flutter_services/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/packages/flutter_services/lib/empty.dart b/sky/packages/flutter_services/lib/empty.dart index a3f12bfd1109e..396832c3347e1 100644 --- a/sky/packages/flutter_services/lib/empty.dart +++ b/sky/packages/flutter_services/lib/empty.dart @@ -1,4 +1,4 @@ -/// Copyright 2016 The Chromium Authors. All rights reserved. +/// Copyright 2013 The Flutter 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/sky/packages/sky_engine/BUILD.gn b/sky/packages/sky_engine/BUILD.gn index 3ea8c4f31b84e..9556957954b31 100644 --- a/sky/packages/sky_engine/BUILD.gn +++ b/sky/packages/sky_engine/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index a49310632e4b2..9316937d70e08 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -2587,6 +2587,15 @@ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- boringssl +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 +-------------------------------------------------------------------------------- +boringssl + The MIT License (MIT) Copyright (c) 2015-2016 the fiat-crypto authors (see @@ -2799,10 +2808,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- boringssl +engine +etc1 observatory_pub_packages skia txt vulkan +wuffs Apache License Version 2.0, January 2004 @@ -3006,6 +3018,32 @@ 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. -------------------------------------------------------------------------------- +bsdiff + +Copyright 2003-2005 Colin Percival. 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 THE AUTHOR ``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 BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- colorama Copyright (c) 2010 Jonathan Hartley @@ -3331,13 +3369,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- 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 --------------------------------------------------------------------------------- -dart +Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +for details. All rights reserved. -Copyright 2012, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -3362,14 +3396,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -double-conversion -icu +dart + +Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +for details. All rights reserved. -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 @@ -3379,7 +3413,6 @@ met: * 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 @@ -3392,14 +3425,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -double-conversion -icu +dart -Copyright 2010 the V8 project authors. All rights reserved. +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 +-------------------------------------------------------------------------------- +dart + +Copyright 2012, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above @@ -3409,7 +3446,6 @@ met: * 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 @@ -3425,7 +3461,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. double-conversion icu -Copyright 2012 the V8 project authors. All rights reserved. +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: @@ -3452,23 +3488,23 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -engine - -Copyright (c) 2013 The Chromium Authors. All rights reserved. +double-conversion +icu +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. + * 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 @@ -3482,23 +3518,23 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -engine - -Copyright 2013 The Chromium Authors. All rights reserved. +double-conversion +icu +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. + * 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 @@ -3513,346 +3549,96 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine +txt -Copyright 2017 The Flutter Authors. All rights reserved. +Copyright 2013 The Flutter Authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +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. + * 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. +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. -------------------------------------------------------------------------------- -engine +files -Copyright 2018 The Flutter Authors. All rights reserved. +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +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: - * 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 above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. -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. +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. -------------------------------------------------------------------------------- -engine -icu +files -Copyright 2014 The Chromium Authors. All rights reserved. +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +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: - * 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 above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. -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. +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. -------------------------------------------------------------------------------- -engine -icu -skia +files -Copyright 2015 The Chromium Authors. All rights reserved. +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + and Clark Cooper +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +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: - * 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. --------------------------------------------------------------------------------- -engine -icu -skia - -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. --------------------------------------------------------------------------------- -engine -skia - -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. --------------------------------------------------------------------------------- -engine -tonic - -Copyright 2016 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. - * 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. --------------------------------------------------------------------------------- -engine -tonic - -Copyright 2017 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. - * 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. --------------------------------------------------------------------------------- -engine -txt - -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. --------------------------------------------------------------------------------- -files - -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. --------------------------------------------------------------------------------- -files - -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. --------------------------------------------------------------------------------- -files - -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 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 @@ -4454,508 +4240,473 @@ Legal Terms -------------------------------------------------------------------------------- gif -GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! +MOZILLA PUBLIC LICENSE + Version 1.1 + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the MPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (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.mozilla.org/MPL + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] -------------------------------------------------------------------------------- gif @@ -4979,6 +4730,194 @@ may be used: CompuServe Incorporated. GIF(sm) is a Service Mark property of CompuServe Incorporated." -------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- +glfw + +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. +-------------------------------------------------------------------------------- harfbuzz Copyright (C) 2012 Grigori Goronzy @@ -5753,7 +5692,128 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2010,2011 Google, Inc. +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. +-------------------------------------------------------------------------------- +harfbuzz + +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. +-------------------------------------------------------------------------------- +harfbuzz + +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. +-------------------------------------------------------------------------------- +harfbuzz + +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. +-------------------------------------------------------------------------------- +harfbuzz + +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. +-------------------------------------------------------------------------------- +harfbuzz + +Copyright © 2011 Martin Hosken +Copyright © 2011 SIL International This is part of HarfBuzz, a text shaping library. @@ -5777,7 +5837,9 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2010,2011,2012 Google, Inc. +Copyright © 2011 Martin Hosken +Copyright © 2011 SIL International +Copyright © 2011,2012 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5801,7 +5863,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2010,2011,2013 Google, Inc. +Copyright © 2011,2012 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5825,7 +5887,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2010,2012 Google, Inc. +Copyright © 2011,2012,2013 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5849,7 +5911,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011 Google, Inc. +Copyright © 2011,2012,2014 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5873,8 +5935,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011 Martin Hosken -Copyright © 2011 SIL International +Copyright © 2011,2014 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5898,9 +5959,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011 Martin Hosken -Copyright © 2011 SIL International -Copyright © 2011,2012 Google, Inc. +Copyright © 2012 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5924,7 +5983,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011,2012 Google, Inc. +Copyright © 2012 Mozilla Foundation. This is part of HarfBuzz, a text shaping library. @@ -5948,7 +6007,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011,2012,2013 Google, Inc. +Copyright © 2012,2013 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5972,7 +6031,8 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011,2012,2014 Google, Inc. +Copyright © 2012,2013 Mozilla Foundation. +Copyright © 2012,2013 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -5996,7 +6056,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2011,2014 Google, Inc. +Copyright © 2012,2017 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6020,7 +6080,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2012 Google, Inc. +Copyright © 2013 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6044,7 +6104,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2012 Mozilla Foundation. +Copyright © 2013 Red Hat, Inc. This is part of HarfBuzz, a text shaping library. @@ -6068,7 +6128,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2012,2013 Google, Inc. +Copyright © 2014 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6092,8 +6152,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2012,2013 Mozilla Foundation. -Copyright © 2012,2013 Google, Inc. +Copyright © 2015 Ebrahim Byagowi This is part of HarfBuzz, a text shaping library. @@ -6117,7 +6176,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2012,2017 Google, Inc. +Copyright © 2015 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6141,7 +6200,8 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2013 Google, Inc. +Copyright © 2015 Mozilla Foundation. +Copyright © 2015 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6165,7 +6225,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2013 Red Hat, Inc. +Copyright © 2015-2018 Ebrahim Byagowi This is part of HarfBuzz, a text shaping library. @@ -6189,7 +6249,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2014 Google, Inc. +Copyright © 2016 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6213,7 +6273,8 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2015 Ebrahim Byagowi +Copyright © 2016 Google, Inc. +Copyright © 2018 Ebrahim Byagowi This is part of HarfBuzz, a text shaping library. @@ -6237,7 +6298,9 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2015 Google, Inc. +Copyright © 2016 Google, Inc. +Copyright © 2018 Khaled Hosny +Copyright © 2018 Ebrahim Byagowi This is part of HarfBuzz, a text shaping library. @@ -6261,8 +6324,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2015 Mozilla Foundation. -Copyright © 2015 Google, Inc. +Copyright © 2016 Igalia S.L. This is part of HarfBuzz, a text shaping library. @@ -6286,7 +6348,8 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2015-2018 Ebrahim Byagowi +Copyright © 2016 Elie Roux +Copyright © 2018 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6310,7 +6373,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2016 Google, Inc. +Copyright © 2017 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6334,8 +6397,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2016 Google, Inc. -Copyright © 2018 Ebrahim Byagowi +Copyright © 2017,2018 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6359,7 +6421,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2016 Igalia S.L. +Copyright © 2018 Ebrahim Byagowi This is part of HarfBuzz, a text shaping library. @@ -6383,7 +6445,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2016 Elie Roux +Copyright © 2018 Ebrahim Byagowi Copyright © 2018 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6408,7 +6470,8 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2017 Google, Inc. +Copyright © 2018 Ebrahim Byagowi +Copyright © 2018 Khaled Hosny This is part of HarfBuzz, a text shaping library. @@ -6432,7 +6495,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2018 Ebrahim Byagowi +Copyright © 2018 Ebrahim Byagowi. This is part of HarfBuzz, a text shaping library. @@ -6456,7 +6519,6 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2018 Ebrahim Byagowi Copyright © 2018 Google, Inc. This is part of HarfBuzz, a text shaping library. @@ -6481,7 +6543,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -------------------------------------------------------------------------------- harfbuzz -Copyright © 2018 Google, Inc. +Copyright © 2018 Adobe Systems Incorporated. This is part of HarfBuzz, a text shaping library. @@ -7054,6 +7116,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- icu +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. +-------------------------------------------------------------------------------- +icu + Copyright © 1991-2018 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. @@ -7150,64 +7242,237 @@ F. Miscellaneous. 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 +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. +-------------------------------------------------------------------------------- +icu +skia + +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. +-------------------------------------------------------------------------------- +icu +skia + +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. +-------------------------------------------------------------------------------- +libcxx + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2017 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. +-------------------------------------------------------------------------------- +libcxx +libcxxabi + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +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. +-------------------------------------------------------------------------------- +libcxxabi + +University of Illinois/NCSA +Open Source License -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/. +Copyright (c) 2009-2018 by the contributors listed in CREDITS.TXT -Unicode Data Files do not include PDF online code charts under the -directory http://www.unicode.org/Public/. +All rights reserved. -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/. +Developed by: -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. + LLVM Team -COPYRIGHT AND PERMISSION NOTICE + University of Illinois at Urbana-Champaign -Copyright © 1991-2017 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + http://llvm.org -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. +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: -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. + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. -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. + * 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. -------------------------------------------------------------------------------- libjpeg-turbo @@ -9704,7 +9969,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skcms skia -vulkan vulkanmemoryallocator Copyright 2018 Google Inc. @@ -10047,7 +10311,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2009 Motorola +Copyright 2009 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 @@ -10077,7 +10341,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2009 The Android Open Source Project +Copyright 2009-2015 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10107,7 +10371,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2009-2015 Google Inc. +Copyright 2010 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10137,7 +10401,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2010 Google Inc. +Copyright 2010 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 @@ -10167,7 +10431,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2010 The Android Open Source Project +Copyright 2011 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10198,6 +10462,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. skia Copyright 2011 Google Inc. +Copyright 2012 Mozilla Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10227,8 +10492,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2011 Google Inc. -Copyright 2012 Mozilla Foundation +Copyright 2011 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 @@ -10258,7 +10522,188 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2011 The Android Open Source Project +Copyright 2012 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. +-------------------------------------------------------------------------------- +skia + +Copyright 2012 Intel 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. +-------------------------------------------------------------------------------- +skia + +Copyright 2012 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 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. +-------------------------------------------------------------------------------- +skia + +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 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. +-------------------------------------------------------------------------------- +skia + +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 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. +-------------------------------------------------------------------------------- +skia + +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 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. +-------------------------------------------------------------------------------- +skia + +Copyright 2014 Google Inc. +Copyright 2017 ARM Ltd. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10288,7 +10733,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2012 Google Inc. +Copyright 2014 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 @@ -10318,7 +10763,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2012 Intel Inc. +Copyright 2015 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10348,7 +10793,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2012 The Android Open Source Project +Copyright 2015 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 @@ -10378,7 +10823,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2013 Google Inc. +Copyright 2016 Mozilla Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10408,7 +10853,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2013 The Android Open Source Project +Copyright 2016 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 @@ -10438,7 +10883,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2014 Google Inc. +Copyright 2017 ARM Ltd. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10468,8 +10913,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2014 Google Inc. -Copyright 2017 ARM Ltd. +Copyright 2017 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10499,7 +10943,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2014 The Android Open Source Project +Copyright 2018 Google LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10529,7 +10973,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2015 Google Inc. +Copyright 2018 Google LLC. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10559,7 +11003,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2015 The Android Open Source Project +Copyright 2018 Google, LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10589,7 +11033,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2016 Mozilla Foundation +Copyright 2018 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 @@ -10619,7 +11063,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2016 The Android Open Source Project +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 @@ -10649,7 +11093,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2017 ARM Ltd. +Copyright 2019 Google Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10679,7 +11123,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2017 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 @@ -10709,7 +11153,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2018 Google LLC +Copyright 2019 Google LLC. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10739,7 +11183,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2018 Google LLC. +Copyright 2019 Google, LLC Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10769,7 +11213,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- skia -Copyright 2018 Google, LLC +NEON optimized code (C) COPYRIGHT 2009 Motorola Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10797,21 +11241,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -skia +tcmalloc -Copyright 2018 The Android Open Source Project +Copyright (c) 2003, 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 + * 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 + * Redistributions in binary form must 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 + * 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. @@ -10827,21 +11272,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -skia +tcmalloc -NEON optimized code (C) COPYRIGHT 2009 Motorola +Copyright (c) 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 + * 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 + * Redistributions in binary form must 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 + * 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. @@ -10857,22 +11303,21 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -tcmalloc +tonic -Copyright (c) 2003, Google Inc. -All rights reserved. +Copyright 2015 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 + * 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 + * Redistributions in binary form must 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 + * 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. @@ -10888,22 +11333,21 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- -tcmalloc +tonic -Copyright (c) 2005, Google Inc. -All rights reserved. +Copyright 2016 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 + * 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 + * Redistributions in binary form must 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 + * 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. @@ -10921,7 +11365,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- tonic -Copyright 2015 The Fuchsia Authors. All rights reserved. +Copyright 2017 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 @@ -11001,6 +11445,185 @@ 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. -------------------------------------------------------------------------------- +wuffs + +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 +-------------------------------------------------------------------------------- zlib Copyright (C) 1995-2003, 2010 Jean-loup Gailly. diff --git a/sky/packages/sky_services/BUILD.gn b/sky/packages/sky_services/BUILD.gn index 44132cc252183..582e57c319dc7 100644 --- a/sky/packages/sky_services/BUILD.gn +++ b/sky/packages/sky_services/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/change_install_name.py b/sky/tools/change_install_name.py index b6d479bc5c760..3d715c422ff43 100755 --- a/sky/tools/change_install_name.py +++ b/sky/tools/change_install_name.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/create_ios_framework.py b/sky/tools/create_ios_framework.py index 73f17b4303ad2..f9dc2f7f56283 100755 --- a/sky/tools/create_ios_framework.py +++ b/sky/tools/create_ios_framework.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/create_macos_gen_snapshot.py b/sky/tools/create_macos_gen_snapshot.py index 218ee2ad725de..a9ea7f88378ec 100755 --- a/sky/tools/create_macos_gen_snapshot.py +++ b/sky/tools/create_macos_gen_snapshot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2018 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/dist_dart_pkg.py b/sky/tools/dist_dart_pkg.py old mode 100644 new mode 100755 index 9a1318796eefd..af8bd1dbb201d --- a/sky/tools/dist_dart_pkg.py +++ b/sky/tools/dist_dart_pkg.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/flutter_gdb b/sky/tools/flutter_gdb index 73a10a5468744..1ba48e6f98633 100755 --- a/sky/tools/flutter_gdb +++ b/sky/tools/flutter_gdb @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/install_framework_headers.py b/sky/tools/install_framework_headers.py old mode 100644 new mode 100755 index f76ffcb16ab8f..a746ff90b0e7e --- a/sky/tools/install_framework_headers.py +++ b/sky/tools/install_framework_headers.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/objcopy.py b/sky/tools/objcopy.py new file mode 100755 index 0000000000000..b8da36d43b8e3 --- /dev/null +++ b/sky/tools/objcopy.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# Copyright 2013 The Flutter 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 os +import subprocess +import sys + +# BFD architecture names recognized by objcopy. +BFD_ARCH = { + 'arm': 'arm', + 'arm64': 'aarch64', + 'x86': 'i386', + 'x64': 'i386:x86-64', +} + +# BFD target names recognized by objcopy. +BFD_TARGET = { + 'arm': 'elf32-littlearm', + 'arm64': 'elf64-littleaarch64', + 'x86': 'elf32-i386', + 'x64': 'elf64-x86-64', +} + +def main(): + parser = argparse.ArgumentParser(description='Convert a data file to an object file') + parser.add_argument('--objcopy', type=str, required=True) + parser.add_argument('--input', type=str, required=True) + parser.add_argument('--output', type=str, required=True) + parser.add_argument('--arch', type=str, required=True) + + args = parser.parse_args() + + input_dir, input_file = os.path.split(args.input) + output_path = os.path.abspath(args.output) + + subprocess.check_call([ + args.objcopy, + '-I', 'binary', + '-O', BFD_TARGET[args.arch], + '-B', BFD_ARCH[args.arch], + input_file, + output_path, + ], cwd=input_dir) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/sky/tools/roll/patch.py b/sky/tools/roll/patch.py index fbc2389e26c1a..f08b610e31601 100755 --- a/sky/tools/roll/patch.py +++ b/sky/tools/roll/patch.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/roll/roll.py b/sky/tools/roll/roll.py index 994685a42bb0f..c4d322cb9cebb 100755 --- a/sky/tools/roll/roll.py +++ b/sky/tools/roll/roll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/sky/tools/roll/utils.py b/sky/tools/roll/utils.py index d2de2c487d148..c14732e34d12d 100755 --- a/sky/tools/roll/utils.py +++ b/sky/tools/roll/utils.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/synchronization/BUILD.gn b/synchronization/BUILD.gn index 258e49a182445..95b8655964b53 100644 --- a/synchronization/BUILD.gn +++ b/synchronization/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/synchronization/pipeline.cc b/synchronization/pipeline.cc index df8505dbee7a9..2baede8dfbd1f 100644 --- a/synchronization/pipeline.cc +++ b/synchronization/pipeline.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/synchronization/pipeline.h b/synchronization/pipeline.h index 726adf64de842..3aafc1767a702 100644 --- a/synchronization/pipeline.h +++ b/synchronization/pipeline.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -55,6 +55,7 @@ class Pipeline : public fml::RefCountedThreadSafe> { TRACE_EVENT_ASYNC_END0("flutter", "PipelineProduce", trace_id_); // The continuation is being dropped on the floor. End the flow. TRACE_FLOW_END("flutter", "PipelineItem", trace_id_); + TRACE_EVENT_ASYNC_END0("flutter", "PipelineItem", trace_id_); } } @@ -79,6 +80,7 @@ class Pipeline : public fml::RefCountedThreadSafe> { ProducerContinuation(Continuation continuation, size_t trace_id) : continuation_(continuation), trace_id_(trace_id) { TRACE_FLOW_BEGIN("flutter", "PipelineItem", trace_id_); + TRACE_EVENT_ASYNC_BEGIN0("flutter", "PipelineItem", trace_id_); TRACE_EVENT_ASYNC_BEGIN0("flutter", "PipelineProduce", trace_id_); } @@ -133,6 +135,7 @@ class Pipeline : public fml::RefCountedThreadSafe> { empty_.Signal(); TRACE_FLOW_END("flutter", "PipelineItem", trace_id); + TRACE_EVENT_ASYNC_END0("flutter", "PipelineItem", trace_id); return items_count > 0 ? PipelineConsumeResult::MoreAvailable : PipelineConsumeResult::Done; diff --git a/synchronization/semaphore.cc b/synchronization/semaphore.cc index f3a6039b15f11..4d8e5003d5921 100644 --- a/synchronization/semaphore.cc +++ b/synchronization/semaphore.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/synchronization/semaphore.h b/synchronization/semaphore.h index e2df8b3d81ad8..594b0a559bc04 100644 --- a/synchronization/semaphore.h +++ b/synchronization/semaphore.h @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/synchronization/semaphore_unittest.cc b/synchronization/semaphore_unittest.cc index 42b898d53acb5..61810b641bee8 100644 --- a/synchronization/semaphore_unittest.cc +++ b/synchronization/semaphore_unittest.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/BUILD.gn b/testing/BUILD.gn index 7d5b8be9ae018..424d978f81570 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/testing/build/gen_fixtures_location_symbol.py b/testing/build/gen_fixtures_location_symbol.py index c2c49d087d7b4..3b66475d4afb8 100644 --- a/testing/build/gen_fixtures_location_symbol.py +++ b/testing/build/gen_fixtures_location_symbol.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 8ab5581317730..d79950d1f1703 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -1,52 +1,53 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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:ui'; import 'dart:typed_data'; +import 'dart:ui'; import 'package:test/test.dart'; -typedef void CanvasCallback(Canvas canvas); +typedef CanvasCallback = void Function(Canvas canvas); void testCanvas(CanvasCallback callback) { try { callback(Canvas(PictureRecorder(), Rect.fromLTRB(0.0, 0.0, 0.0, 0.0))); - } catch (error) { } + } catch (error) { } // ignore: empty_catches } void main() { - test("canvas APIs should not crash", () { - Paint paint = Paint(); - Rect rect = Rect.fromLTRB(double.nan, double.nan, double.nan, double.nan); - RRect rrect = RRect.fromRectAndCorners(rect); - Offset offset = Offset(double.nan, double.nan); - Path path = Path(); - Color color = Color(0); - Paragraph paragraph = ParagraphBuilder(ParagraphStyle()).build(); + test('canvas APIs should not crash', () async { + final Paint paint = Paint(); + final Rect rect = Rect.fromLTRB(double.nan, double.nan, double.nan, double.nan); + final RRect rrect = RRect.fromRectAndCorners(rect); + const Offset offset = Offset(double.nan, double.nan); + final Path path = Path(); + const Color color = Color(0); + final Paragraph paragraph = ParagraphBuilder(ParagraphStyle()).build(); - PictureRecorder recorder = PictureRecorder(); - Canvas recorderCanvas = Canvas(recorder); - Picture picture = recorder.endRecording(); - Image image = picture.toImage(1, 1); + final PictureRecorder recorder = PictureRecorder(); + final Canvas recorderCanvas = Canvas(recorder); + recorderCanvas.scale(1.0, 1.0); + final Picture picture = recorder.endRecording(); + final Image image = await picture.toImage(1, 1); - try { Canvas(null, null); } catch (error) { } - try { Canvas(null, rect); } catch (error) { } - try { Canvas(PictureRecorder(), null); } catch (error) { } - try { Canvas(PictureRecorder(), rect); } catch (error) { } + try { Canvas(null, null); } catch (error) { } // ignore: empty_catches + try { Canvas(null, rect); } catch (error) { } // ignore: empty_catches + try { Canvas(PictureRecorder(), null); } catch (error) { } // ignore: empty_catches + try { Canvas(PictureRecorder(), rect); } catch (error) { } // ignore: empty_catches try { PictureRecorder() ..endRecording() ..endRecording() ..endRecording(); - } catch (error) { } + } catch (error) { } // ignore: empty_catches testCanvas((Canvas canvas) => canvas.clipPath(path)); testCanvas((Canvas canvas) => canvas.clipRect(rect)); testCanvas((Canvas canvas) => canvas.clipRRect(rrect)); testCanvas((Canvas canvas) => canvas.drawArc(rect, 0.0, 0.0, false, paint)); - testCanvas((Canvas canvas) => canvas.drawAtlas(image, [], [], [], BlendMode.src, rect, paint)); + testCanvas((Canvas canvas) => canvas.drawAtlas(image, [], [], [], BlendMode.src, rect, paint)); testCanvas((Canvas canvas) => canvas.drawCircle(offset, double.nan, paint)); testCanvas((Canvas canvas) => canvas.drawColor(color, BlendMode.src)); testCanvas((Canvas canvas) => canvas.drawDRRect(rrect, rrect, paint)); @@ -59,7 +60,7 @@ void main() { testCanvas((Canvas canvas) => canvas.drawParagraph(paragraph, offset)); testCanvas((Canvas canvas) => canvas.drawPath(path, paint)); testCanvas((Canvas canvas) => canvas.drawPicture(picture)); - testCanvas((Canvas canvas) => canvas.drawPoints(PointMode.points, [], paint)); + testCanvas((Canvas canvas) => canvas.drawPoints(PointMode.points, [], paint)); testCanvas((Canvas canvas) => canvas.drawRawAtlas(image, Float32List(0), Float32List(0), Int32List(0), BlendMode.src, rect, paint)); testCanvas((Canvas canvas) => canvas.drawRawPoints(PointMode.points, Float32List(0), paint)); testCanvas((Canvas canvas) => canvas.drawRect(rect, paint)); @@ -67,7 +68,7 @@ void main() { testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, null)); testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, false)); testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, true)); - testCanvas((Canvas canvas) => canvas.drawVertices(Vertices(VertexMode.triangles, []), null, paint)); + testCanvas((Canvas canvas) => canvas.drawVertices(Vertices(VertexMode.triangles, []), null, paint)); testCanvas((Canvas canvas) => canvas.getSaveCount()); testCanvas((Canvas canvas) => canvas.restore()); testCanvas((Canvas canvas) => canvas.rotate(double.nan)); diff --git a/testing/dart/codec_test.dart b/testing/dart/codec_test.dart index b205aef539f00..728bfdcc269c1 100644 --- a/testing/dart/codec_test.dart +++ b/testing/dart/codec_test.dart @@ -1,11 +1,10 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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'; -import 'dart:ui' as ui; import 'dart:typed_data'; +import 'dart:ui' as ui; import 'package:test/test.dart'; import 'package:path/path.dart' as path; @@ -27,7 +26,7 @@ void main() { }); test('Fails with invalid data', () async { - Uint8List data = new Uint8List.fromList([1, 2, 3]); + final Uint8List data = Uint8List.fromList([1, 2, 3]); expect( ui.instantiateImageCodec(data), throwsA(exceptionWithMessage('operation failed')) @@ -35,64 +34,64 @@ void main() { }); test('nextFrame', () async { - Uint8List data = await _getSkiaResource('test640x479.gif').readAsBytes(); - ui.Codec codec = await ui.instantiateImageCodec(data); - List> decodedFrameInfos = []; + final Uint8List data = await _getSkiaResource('test640x479.gif').readAsBytes(); + final ui.Codec codec = await ui.instantiateImageCodec(data); + final List> decodedFrameInfos = >[]; for (int i = 0; i < 5; i++) { - ui.FrameInfo frameInfo = await codec.getNextFrame(); - decodedFrameInfos.add([ + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + decodedFrameInfos.add([ frameInfo.duration.inMilliseconds, frameInfo.image.width, frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals([ - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], + expect(decodedFrameInfos, equals(>[ + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], ])); }); test('decodedCacheRatioCap', () async { // No real way to test the native layer, but a smoke test here to at least // verify that animation is still consistent with caching disabled. - Uint8List data = await _getSkiaResource('test640x479.gif').readAsBytes(); - ui.Codec codec = await ui.instantiateImageCodec(data, decodedCacheRatioCap: 1.0); - List> decodedFrameInfos = []; + final Uint8List data = await _getSkiaResource('test640x479.gif').readAsBytes(); + final ui.Codec codec = await ui.instantiateImageCodec(data, decodedCacheRatioCap: 1.0); + final List> decodedFrameInfos = >[]; for (int i = 0; i < 5; i++) { - ui.FrameInfo frameInfo = await codec.getNextFrame(); - decodedFrameInfos.add([ + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + decodedFrameInfos.add([ frameInfo.duration.inMilliseconds, frameInfo.image.width, frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals([ - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], + expect(decodedFrameInfos, equals(>[ + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], ])); }); test('non animated image', () async { - Uint8List data = await _getSkiaResource('baby_tux.png').readAsBytes(); - ui.Codec codec = await ui.instantiateImageCodec(data); - List> decodedFrameInfos = []; + final Uint8List data = await _getSkiaResource('baby_tux.png').readAsBytes(); + final ui.Codec codec = await ui.instantiateImageCodec(data); + final List> decodedFrameInfos = >[]; for (int i = 0; i < 2; i++) { - ui.FrameInfo frameInfo = await codec.getNextFrame(); - decodedFrameInfos.add([ + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + decodedFrameInfos.add([ frameInfo.duration.inMilliseconds, frameInfo.image.width, frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals([ - [0, 240, 246], - [0, 240, 246], + expect(decodedFrameInfos, equals(>[ + [0, 240, 246], + [0, 240, 246], ])); }); } @@ -104,13 +103,13 @@ File _getSkiaResource(String fileName) { // assuming the curent working directory is engine/src. // This is fragile and should be changed once the Platform.script issue is // resolved. - String assetPath = + final String assetPath = path.join('third_party', 'skia', 'resources', 'images', fileName); - return new File(assetPath); + return File(assetPath); } Matcher exceptionWithMessage(String m) { - return predicate((e) { + return predicate((Exception e) { return e is Exception && e.toString().contains(m); }); } diff --git a/testing/dart/color_test.dart b/testing/dart/color_test.dart index 6bdccc7b72f6d..c70d2c7182f21 100644 --- a/testing/dart/color_test.dart +++ b/testing/dart/color_test.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,7 +12,7 @@ class NotAColor extends Color { void main() { test('color accessors should work', () { - final Color foo = const Color(0x12345678); + const Color foo = Color(0x12345678); expect(foo.alpha, equals(0x12)); expect(foo.red, equals(0x34)); expect(foo.green, equals(0x56)); @@ -20,7 +20,7 @@ void main() { }); test('paint set to black', () { - final Color c = const Color(0x00000000); + const Color c = Color(0x00000000); final Paint p = new Paint(); p.color = c; expect(c.toString(), equals('Color(0x00000000)')); @@ -28,7 +28,7 @@ void main() { test('color created with out of bounds value', () { try { - final Color c = const Color(0x100 << 24); + const Color c = Color(0x100 << 24); final Paint p = new Paint(); p.color = c; } catch (e) { @@ -38,7 +38,7 @@ void main() { test('color created with wildly out of bounds value', () { try { - final Color c = const Color(1 << 1000000); + const Color c = Color(1 << 1000000); final Paint p = new Paint(); p.color = c; } catch (e) { @@ -48,7 +48,7 @@ void main() { 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(new Color(123))); + expect(const Color(123), equals(Color(123))); // ignore: prefer_const_constructors 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)))); @@ -123,14 +123,14 @@ void main() { test('compute gray luminance', () { // Each color component is at 20%. - final Color lightGray = const Color(0xFF333333); + const Color lightGray = Color(0xFF333333); // Relative luminance's formula is just the linearized color value for gray. // ((0.2 + 0.055) / 1.055) ^ 2.4. expect(lightGray.computeLuminance(), equals(0.033104766570885055)); }); test('compute color luminance', () { - final Color brightRed = const Color(0xFFFF3B30); + const Color brightRed = Color(0xFFFF3B30); // 0.2126 * ((1.0 + 0.055) / 1.055) ^ 2.4 + // 0.7152 * ((0.23137254902 +0.055) / 1.055) ^ 2.4 + // 0.0722 * ((0.18823529411 + 0.055) / 1.055) ^ 2.4 diff --git a/testing/dart/encoding_test.dart b/testing/dart/encoding_test.dart index c8210d9b4e094..5b220b77916aa 100644 --- a/testing/dart/encoding_test.dart +++ b/testing/dart/encoding_test.dart @@ -1,11 +1,11 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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:ui'; -import 'dart:typed_data'; import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; import 'package:path/path.dart' as path; import 'package:test/test.dart'; @@ -20,14 +20,15 @@ void main() { group('Image.toByteData', () { group('RGBA format', () { test('works with simple image', () async { - ByteData data = await Square4x4Image.image.toByteData(); - expect(new Uint8List.view(data.buffer), Square4x4Image.bytes); + final Image image = await Square4x4Image.image; + final ByteData data = await image.toByteData(); + expect(Uint8List.view(data.buffer), Square4x4Image.bytes); }); test('converts grayscale images', () async { - Image image = await GrayscaleImage.load(); - ByteData data = await image.toByteData(); - Uint8List bytes = data.buffer.asUint8List(); + final Image image = await GrayscaleImage.load(); + final ByteData data = await image.toByteData(); + final Uint8List bytes = data.buffer.asUint8List(); expect(bytes, hasLength(16)); expect(bytes, GrayscaleImage.bytesAsRgba); }); @@ -35,15 +36,15 @@ void main() { group('Unmodified format', () { test('works with simple image', () async { - Image image = Square4x4Image.image; - ByteData data = await image.toByteData(format: ImageByteFormat.rawUnmodified); - expect(new Uint8List.view(data.buffer), Square4x4Image.bytes); + final Image image = await Square4x4Image.image; + final ByteData data = await image.toByteData(format: ImageByteFormat.rawUnmodified); + expect(Uint8List.view(data.buffer), Square4x4Image.bytes); }); test('works with grayscale images', () async { - Image image = await GrayscaleImage.load(); - ByteData data = await image.toByteData(format: ImageByteFormat.rawUnmodified); - Uint8List bytes = data.buffer.asUint8List(); + final Image image = await GrayscaleImage.load(); + final ByteData data = await image.toByteData(format: ImageByteFormat.rawUnmodified); + final Uint8List bytes = data.buffer.asUint8List(); expect(bytes, hasLength(4)); expect(bytes, GrayscaleImage.bytesUnmodified); }); @@ -51,46 +52,48 @@ void main() { group('PNG format', () { test('works with simple image', () async { - Image image = Square4x4Image.image; - ByteData data = await image.toByteData(format: ImageByteFormat.png); - List expected = await readFile('square.png'); - expect(new Uint8List.view(data.buffer), expected); + final Image image = await Square4x4Image.image; + final ByteData data = await image.toByteData(format: ImageByteFormat.png); + final List expected = await readFile('square.png'); + expect(Uint8List.view(data.buffer), expected); }); }); }); } class Square4x4Image { - static Image get image { - double width = _kWidth.toDouble(); - double radius = _kRadius.toDouble(); - double innerWidth = (_kWidth - 2 * _kRadius).toDouble(); + Square4x4Image._(); + + static Future get image async { + final double width = _kWidth.toDouble(); + final double radius = _kRadius.toDouble(); + final double innerWidth = (_kWidth - 2 * _kRadius).toDouble(); - PictureRecorder recorder = new PictureRecorder(); - Canvas canvas = - new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, width, width)); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = + Canvas(recorder, Rect.fromLTWH(0.0, 0.0, width, width)); - Paint black = new Paint() + final Paint black = Paint() ..strokeWidth = 1.0 ..color = _kBlack; - Paint green = new Paint() + final Paint green = Paint() ..strokeWidth = 1.0 ..color = _kGreen; - canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, width, width), black); + canvas.drawRect(Rect.fromLTWH(0.0, 0.0, width, width), black); canvas.drawRect( - new Rect.fromLTWH(radius, radius, innerWidth, innerWidth), green); - return recorder.endRecording().toImage(_kWidth, _kWidth); + Rect.fromLTWH(radius, radius, innerWidth, innerWidth), green); + return await recorder.endRecording().toImage(_kWidth, _kWidth); } static List get bytes { - int bytesPerChannel = 4; - List result = new List(_kWidth * _kWidth * bytesPerChannel); + const int bytesPerChannel = 4; + final List result = List(_kWidth * _kWidth * bytesPerChannel); - fillWithColor(Color color, int min, int max) { + void fillWithColor(Color color, int min, int max) { for (int i = min; i < max; i++) { for (int j = min; j < max; j++) { - int offset = i * bytesPerChannel + j * _kWidth * bytesPerChannel; + final int offset = i * bytesPerChannel + j * _kWidth * bytesPerChannel; result[offset] = color.red; result[offset + 1] = color.green; result[offset + 2] = color.blue; @@ -107,9 +110,11 @@ class Square4x4Image { } class GrayscaleImage { + GrayscaleImage._(); + static Future load() async { - Uint8List bytes = await readFile('4x4.png'); - Completer completer = new Completer(); + final Uint8List bytes = await readFile('4x4.png'); + final Completer completer = Completer(); decodeImageFromList(bytes, (Image image) => completer.complete(image)); return await completer.future; } @@ -126,7 +131,7 @@ class GrayscaleImage { static List get bytesUnmodified => [255, 127, 127, 0]; } -Future readFile(fileName) async { - final file = new File(path.join('flutter', 'testing', 'resources', fileName)); +Future readFile(String fileName) async { + final File file = File(path.join('flutter', 'testing', 'resources', fileName)); return await file.readAsBytes(); } diff --git a/testing/dart/geometry_test.dart b/testing/dart/geometry_test.dart index fc3ce17434233..20445b862ca4e 100644 --- a/testing/dart/geometry_test.dart +++ b/testing/dart/geometry_test.dart @@ -1,13 +1,48 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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:ui'; +import 'dart:math' as math show sqrt; import 'dart:math' show pi; +import 'dart:ui'; import 'package:test/test.dart'; void main() { + test('OffsetBase.>=', () { + expect(const Offset(0, 0), greaterThanOrEqualTo(const Offset(0, -1))); + expect(const Offset(0, 0), greaterThanOrEqualTo(const Offset(-1, 0))); + expect(const Offset(0, 0), greaterThanOrEqualTo(const Offset(-1, -1))); + expect(const Offset(0, 0), greaterThanOrEqualTo(const Offset(0, 0))); + expect(const Offset(0, 0), isNot(greaterThanOrEqualTo(const Offset(0, double.nan)))); + expect(const Offset(0, 0), isNot(greaterThanOrEqualTo(const Offset(double.nan, 0)))); + expect(const Offset(0, 0), isNot(greaterThanOrEqualTo(const Offset(10, -10)))); + }); + test('OffsetBase.<=', () { + expect(const Offset(0, 0), lessThanOrEqualTo(const Offset(0, 1))); + expect(const Offset(0, 0), lessThanOrEqualTo(const Offset(1, 0))); + expect(const Offset(0, 0), lessThanOrEqualTo(const Offset(0, 0))); + expect(const Offset(0, 0), isNot(lessThanOrEqualTo(const Offset(0, double.nan)))); + expect(const Offset(0, 0), isNot(lessThanOrEqualTo(const Offset(double.nan, 0)))); + expect(const Offset(0, 0), isNot(lessThanOrEqualTo(const Offset(10, -10)))); + }); + test('OffsetBase.>', () { + expect(const Offset(0, 0), greaterThan(const Offset(-1, -1))); + expect(const Offset(0, 0), isNot(greaterThan(const Offset(0, -1)))); + expect(const Offset(0, 0), isNot(greaterThan(const Offset(-1, 0)))); + expect(const Offset(0, 0), isNot(greaterThan(const Offset(double.nan, -1)))); + }); + test('OffsetBase.<', () { + expect(const Offset(0, 0), lessThan(const Offset(1, 1))); + expect(const Offset(0, 0), isNot(lessThan(const Offset(0, 1)))); + expect(const Offset(0, 0), isNot(lessThan(const Offset(1, 0)))); + expect(const Offset(0, 0), isNot(lessThan(const Offset(double.nan, 1)))); + }); + test('OffsetBase.==', () { + expect(const Offset(0, 0), equals(const Offset(0, 0))); + expect(const Offset(0, 0), isNot(equals(const Offset(1, 0)))); + expect(const Offset(0, 0), isNot(equals(const Offset(0, 1)))); + }); test('Offset.direction', () { expect(const Offset(0.0, 0.0).direction, 0.0); expect(const Offset(0.0, 1.0).direction, pi / 2.0); @@ -19,4 +54,40 @@ void main() { expect(const Offset(-1.0, 1.0).direction, pi * 3.0 / 4.0); expect(const Offset(-1.0, -1.0).direction, -pi * 3.0 / 4.0); }); + test('Offset.fromDirection', () { + expect(Offset.fromDirection(0.0, 0.0), const Offset(0.0, 0.0)); + expect(Offset.fromDirection(pi / 2.0).dx, closeTo(0.0, 1e-12)); // aah, floating point math. i love you so. + expect(Offset.fromDirection(pi / 2.0).dy, 1.0); + expect(Offset.fromDirection(-pi / 2.0).dx, closeTo(0.0, 1e-12)); + expect(Offset.fromDirection(-pi / 2.0).dy, -1.0); + expect(Offset.fromDirection(0.0), const Offset(1.0, 0.0)); + expect(Offset.fromDirection(pi / 4.0).dx, closeTo(1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(pi / 4.0).dy, closeTo(1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(-pi / 4.0).dx, closeTo(1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(-pi / 4.0).dy, closeTo(-1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(pi).dx, -1.0); + expect(Offset.fromDirection(pi).dy, closeTo(0.0, 1e-12)); + expect(Offset.fromDirection(pi * 3.0 / 4.0).dx, closeTo(-1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(pi * 3.0 / 4.0).dy, closeTo(1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(-pi * 3.0 / 4.0).dx, closeTo(-1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(-pi * 3.0 / 4.0).dy, closeTo(-1.0 / math.sqrt(2.0), 1e-12)); + expect(Offset.fromDirection(0.0, 2.0), const Offset(2.0, 0.0)); + expect(Offset.fromDirection(pi / 6, 2.0).dx, closeTo(math.sqrt(3.0), 1e-12)); + expect(Offset.fromDirection(pi / 6, 2.0).dy, closeTo(1.0, 1e-12)); + }); + test('Size.aspectRatio', () { + expect(const Size(0.0, 0.0).aspectRatio, 0.0); + expect(const Size(-0.0, 0.0).aspectRatio, 0.0); + expect(const Size(0.0, -0.0).aspectRatio, 0.0); + expect(const Size(-0.0, -0.0).aspectRatio, 0.0); + expect(const Size(0.0, 1.0).aspectRatio, 0.0); + expect(const Size(0.0, -1.0).aspectRatio, -0.0); + expect(const Size(1.0, 0.0).aspectRatio, double.infinity); + expect(const Size(1.0, 1.0).aspectRatio, 1.0); + expect(const Size(1.0, -1.0).aspectRatio, -1.0); + expect(const Size(-1.0, 0.0).aspectRatio, -double.infinity); + expect(const Size(-1.0, 1.0).aspectRatio, -1.0); + expect(const Size(-1.0, -1.0).aspectRatio, 1.0); + expect(const Size(3.0, 4.0).aspectRatio, 3.0 / 4.0); + }); } diff --git a/testing/dart/gradient_test.dart b/testing/dart/gradient_test.dart index 881b70fec558d..5e1bd6fd6ff13 100644 --- a/testing/dart/gradient_test.dart +++ b/testing/dart/gradient_test.dart @@ -1,8 +1,7 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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' show Float64List; import 'dart:ui'; import 'package:test/test.dart'; @@ -66,7 +65,7 @@ void main() { Offset.zero, 1.0, ), - throwsA(const isInstanceOf()), + throwsA(const TypeMatcher()), ); }); } diff --git a/testing/dart/isolate_name_server_test.dart b/testing/dart/isolate_name_server_test.dart index 37f6349d579a7..5a02d9722dd4d 100644 --- a/testing/dart/isolate_name_server_test.dart +++ b/testing/dart/isolate_name_server_test.dart @@ -1,25 +1,24 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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:isolate'; import 'dart:ui'; import 'package:test/test.dart'; -const kPortName = 'foobar'; -const kErrorCode = -1; -const kStartCode = 0; -const kCloseCode = 1; -const kDeletedCode = 2; +const String kPortName = 'foobar'; +const int kErrorCode = -1; +const int kStartCode = 0; +const int kCloseCode = 1; +const int kDeletedCode = 2; void isolateSpawnEntrypoint(SendPort port) { - sendHelper(int code, [String message = '']) { + void sendHelper(int code, [String message = '']) { port.send([code, message]); } - SendPort shared = IsolateNameServer.lookupPortByName(kPortName); + final SendPort shared = IsolateNameServer.lookupPortByName(kPortName); if (shared == null) { sendHelper(kErrorCode, 'Could not find port: $kPortName'); return; @@ -35,7 +34,7 @@ void isolateSpawnEntrypoint(SendPort port) { // send another message to ensure we don't crash. shared.send('garbage'); - bool result = IsolateNameServer.removePortNameMapping(kPortName); + final bool result = IsolateNameServer.removePortNameMapping(kPortName); if (result) { sendHelper(kDeletedCode); } else { @@ -55,14 +54,14 @@ void main() { expect(IsolateNameServer.removePortNameMapping(kPortName), isFalse); // Register a SendPort. - final receivePort = new ReceivePort(); - final sendPort = receivePort.sendPort; + final ReceivePort receivePort = ReceivePort(); + final SendPort sendPort = receivePort.sendPort; expect(IsolateNameServer.registerPortWithName(sendPort, kPortName), isTrue); expect(IsolateNameServer.lookupPortByName(kPortName), sendPort); // Check we can't register the same name twice. - final receivePort2 = new ReceivePort(); - final sendPort2 = receivePort2.sendPort; + final ReceivePort receivePort2 = ReceivePort(); + final SendPort sendPort2 = receivePort2.sendPort; expect( IsolateNameServer.registerPortWithName(sendPort2, kPortName), isFalse); expect(IsolateNameServer.lookupPortByName(kPortName), sendPort); @@ -83,13 +82,13 @@ void main() { test('isolate name server multi-isolate', () async { // Register our send port with the name server. - final receivePort = new ReceivePort(); - final sendPort = receivePort.sendPort; + final ReceivePort receivePort = ReceivePort(); + final SendPort sendPort = receivePort.sendPort; expect(IsolateNameServer.registerPortWithName(sendPort, kPortName), isTrue); // Test driver. - final testReceivePort = new ReceivePort(); - testReceivePort.listen(expectAsync1((response) { + final ReceivePort testReceivePort = ReceivePort(); + testReceivePort.listen(expectAsync1((dynamic response) { final int code = response[0]; final String message = response[1]; switch (code) { @@ -110,7 +109,7 @@ void main() { } }, count: 3)); - receivePort.listen(expectAsync1((message) { + receivePort.listen(expectAsync1((dynamic message) { // If we don't get this message, we timeout and fail. expect(message, kPortName); })); diff --git a/testing/dart/locale_test.dart b/testing/dart/locale_test.dart index 60a7bf99fe2af..576df68af7064 100644 --- a/testing/dart/locale_test.dart +++ b/testing/dart/locale_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -8,15 +8,41 @@ import 'package:test/test.dart'; void main() { test('Locale', () { - final Null $null = null; + const Null $null = null; expect(const Locale('en').toString(), 'en'); - expect(const Locale('en'), new Locale('en', $null)); - expect(const Locale('en').hashCode, new Locale('en', $null).hashCode); - expect(const Locale('en'), isNot(new Locale('en', ''))); - expect(const Locale('en').hashCode, isNot(new Locale('en', '').hashCode)); + expect(const Locale('en'), const Locale('en', $null)); + expect(const Locale('en').hashCode, const Locale('en', $null).hashCode); + expect(const Locale('en'), isNot(const Locale('en', ''))); + expect(const Locale('en').hashCode, isNot(const Locale('en', '').hashCode)); expect(const Locale('en', 'US').toString(), 'en_US'); expect(const Locale('iw').toString(), 'he'); expect(const Locale('iw', 'DD').toString(), 'he_DE'); expect(const Locale('iw', 'DD'), const Locale('he', 'DE')); }); + + test('Locale.fromSubtags', () { + expect(const Locale.fromSubtags().languageCode, 'und'); + expect(const Locale.fromSubtags().scriptCode, null); + expect(const Locale.fromSubtags().countryCode, null); + + expect(const Locale.fromSubtags(languageCode: 'en').toString(), 'en'); + expect(const Locale.fromSubtags(languageCode: 'en').languageCode, 'en'); + expect(const Locale.fromSubtags(scriptCode: 'Latn').toString(), 'und_Latn'); + expect(const Locale.fromSubtags(scriptCode: 'Latn').scriptCode, 'Latn'); + expect(const Locale.fromSubtags(countryCode: 'US').toString(), 'und_US'); + expect(const Locale.fromSubtags(countryCode: 'US').countryCode, 'US'); + + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').toString(), 'es_419'); + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').languageCode, 'es'); + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').countryCode, '419'); + + expect(const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN').toString(), 'zh_Hans_CN'); + }); + + test('Locale equality', () { + expect(const Locale.fromSubtags(languageCode: 'en'), + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn'))); + expect(const Locale.fromSubtags(languageCode: 'en').hashCode, + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode)); + }); } diff --git a/testing/dart/paragraph_builder_test.dart b/testing/dart/paragraph_builder_test.dart index 0d5afc9e0bfa7..2810d73113356 100644 --- a/testing/dart/paragraph_builder_test.dart +++ b/testing/dart/paragraph_builder_test.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -13,8 +13,15 @@ void main() { final Paragraph paragraph = builder.build(); expect(paragraph, isNotNull); - paragraph.layout(ParagraphConstraints(width: 800.0)); + paragraph.layout(const ParagraphConstraints(width: 800.0)); expect(paragraph.width, isNonZero); expect(paragraph.height, isNonZero); }); + + test('PushStyle should not segfault after build()', () { + final ParagraphBuilder paragraphBuilder = + ParagraphBuilder(ParagraphStyle()); + paragraphBuilder.build(); + paragraphBuilder.pushStyle(TextStyle()); + }); } diff --git a/testing/dart/path_test.dart b/testing/dart/path_test.dart index 732f22304892b..6a0c99f1aefad 100644 --- a/testing/dart/path_test.dart +++ b/testing/dart/path_test.dart @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -93,7 +93,7 @@ void main() { test('transformation tests', () { final Rect bounds = new Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); final Path p = new Path()..addRect(bounds); - final Float64List scaleMatrix = new Float64List.fromList([ + final Float64List scaleMatrix = Float64List.fromList([ 2.5, 0.0, 0.0, 0.0, // first col 0.0, 0.5, 0.0, 0.0, // second col 0.0, 0.0, 1.0, 0.0, // third col @@ -178,7 +178,7 @@ void main() { final double midPoint = simpleMetricsDiagonal.iterator.current.length / 2; final Tangent posTanDiagonal = simpleMetricsDiagonal.iterator.current.getTangentForOffset(midPoint); - expect(posTanDiagonal.position, equals(new Offset(5.0, 5.0))); + expect(posTanDiagonal.position, equals(const Offset(5.0, 5.0))); expect(posTanDiagonal.angle, closeTo(-0.7853981633974483, .00001)); // ~45 degrees @@ -199,4 +199,22 @@ void main() { expect(multiContourMetric.iterator.moveNext(), isFalse); expect(multiContourMetric.iterator.current, isNull); }); + + test('PathMetrics can remember lengths and isClosed', () { + final Path path = Path()..lineTo(0, 10) + ..close() + ..moveTo(0, 15) + ..lineTo(10, 15); + final List metrics = path.computeMetrics().toList(); + expect(metrics.length, 2); + print(metrics); + expect(metrics[0].length, 20); + expect(metrics[0].isClosed, true); + expect(() => metrics[0].getTangentForOffset(4.0), isNotNull); + expect(() => metrics[0].extractPath(4.0, 10.0), isNotNull); + expect(metrics[1].length, 10); + expect(metrics[1].isClosed, false); + expect(() => metrics[1].getTangentForOffset(4.0), isNotNull); + expect(() => metrics[1].extractPath(4.0, 10.0), isNotNull); + }); } diff --git a/testing/dart/plugin_utilities_test.dart b/testing/dart/plugin_utilities_test.dart index 89f43824bc2dc..057b17cf76a4c 100644 --- a/testing/dart/plugin_utilities_test.dart +++ b/testing/dart/plugin_utilities_test.dart @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,7 +6,7 @@ import 'dart:ui'; import 'package:test/test.dart'; -String top() => "top"; +String top() => 'top'; class Foo { const Foo(); @@ -19,25 +19,29 @@ const Foo foo = const Foo(); void main() { test('PluginUtilities Callback Handles', () { // Top level callback. - final hTop = PluginUtilities.getCallbackHandle(top); + final CallbackHandle hTop = PluginUtilities.getCallbackHandle(top); expect(hTop, isNotNull); expect(hTop, isNot(0)); expect(PluginUtilities.getCallbackHandle(top), hTop); - final topClosure = PluginUtilities.getCallbackFromHandle(hTop); + final Function topClosure = PluginUtilities.getCallbackFromHandle(hTop); expect(topClosure, isNotNull); - expect(topClosure(), "top"); + expect(topClosure(), 'top'); - // Static method callback - final hGetInt = PluginUtilities.getCallbackHandle(Foo.getInt); + // Static method callback. + final CallbackHandle hGetInt = PluginUtilities.getCallbackHandle(Foo.getInt); expect(hGetInt, isNotNull); expect(hGetInt, isNot(0)); expect(PluginUtilities.getCallbackHandle(Foo.getInt), hGetInt); - final getIntClosure = PluginUtilities.getCallbackFromHandle(hGetInt); + final Function getIntClosure = PluginUtilities.getCallbackFromHandle(hGetInt); expect(getIntClosure, isNotNull); expect(getIntClosure(), 1); // Instance method callbacks cannot be looked up. - final foo = new Foo(); + const Foo foo = Foo(); expect(PluginUtilities.getCallbackHandle(foo.getDouble), isNull); + + // Anonymous closures cannot be looked up. + final Function anon = (int a, int b) => a + b; + expect(PluginUtilities.getCallbackHandle(anon), isNull); }); } diff --git a/testing/dart/pubspec.yaml b/testing/dart/pubspec.yaml index ec3ea88b2ac6d..1c06c331bf831 100644 --- a/testing/dart/pubspec.yaml +++ b/testing/dart/pubspec.yaml @@ -2,3 +2,9 @@ name: engine_tests dependencies: test: 1.3.0 path: 1.6.2 + +dependency_overrides: + sky_engine: + path: ../../../out/host_debug_unopt/gen/dart-pkg/sky_engine + sky_services: + path: ../../../out/host_debug_unopt/gen/dart-pkg/sky_services diff --git a/testing/dart/rect_test.dart b/testing/dart/rect_test.dart index c075826559714..b392932c662ea 100644 --- a/testing/dart/rect_test.dart +++ b/testing/dart/rect_test.dart @@ -1,4 +1,4 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,16 +7,16 @@ import 'dart:ui'; import 'package:test/test.dart'; void main() { - test("rect accessors", () { - final Rect r = new Rect.fromLTRB(1.0, 3.0, 5.0, 7.0); + test('rect accessors', () { + final Rect r = Rect.fromLTRB(1.0, 3.0, 5.0, 7.0); expect(r.left, equals(1.0)); expect(r.top, equals(3.0)); expect(r.right, equals(5.0)); expect(r.bottom, equals(7.0)); }); - test("rect created by width and height", () { - final Rect r = new Rect.fromLTWH(1.0, 3.0, 5.0, 7.0); + test('rect created by width and height', () { + final Rect r = Rect.fromLTWH(1.0, 3.0, 5.0, 7.0); expect(r.left, equals(1.0)); expect(r.top, equals(3.0)); expect(r.right, equals(6.0)); @@ -25,9 +25,9 @@ void main() { expect(r.longestSide, equals(7.0)); }); - test("rect intersection", () { - final Rect r1 = new Rect.fromLTRB(0.0, 0.0, 100.0, 100.0); - final Rect r2 = new Rect.fromLTRB(50.0, 50.0, 200.0, 200.0); + test('rect intersection', () { + final Rect r1 = Rect.fromLTRB(0.0, 0.0, 100.0, 100.0); + final Rect r2 = Rect.fromLTRB(50.0, 50.0, 200.0, 200.0); final Rect r3 = r1.intersect(r2); expect(r3.left, equals(50.0)); expect(r3.top, equals(50.0)); @@ -37,9 +37,9 @@ void main() { expect(r4, equals(r3)); }); - test("rect expandToInclude overlapping rects", () { - final Rect r1 = new Rect.fromLTRB(0.0, 0.0, 100.0, 100.0); - final Rect r2 = new Rect.fromLTRB(50.0, 50.0, 200.0, 200.0); + test('rect expandToInclude overlapping rects', () { + final Rect r1 = Rect.fromLTRB(0.0, 0.0, 100.0, 100.0); + final Rect r2 = Rect.fromLTRB(50.0, 50.0, 200.0, 200.0); final Rect r3 = r1.expandToInclude(r2); expect(r3.left, equals(0.0)); expect(r3.top, equals(0.0)); @@ -49,9 +49,9 @@ void main() { expect(r4, equals(r3)); }); - test("rect expandToInclude crossing rects", () { - final Rect r1 = new Rect.fromLTRB(50.0, 0.0, 50.0, 200.0); - final Rect r2 = new Rect.fromLTRB(0.0, 50.0, 200.0, 50.0); + test('rect expandToInclude crossing rects', () { + final Rect r1 = Rect.fromLTRB(50.0, 0.0, 50.0, 200.0); + final Rect r2 = Rect.fromLTRB(0.0, 50.0, 200.0, 50.0); final Rect r3 = r1.expandToInclude(r2); expect(r3.left, equals(0.0)); expect(r3.top, equals(0.0)); @@ -61,17 +61,17 @@ void main() { expect(r4, equals(r3)); }); - test("size created from doubles", () { - final Size size = new Size(5.0, 7.0); + test('size created from doubles', () { + const Size size = Size(5.0, 7.0); expect(size.width, equals(5.0)); expect(size.height, equals(7.0)); expect(size.shortestSide, equals(5.0)); expect(size.longestSide, equals(7.0)); }); - test("rounded rect created from rect and radii", () { - final Rect baseRect = new Rect.fromLTWH(1.0, 3.0, 5.0, 7.0); - final RRect r = new RRect.fromRectXY(baseRect, 1.0, 1.0); + test('rounded rect created from rect and radii', () { + final Rect baseRect = Rect.fromLTWH(1.0, 3.0, 5.0, 7.0); + final RRect r = RRect.fromRectXY(baseRect, 1.0, 1.0); expect(r.left, equals(1.0)); expect(r.top, equals(3.0)); expect(r.right, equals(6.0)); diff --git a/testing/dart/versions_test.dart b/testing/dart/versions_test.dart new file mode 100644 index 0000000000000..91935c76a58bc --- /dev/null +++ b/testing/dart/versions_test.dart @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:test/test.dart'; + +bool _isNotEmpty(String s) { + if (s == null || s.isEmpty) { + return false; + } else { + return true; + } +} + +void main() { + test('dartVersion should not be empty', () { + final String dartVersion = versions.dartVersion; + expect(_isNotEmpty(dartVersion), equals(true)); + }); + + test('skiaVersion should not be empty', () { + final String skiaVersion = versions.skiaVersion; + expect(_isNotEmpty(skiaVersion), equals(true)); + }); + + test('flutterEngineVersion should not be empty', () { + final String flutterEngineVersion = versions.flutterEngineVersion; + expect(_isNotEmpty(flutterEngineVersion), equals(true)); + }); +} diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index b56aaeee4d1e9..3925594fd3884 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,15 +6,14 @@ library dart.ui; import 'dart:async'; -import 'dart:io'; -import 'dart:typed_data'; +// this needs to be imported because painting.dart expects it this way +import 'dart:collection' as collection; import 'dart:convert'; import 'dart:developer' as developer; import 'dart:math' as math; -import 'dart:nativewrappers'; +import 'dart:nativewrappers'; // ignore: unused_import +import 'dart:typed_data'; -// this needs to be imported because painting.dart expects it this way -import 'dart:collection' as collection; import 'package:test/test.dart'; @@ -67,6 +66,12 @@ void main() { window.onTextScaleFactorChanged = originalOnTextScaleFactorChanged; }); + test('updateUserSettings can handle an empty object', () { + // this should now throw. + _updateUserSettingsData('{}'); + expect(true, equals(true)); + }); + test('onMetricsChanged preserves callback zone', () { Zone innerZone; Zone runZone; @@ -100,7 +105,7 @@ void main() { }; }); - _updateLocale('en', 'US', '', ''); + _updateLocales(['en', 'US', '', '']); expect(runZone, isNotNull); expect(runZone, same(innerZone)); expect(locale, equals(const Locale('en', 'US'))); @@ -241,5 +246,25 @@ void main() { expect(runZone, same(innerZone)); expect(textScaleFactor, equals(0.5)); }); + + test('onThemeBrightnessMode preserves callback zone', () { + Zone innerZone; + Zone runZone; + Brightness platformBrightness; + + runZoned(() { + innerZone = Zone.current; + window.onPlatformBrightnessChanged = () { + runZone = Zone.current; + platformBrightness = window.platformBrightness; + }; + }); + + window.onPlatformBrightnessChanged(); + _updatePlatformBrightness('dark'); + expect(runZone, isNotNull); + expect(runZone, same(innerZone)); + expect(platformBrightness, equals(Brightness.dark)); + }); }); } diff --git a/testing/dart/window_test.dart b/testing/dart/window_test.dart index 5605355c0c390..e9b20ebd327c9 100644 --- a/testing/dart/window_test.dart +++ b/testing/dart/window_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,7 +12,7 @@ void main() { test('window.sendPlatformMessage preserves callback zone', () { runZoned(() { final Zone innerZone = Zone.current; - window.sendPlatformMessage('test', new ByteData.view(new Uint8List(0).buffer), expectAsync((ByteData data) { + window.sendPlatformMessage('test', new ByteData.view(new Uint8List(0).buffer), expectAsync1((ByteData data) { final Zone runZone = Zone.current; expect(runZone, isNotNull); expect(runZone, same(innerZone)); diff --git a/testing/resources/performance_overlay_gold.png b/testing/resources/performance_overlay_gold.png new file mode 100644 index 0000000000000..119551f705793 Binary files /dev/null and b/testing/resources/performance_overlay_gold.png differ diff --git a/testing/run_all_unittests.cc b/testing/run_all_unittests.cc index d300edf026f17..1fa0019be22b4 100644 --- a/testing/run_all_unittests.cc +++ b/testing/run_all_unittests.cc @@ -1,4 +1,4 @@ -// Copyright 2016 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/run_tests.sh b/testing/run_tests.sh index 6c0c79f523cf8..ea491b4bb720a 100755 --- a/testing/run_tests.sh +++ b/testing/run_tests.sh @@ -2,23 +2,81 @@ set -o pipefail -e; -out/host_debug_unopt/fml_unittests -out/host_debug_unopt/synchronization_unittests +BUILDROOT_DIR="$(pwd)" +if [[ "$BUILDROOT_DIR" != */src ]]; then + if [[ "$BUILDROOT_DIR" != */src/* ]]; then + echo "Unable to determine build root. Exiting." + exit 1 + fi + BUILDROOT_DIR="${BUILDROOT_DIR%/src/*}/src" +fi +echo "Using build root: $BUILDROOT_DIR" -pushd flutter/testing/dart -pub get +OUT_DIR="$BUILDROOT_DIR/out" +HOST_DIR="$OUT_DIR/${1:-host_debug_unopt}" + +# Check a Dart SDK has been built. +if [[ ! -d "$HOST_DIR/dart-sdk" ]]; then + echo "Built Dart SDK not found at $HOST_DIR/dart-sdk. Exiting." + exit 1 +fi + +# Switch to buildroot dir. Some tests assume paths relative to buildroot. +cd "$BUILDROOT_DIR" + +# TODO(dnfield): Re-enable this when the upstream Dart changes that make it not be flaky land. +# $HOST_DIR/embedder_unittests +echo "Running flow_unittests..." +"$HOST_DIR/flow_unittests" + +echo "Running fml_unittests..." +"$HOST_DIR/fml_unittests" --gtest_filter="-*TimeSensitiveTest*" + +echo "Running runtime_unittests..." +"$HOST_DIR/runtime_unittests" + +echo "Running shell_unittests..." +"$HOST_DIR/shell_unittests" + +echo "Running synchronization_unittests..." +"$HOST_DIR/synchronization_unittests" + +echo "Running txt_unittests..." +"$HOST_DIR/txt_unittests" --font-directory="$BUILDROOT_DIR/flutter/third_party/txt/third_party/fonts" + +# Build flutter/sky/packages. +# +# flutter/testing/dart/pubspec.yaml contains harcoded path deps to +# host_debug_unopt packages. +"$BUILDROOT_DIR/flutter/tools/gn" --unoptimized +ninja -C $OUT_DIR/host_debug_unopt flutter/sky/packages + +# Fetch Dart test dependencies. +pushd "$BUILDROOT_DIR/flutter/testing/dart" +"$HOST_DIR/dart-sdk/bin/pub" get popd run_test () { - out/host_debug_unopt/dart out/host_debug_unopt/gen/frontend_server.dart.snapshot --sdk-root out/host_debug_unopt/flutter_patched_sdk --incremental --strong --target=flutter --packages flutter/testing/dart/.packages --output-dill out/host_debug_unopt/engine_test.dill $1 - out/host_debug_unopt/flutter_tester --disable-observatory --use-test-fonts out/host_debug_unopt/engine_test.dill + "$HOST_DIR/dart" $HOST_DIR/gen/frontend_server.dart.snapshot \ + --sdk-root $HOST_DIR/flutter_patched_sdk \ + --incremental \ + --strong \ + --target=flutter \ + --packages flutter/testing/dart/.packages \ + --output-dill $HOST_DIR/engine_test.dill \ + $1 + + "$HOST_DIR/flutter_tester" \ + --disable-observatory \ + --use-test-fonts \ + "$HOST_DIR/engine_test.dill" } # Verify that a failing test returns a failure code. -! run_test flutter/testing/fail_test.dart +! run_test "$BUILDROOT_DIR/flutter/testing/smoke_test_failure/fail_test.dart" -for TEST_SCRIPT in flutter/testing/dart/*.dart; do - run_test $TEST_SCRIPT +for TEST_SCRIPT in "$BUILDROOT_DIR"/flutter/testing/dart/*.dart; do + run_test "$TEST_SCRIPT" done pushd flutter diff --git a/testing/fail_test.dart b/testing/smoke_test_failure/fail_test.dart similarity index 80% rename from testing/fail_test.dart rename to testing/smoke_test_failure/fail_test.dart index 6c642710fe914..5d88caaaa5d47 100644 --- a/testing/fail_test.dart +++ b/testing/smoke_test_failure/fail_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/smoke_test_failure/pubspec.yaml b/testing/smoke_test_failure/pubspec.yaml new file mode 100644 index 0000000000000..9eb330ef6f4e7 --- /dev/null +++ b/testing/smoke_test_failure/pubspec.yaml @@ -0,0 +1,4 @@ +name: smoke_test_failure +dependencies: + test: 1.3.0 + path: 1.6.2 diff --git a/testing/symbols/pubspec.yaml b/testing/symbols/pubspec.yaml index cfea098886fd4..a45a4a0b79afb 100644 --- a/testing/symbols/pubspec.yaml +++ b/testing/symbols/pubspec.yaml @@ -1,3 +1,4 @@ name: verify_exported dependencies: path: 1.6.2 + collection: 1.14.11 diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 37ff8f5be5f6a..921f90c0d01f5 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:collection/collection.dart' show MapEquality; // This script verifies that the release binaries only export the expected // symbols. @@ -13,10 +14,18 @@ import 'package:path/path.dart' as p; // Symbols from the Flutter namespace. These are either of type // "(__DATA,__common)" or "(__DATA,__objc_data)". -/// Takes the path to the out directory as argument. +/// Takes the path to the out directory as the first argument, and the path to +/// the buildtools directory as the second argument. +/// +/// If the second argument is not specified, it is assumed that it is the parent +/// of the out directory (for backwards compatibility). void main(List arguments) { - assert(arguments.length == 1); + assert(arguments.length == 2 || arguments.length == 1); final String outPath = arguments.first; + final String buildToolsPath = arguments.length == 1 + ? p.join(p.dirname(outPath), 'buildtools') + : arguments[1]; + String platform; if (Platform.isLinux) { platform = 'linux-x64'; @@ -25,12 +34,12 @@ void main(List arguments) { } else { throw new UnimplementedError('Script only support running on Linux or MacOS.'); } - final String nmPath = p.join(p.dirname(outPath), 'buildtools', platform, 'clang', 'bin', 'llvm-nm'); + final String nmPath = p.join(buildToolsPath, platform, 'clang', 'bin', 'llvm-nm'); assert(new Directory(outPath).existsSync()); final Iterable releaseBuilds = new Directory(outPath).listSync() .where((FileSystemEntity entity) => entity is Directory) - .map((FileSystemEntity dir) => p.basename(dir.path)) + .map((FileSystemEntity dir) => p.basename(dir.path)) .where((String s) => s.contains('_release')); final Iterable iosReleaseBuilds = releaseBuilds @@ -60,7 +69,7 @@ int _checkIos(String outPath, String nmPath, Iterable builds) { continue; } final Iterable unexpectedEntries = NmEntry.parse(nmResult.stdout).where((NmEntry entry) { - return !((entry.type == '(__DATA,__common)' && entry.name.startsWith('_Flutter')) + return !(((entry.type == '(__DATA,__common)' || entry.type == '(__DATA,__const)') && entry.name.startsWith('_Flutter')) || (entry.type == '(__DATA,__objc_data)' && (entry.name.startsWith('_OBJC_METACLASS_\$_Flutter') || entry.name.startsWith('_OBJC_CLASS_\$_Flutter')))); }); @@ -92,16 +101,19 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { continue; } final Iterable entries = NmEntry.parse(nmResult.stdout); - if (entries.isEmpty) { - print('ERROR: $libFlutter exports no symbol'); - print(' Expected exactly one symbol "JNI_OnLoad" of type "T", but got none.'); - failures++; - } else if (entries.length > 1 || entries.first.type != 'T' || entries.first.name != 'JNI_OnLoad') { - print('ERROR: $libFlutter exports unexpected symbols.'); - print(' Expected exactly one symbol "JNI_OnLoad" of type "T", but got instead:'); - print(entries.fold('', (String previous, NmEntry entry) { - return '${previous == '' ? '' : '$previous\n'} ${entry.type} ${entry.name}'; - })); + final Map entryMap = Map.fromIterable( + entries, + key: (entry) => entry.name, + value: (entry) => entry.type); + final Map expectedSymbols = { + 'JNI_OnLoad': 'T', + '_binary_icudtl_dat_size': 'A', + '_binary_icudtl_dat_start': 'D', + }; + if (!MapEquality().equals(entryMap, expectedSymbols)) { + print('ERROR: $libFlutter exports the wrong symbols'); + print(' Expected $expectedSymbols'); + print(' Library has $entryMap.'); failures++; } else { print('OK: $libFlutter'); diff --git a/testing/testing.cc b/testing/testing.cc index 835ce4fe05684..93e135e7ab43d 100644 --- a/testing/testing.cc +++ b/testing/testing.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Fuchsia Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/testing.gni b/testing/testing.gni index a58876c38df6c..8d01b6810c044 100644 --- a/testing/testing.gni +++ b/testing/testing.gni @@ -1,19 +1,41 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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_root/common/config.gni") +import("//build/compiled_action.gni") +import("//third_party/dart/build/dart/dart_action.gni") + +# Builds test fixtures for a unit test. +# +# Generates a directory structure containing an assets directory and the Dart +# code to execute compiled to kernel. Generates: +# * an asserts directory at `$target_name/assets`. +# * a C++ implementation for `flutter/testing/testing.h`, defining +# `testing::GetFixturesPath()`, which returns the assets path. +# * a kernel snapshot at `$target_name/assets/$kernel_out`. +# +# Rule attributes: +# * fixtures: list of Dart files, including a main entrypoint. +# * kernel_out: the output path, relative to the assets directory, of the +# generated kernel snapshot. By default, `kernel_blob.bin`. template("test_fixtures") { testonly = true assert(defined(invoker.fixtures), "Test fixtures must be specified.") + if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { + kernel_out = "kernel_snapshot.dill" + } else { + kernel_out = "kernel_blob.bin" + } + if (defined(invoker.kernel_out)) { + kernel_out = invoker.kernel_out + } - fixtures_name_target_name = target_name + "_gen_fixtures_name" - fixtures_source_set_target_name = target_name + "_gen_fixtures_source_set" - fixtures_copy_target_name = target_name + "_copy_fixtures" - - fixtures_location = "$target_gen_dir/fixtures" - fixtures_location_file = "$target_gen_dir/test_fixtures_location.cc" + fixtures_location = "$target_gen_dir/$target_name/assets" + fixtures_location_file = "$target_gen_dir/$target_name/test_fixtures_location.cc" + fixtures_name_target_name = target_name + "_gen_fixtures_name" action(fixtures_name_target_name) { script = "$flutter_root/testing/build/gen_fixtures_location_symbol.py" @@ -29,7 +51,9 @@ template("test_fixtures") { ] } + fixtures_source_set_target_name = target_name + "_gen_fixtures_source_set" source_set(fixtures_source_set_target_name) { + testonly = true sources = [ fixtures_location_file, ] @@ -39,17 +63,84 @@ template("test_fixtures") { ] } - copy(fixtures_copy_target_name) { - sources = invoker.fixtures + fixtures_kernel_target_name = target_name + "_kernel" + dart_action(fixtures_kernel_target_name) { + testonly = true + script = "$root_out_dir/frontend_server.dart.snapshot" + + fixture_paths = [] + foreach(fixture, invoker.fixtures) { + fixture_paths += [ rebase_path(fixture) ] + } + inputs = fixture_paths + outputs = ["$fixtures_location/$kernel_out"] + + deps = [ + "//third_party/dart/utils/kernel-service:frontend_server" + ] + + if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { + args = [ + "--sdk-root", rebase_path("$root_out_dir/flutter_patched_sdk"), + "--strong", + "--target=flutter", + "--aot", + "--tfa", + "-Ddart.vm.product=true", + "--output-dill", rebase_path("$fixtures_location/$kernel_out"), + ] + fixture_paths + + deps += [ "//flutter/lib/snapshot:strong_platform" ] + } else { + args = [ + "--sdk-root", rebase_path("$root_out_dir/flutter_patched_sdk"), + "--target", "flutter", + "--output-dill", rebase_path("$fixtures_location/$kernel_out"), + ] + fixture_paths + } + } + + fixtures_aot_target_name = target_name + "_aot" + compiled_action(fixtures_aot_target_name) { + testonly = true + tool = "//third_party/dart/runtime/bin:gen_snapshot" + + inputs = [ + "$fixtures_location/$kernel_out" + ] + outputs = [ - "$fixtures_location/{{source_file_part}}", + "$fixtures_location/vm_snapshot_data", + "$fixtures_location/vm_snapshot_instr", + "$fixtures_location/isolate_snapshot_data", + "$fixtures_location/isolate_snapshot_instr", + ] + + args = [ + "--causal_async_stacks", + "--deterministic", + "--snapshot_kind=app-aot-blobs", + "--vm_snapshot_data=" + rebase_path("$fixtures_location/vm_snapshot_data"), + "--vm_snapshot_instructions=" + rebase_path("$fixtures_location/vm_snapshot_instr"), + "--isolate_snapshot_data=" + rebase_path("$fixtures_location/isolate_snapshot_data"), + "--isolate_snapshot_instructions=" + rebase_path("$fixtures_location/isolate_snapshot_instr"), + rebase_path("$fixtures_location/$kernel_out") + ] + + deps = [ + ":$fixtures_kernel_target_name", ] } group(target_name) { + testonly = true deps = [ - ":$fixtures_copy_target_name", ":$fixtures_source_set_target_name", ] + if (flutter_runtime_mode == "profile" || flutter_runtime_mode == "release") { + deps += [ ":$fixtures_aot_target_name" ] + } else { + deps += [ ":$fixtures_kernel_target_name" ] + } } } diff --git a/testing/testing.h b/testing/testing.h index 2377ce5114917..dac2d229b9169 100644 --- a/testing/testing.h +++ b/testing/testing.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/thread_test.cc b/testing/thread_test.cc index 1f4c1462bf387..513dbfd1a7eef 100644 --- a/testing/thread_test.cc +++ b/testing/thread_test.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/testing/thread_test.h b/testing/thread_test.h index 131aa05273f54..8b4487c1b791f 100644 --- a/testing/thread_test.h +++ b/testing/thread_test.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter 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/third_party/bsdiff/LICENSE b/third_party/bsdiff/LICENSE new file mode 100644 index 0000000000000..d04a52996551e --- /dev/null +++ b/third_party/bsdiff/LICENSE @@ -0,0 +1,23 @@ +Copyright 2003-2005 Colin Percival. 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 THE AUTHOR ``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 BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/bsdiff/README.md b/third_party/bsdiff/README.md new file mode 100644 index 0000000000000..79aac03d0fc2f --- /dev/null +++ b/third_party/bsdiff/README.md @@ -0,0 +1,12 @@ +# BSDiff + +Binary diff/patch algorithm based on +[bsdiff 4.3](http://www.daemonology.net/bsdiff/) by Colin Percival. +It's very effective at compressesing incremental changes in binaries. + +This implementation has the following differences from Colin's code, +to make it easier to read and apply patches in Java and Objective C: + +* Using gzip instead of bzip2 because gzip is included in JDK by default +* Using big- instead of little-endian serialization to simplify Java code +* Using two's complement instead of high-bit coding for negatives numbers diff --git a/third_party/bsdiff/io/flutter/util/BSDiff.java b/third_party/bsdiff/io/flutter/util/BSDiff.java new file mode 100644 index 0000000000000..135882033cbc8 --- /dev/null +++ b/third_party/bsdiff/io/flutter/util/BSDiff.java @@ -0,0 +1,91 @@ +// Copyright 2003-2005 Colin Percival. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.util; + +import java.io.*; +import java.util.zip.GZIPInputStream; + +/** + * This is a Java port of algorithm from Flutter framework bsdiff.dart. + * Note that this port uses 32-bit ints, which limits data size to 2GB. + **/ +public abstract class BSDiff { + public static byte[] bspatch(byte[] olddata, byte[] diffdata) throws IOException { + InputStream in = new ByteArrayInputStream(diffdata, 0, diffdata.length); + DataInputStream header = new DataInputStream(in); + + byte[] magic = new byte[8]; + header.read(magic); + if (!new String(magic).equals("BZDIFF40")) { + throw new IOException("Invalid magic"); + } + + int ctrllen = (int) header.readLong(); + int datalen = (int) header.readLong(); + int newsize = (int) header.readLong(); + header.close(); + + in = new ByteArrayInputStream(diffdata, 0, diffdata.length); + in.skip(32); + DataInputStream cpf = new DataInputStream(new GZIPInputStream(in)); + + in = new ByteArrayInputStream(diffdata, 0, diffdata.length); + in.skip(32 + ctrllen); + InputStream dpf = new GZIPInputStream(in); + + in = new ByteArrayInputStream(diffdata, 0, diffdata.length); + in.skip(32 + ctrllen + datalen); + InputStream epf = new GZIPInputStream(in); + + byte[] newdata = new byte[newsize]; + + int oldpos = 0; + int newpos = 0; + + while (newpos < newsize) { + int[] ctrl = new int[3]; + for (int i = 0; i <= 2; i++) { + ctrl[i] = (int) cpf.readLong(); + } + if (newpos + ctrl[0] > newsize) { + throw new IOException("Invalid ctrl[0]"); + } + + read(dpf, newdata, newpos, ctrl[0]); + + for (int i = 0; i < ctrl[0]; i++) { + if ((oldpos + i >= 0) && (oldpos + i < olddata.length)) { + newdata[newpos + i] += olddata[oldpos + i]; + } + } + + newpos += ctrl[0]; + oldpos += ctrl[0]; + + if (newpos + ctrl[1] > newsize) { + throw new IOException("Invalid ctrl[0]"); + } + + read(epf, newdata, newpos, ctrl[1]); + + newpos += ctrl[1]; + oldpos += ctrl[2]; + } + + cpf.close(); + dpf.close(); + epf.close(); + + return newdata; + } + + private static void read(InputStream in, byte[] buf, int off, int len) throws IOException { + for (int i, n = 0; n < len; n += i) { + if ((i = in.read(buf, off + n, len - n)) < 0) { + throw new IOException("Unexpected EOF"); + } + } + } +} diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index 722a4af4dc84a..a6a4bd82d170d 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -113,7 +113,7 @@ source_set("txt") { ] deps = [ - "//third_party/skia:effects", + "//third_party/skia", ] if (is_mac || is_ios) { diff --git a/third_party/txt/benchmarks/paint_record_benchmarks.cc b/third_party/txt/benchmarks/paint_record_benchmarks.cc index 29bbaa94b10bf..b2adf0d3468cd 100644 --- a/third_party/txt/benchmarks/paint_record_benchmarks.cc +++ b/third_party/txt/benchmarks/paint_record_benchmarks.cc @@ -28,18 +28,17 @@ static void BM_PaintRecordInit(benchmark::State& state) { TextStyle style; style.font_family = "Roboto"; - SkPaint paint; - paint.setAntiAlias(true); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setTextSize(14); - paint.setFakeBoldText(false); + SkFont font; + font.setEdging(SkFont::Edging::kAntiAlias); + font.setSize(14); + font.setEmbolden(false); SkTextBlobBuilder builder; - builder.allocRunPos(paint, 100); + builder.allocRunPos(font, 100); auto text_blob = builder.make(); while (state.KeepRunning()) { - PaintRecord PaintRecord(style, text_blob, SkPaint::FontMetrics(), 0, 0); + PaintRecord PaintRecord(style, text_blob, SkFontMetrics(), 0, 0); } } BENCHMARK(BM_PaintRecordInit); diff --git a/third_party/txt/benchmarks/paragraph_benchmarks.cc b/third_party/txt/benchmarks/paragraph_benchmarks.cc index d3236847bfc41..b19783b169b56 100644 --- a/third_party/txt/benchmarks/paragraph_benchmarks.cc +++ b/third_party/txt/benchmarks/paragraph_benchmarks.cc @@ -426,15 +426,14 @@ BENCHMARK(BM_ParagraphMinikinAddStyleRun) ->Complexity(benchmark::oN); static void BM_ParagraphSkTextBlobAlloc(benchmark::State& state) { - SkPaint paint; - paint.setAntiAlias(true); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setTextSize(14); - paint.setFakeBoldText(false); + SkFont font; + font.setEdging(SkFont::Edging::kAntiAlias); + font.setSize(14); + font.setEmbolden(false); while (state.KeepRunning()) { SkTextBlobBuilder builder; - builder.allocRunPos(paint, state.range(0)); + builder.allocRunPos(font, state.range(0)); } state.SetComplexityN(state.range(0)); } diff --git a/third_party/txt/src/minikin/GraphemeBreak.h b/third_party/txt/src/minikin/GraphemeBreak.h index 233ec15c76766..62b9d59aa9344 100644 --- a/third_party/txt/src/minikin/GraphemeBreak.h +++ b/third_party/txt/src/minikin/GraphemeBreak.h @@ -17,6 +17,9 @@ #ifndef MINIKIN_GRAPHEME_BREAK_H #define MINIKIN_GRAPHEME_BREAK_H +#include +#include + namespace minikin { class GraphemeBreak { diff --git a/third_party/txt/src/minikin/Layout.cpp b/third_party/txt/src/minikin/Layout.cpp index 4339fcb02a920..857c744b939fa 100644 --- a/third_party/txt/src/minikin/Layout.cpp +++ b/third_party/txt/src/minikin/Layout.cpp @@ -41,9 +41,6 @@ #include "LayoutUtils.h" #include "MinikinInternal.h" -using std::string; -using std::vector; - namespace minikin { const int kDirection_Mask = 0x1; @@ -170,20 +167,10 @@ class LayoutCache : private android::OnEntryRemoved { static const size_t kMaxEntries = 5000; }; -static unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, - hb_codepoint_t, - hb_codepoint_t*, - void*) { - return 0; -} - class LayoutEngine { public: LayoutEngine() { unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); - /* Disable the function used for compatibility decomposition */ - hb_unicode_funcs_set_decompose_compatibility_func( - unicodeFunctions, disabledDecomposeCompatibility, NULL, NULL); hbBuffer = hb_buffer_create(); hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions); } @@ -299,8 +286,7 @@ hb_font_funcs_t* getHbFontFuncs(bool forColorBitmapFont) { static bool isColorBitmapFont(hb_font_t* font) { hb_face_t* face = hb_font_get_face(font); - HbBlob cbdt(hb_face_reference_table(face, HB_TAG('C', 'B', 'D', 'T'))); - return cbdt.size() > 0; + return hb_ot_color_has_png(face); } static float HBFixedToFloat(hb_position_t v) { @@ -755,7 +741,8 @@ float Layout::doLayoutWord(const uint16_t* buf, return advance; } -static void addFeatures(const string& str, vector* features) { +static void addFeatures(const std::string& str, + std::vector* features) { if (!str.size()) return; @@ -935,10 +922,10 @@ void Layout::doLayoutRun(const uint16_t* buf, LayoutContext* ctx, const std::shared_ptr& collection) { hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer; - vector items; + std::vector items; collection->itemize(buf + start, count, ctx->style, &items); - vector features; + std::vector features; // Disable default-on non-required ligature features if letter-spacing // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property // "When the effective spacing between two characters is not zero (due to diff --git a/third_party/txt/src/txt/font_collection.cc b/third_party/txt/src/txt/font_collection.cc index 03e960a8d6dda..ff687726ebed9 100644 --- a/third_party/txt/src/txt/font_collection.cc +++ b/third_party/txt/src/txt/font_collection.cc @@ -16,6 +16,7 @@ #include "font_collection.h" +#include #include #include #include @@ -36,14 +37,24 @@ const std::shared_ptr g_null_family; } // anonymous namespace +FontCollection::FamilyKey::FamilyKey(const std::vector& families, + const std::string& loc) { + locale = loc; + + std::stringstream stream; + for_each(families.begin(), families.end(), + [&stream](const std::string& str) { stream << str << ','; }); + font_families = stream.str(); +} + bool FontCollection::FamilyKey::operator==( const FontCollection::FamilyKey& other) const { - return font_family == other.font_family && locale == other.locale; + return font_families == other.font_families && locale == other.locale; } size_t FontCollection::FamilyKey::Hasher::operator()( const FontCollection::FamilyKey& key) const { - return std::hash()(key.font_family) ^ + return std::hash()(key.font_families) ^ std::hash()(key.locale); } @@ -95,12 +106,12 @@ void FontCollection::SetTestFontManager(sk_sp font_manager) { // Return the available font managers in the order they should be queried. std::vector> FontCollection::GetFontManagerOrder() const { std::vector> order; - if (test_font_manager_) - order.push_back(test_font_manager_); if (dynamic_font_manager_) order.push_back(dynamic_font_manager_); if (asset_font_manager_) order.push_back(asset_font_manager_); + if (test_font_manager_) + order.push_back(test_font_manager_); if (default_font_manager_) order.push_back(default_font_manager_); return order; @@ -111,54 +122,72 @@ void FontCollection::DisableFontFallback() { } std::shared_ptr -FontCollection::GetMinikinFontCollectionForFamily( - const std::string& font_family, +FontCollection::GetMinikinFontCollectionForFamilies( + const std::vector& font_families, const std::string& locale) { // Look inside the font collections cache first. - FamilyKey family_key(font_family, locale); + FamilyKey family_key(font_families, locale); auto cached = font_collections_cache_.find(family_key); if (cached != font_collections_cache_.end()) { return cached->second; } - for (sk_sp& manager : GetFontManagerOrder()) { - std::shared_ptr minikin_family = - CreateMinikinFontFamily(manager, font_family); - if (!minikin_family) - continue; + std::vector> minikin_families; - // Create a vector of font families for the Minikin font collection. - std::vector> minikin_families = { - minikin_family, - }; - if (enable_font_fallback_) { - for (std::string fallback_family : fallback_fonts_for_locale_[locale]) - minikin_families.push_back(fallback_fonts_[fallback_family]); + // Search for all user provided font families. + for (size_t fallback_index = 0; fallback_index < font_families.size(); + fallback_index++) { + std::shared_ptr minikin_family = + FindFontFamilyInManagers(font_families[fallback_index]); + if (minikin_family != nullptr) { + minikin_families.push_back(minikin_family); } - - // Create the minikin font collection. - auto font_collection = - std::make_shared(std::move(minikin_families)); - if (enable_font_fallback_) { - font_collection->set_fallback_font_provider( - std::make_unique(shared_from_this())); + } + // Search for default font family if no user font families were found. + if (minikin_families.empty()) { + const auto default_font_family = GetDefaultFontFamily(); + std::shared_ptr minikin_family = + FindFontFamilyInManagers(default_font_family); + if (minikin_family != nullptr) { + minikin_families.push_back(minikin_family); + } + } + // Default font family also not found. We fail to get a FontCollection. + if (minikin_families.empty()) { + return nullptr; + } + if (enable_font_fallback_) { + for (std::string fallback_family : fallback_fonts_for_locale_[locale]) { + auto it = fallback_fonts_.find(fallback_family); + if (it != fallback_fonts_.end()) { + minikin_families.push_back(it->second); + } } + } + // Create the minikin font collection. + auto font_collection = + std::make_shared(std::move(minikin_families)); + if (enable_font_fallback_) { + font_collection->set_fallback_font_provider( + std::make_unique(shared_from_this())); + } - // Cache the font collection for future queries. - font_collections_cache_[family_key] = font_collection; + // Cache the font collection for future queries. + font_collections_cache_[family_key] = font_collection; - return font_collection; - } + return font_collection; +} - const auto default_font_family = GetDefaultFontFamily(); - if (font_family != default_font_family) { - std::shared_ptr default_collection = - GetMinikinFontCollectionForFamily(default_font_family, ""); - font_collections_cache_[family_key] = default_collection; - return default_collection; +std::shared_ptr FontCollection::FindFontFamilyInManagers( + const std::string& family_name) { + // Search for the font family in each font manager. + for (sk_sp& manager : GetFontManagerOrder()) { + std::shared_ptr minikin_family = + CreateMinikinFontFamily(manager, family_name); + if (!minikin_family) + continue; + return minikin_family; } - - // No match found in any of our font managers. return nullptr; } @@ -198,6 +227,22 @@ std::shared_ptr FontCollection::CreateMinikinFontFamily( const std::shared_ptr& FontCollection::MatchFallbackFont( uint32_t ch, std::string locale) { + // Check if the ch's matched font has been cached. We cache the results of + // this method as repeated matchFamilyStyleCharacter calls can become + // extremely laggy when typing a large number of complex emojis. + auto lookup = fallback_match_cache_.find(ch); + if (lookup != fallback_match_cache_.end()) { + return *lookup->second; + } + const std::shared_ptr* match = + &DoMatchFallbackFont(ch, locale); + fallback_match_cache_.insert(std::make_pair(ch, match)); + return *match; +} + +const std::shared_ptr& FontCollection::DoMatchFallbackFont( + uint32_t ch, + std::string locale) { for (const sk_sp& manager : GetFontManagerOrder()) { std::vector bcp47; if (!locale.empty()) @@ -215,7 +260,6 @@ const std::shared_ptr& FontCollection::MatchFallbackFont( return GetFallbackFontFamily(manager, family_name); } - return g_null_family; } @@ -235,11 +279,15 @@ FontCollection::GetFallbackFontFamily(const sk_sp& manager, auto insert_it = fallback_fonts_.insert(std::make_pair(family_name, minikin_family)); - // Clear the cache to force creation of new font collections that will include - // this fallback font. + // Clear the cache to force creation of new font collections that will + // include this fallback font. font_collections_cache_.clear(); return insert_it.first->second; } +void FontCollection::ClearFontFamilyCache() { + font_collections_cache_.clear(); +} + } // namespace txt diff --git a/third_party/txt/src/txt/font_collection.h b/third_party/txt/src/txt/font_collection.h index d512a6dd3ae26..c98b4d29ca05d 100644 --- a/third_party/txt/src/txt/font_collection.h +++ b/third_party/txt/src/txt/font_collection.h @@ -45,10 +45,12 @@ class FontCollection : public std::enable_shared_from_this { void SetDynamicFontManager(sk_sp font_manager); void SetTestFontManager(sk_sp font_manager); - std::shared_ptr GetMinikinFontCollectionForFamily( - const std::string& family, + std::shared_ptr GetMinikinFontCollectionForFamilies( + const std::vector& font_families, const std::string& locale); + // Provides a FontFamily that contains glyphs for ch. This caches previously + // matched fonts. Also see FontCollection::DoMatchFallbackFont. const std::shared_ptr& MatchFallbackFont( uint32_t ch, std::string locale); @@ -57,12 +59,15 @@ class FontCollection : public std::enable_shared_from_this { // missing from the requested font family. void DisableFontFallback(); + // Remove all entries in the font family cache. + void ClearFontFamilyCache(); + private: struct FamilyKey { - FamilyKey(const std::string& family, const std::string& loc) - : font_family(family), locale(loc) {} + FamilyKey(const std::vector& families, const std::string& loc); - std::string font_family; + // Concatenated string with all font families. + std::string font_families; std::string locale; bool operator==(const FamilyKey& other) const; @@ -80,14 +85,27 @@ class FontCollection : public std::enable_shared_from_this { std::shared_ptr, FamilyKey::Hasher> font_collections_cache_; + // Cache that stores the results of MatchFallbackFont to ensure lag-free emoji + // font fallback matching. + std::unordered_map*> + fallback_match_cache_; std::unordered_map> fallback_fonts_; std::unordered_map> fallback_fonts_for_locale_; bool enable_font_fallback_; + // Performs the actual work of MatchFallbackFont. The result is cached in + // fallback_match_cache_. + const std::shared_ptr& DoMatchFallbackFont( + uint32_t ch, + std::string locale); + std::vector> GetFontManagerOrder() const; + std::shared_ptr FindFontFamilyInManagers( + const std::string& family_name); + std::shared_ptr CreateMinikinFontFamily( const sk_sp& manager, const std::string& family_name); diff --git a/third_party/txt/src/txt/font_skia.cc b/third_party/txt/src/txt/font_skia.cc index 5290bdbe4242a..705e014fa50d9 100644 --- a/third_party/txt/src/txt/font_skia.cc +++ b/third_party/txt/src/txt/font_skia.cc @@ -15,6 +15,7 @@ */ #include "font_skia.h" +#include "SkFont.h" #include @@ -47,33 +48,32 @@ FontSkia::FontSkia(sk_sp typeface) FontSkia::~FontSkia() = default; -static void FontSkia_SetSkiaPaint(sk_sp typeface, - SkPaint* skPaint, - const minikin::MinikinPaint& paint) { - skPaint->setTypeface(std::move(typeface)); - skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); +static void FontSkia_SetSkiaFont(sk_sp typeface, + SkFont* skFont, + const minikin::MinikinPaint& paint) { + skFont->setTypeface(std::move(typeface)); // TODO: set more paint parameters from Minikin - skPaint->setTextSize(paint.size); + skFont->setSize(paint.size); } float FontSkia::GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint) const { - SkPaint skPaint; + SkFont skFont; uint16_t glyph16 = glyph_id; SkScalar skWidth; - FontSkia_SetSkiaPaint(typeface_, &skPaint, paint); - skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL); + FontSkia_SetSkiaFont(typeface_, &skFont, paint); + skFont.getWidths(&glyph16, 1, &skWidth); return skWidth; } void FontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, const minikin::MinikinPaint& paint) const { - SkPaint skPaint; + SkFont skFont; uint16_t glyph16 = glyph_id; SkRect skBounds; - FontSkia_SetSkiaPaint(typeface_, &skPaint, paint); - skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds); + FontSkia_SetSkiaFont(typeface_, &skFont, paint); + skFont.getWidths(&glyph16, 1, NULL, &skBounds); bounds->mLeft = skBounds.fLeft; bounds->mTop = skBounds.fTop; bounds->mRight = skBounds.fRight; diff --git a/third_party/txt/src/txt/paint_record.cc b/third_party/txt/src/txt/paint_record.cc index f37429499ce71..0ee43f847808f 100644 --- a/third_party/txt/src/txt/paint_record.cc +++ b/third_party/txt/src/txt/paint_record.cc @@ -24,26 +24,30 @@ PaintRecord::~PaintRecord() = default; PaintRecord::PaintRecord(TextStyle style, SkPoint offset, sk_sp text, - SkPaint::FontMetrics metrics, + SkFontMetrics metrics, size_t line, - double run_width) + double run_width, + bool is_ghost) : style_(style), offset_(offset), text_(std::move(text)), metrics_(metrics), line_(line), - run_width_(run_width) {} + run_width_(run_width), + is_ghost_(is_ghost) {} PaintRecord::PaintRecord(TextStyle style, sk_sp text, - SkPaint::FontMetrics metrics, + SkFontMetrics metrics, size_t line, - double run_width) + double run_width, + bool is_ghost) : style_(style), text_(std::move(text)), metrics_(metrics), line_(line), - run_width_(run_width) {} + run_width_(run_width), + is_ghost_(is_ghost) {} PaintRecord::PaintRecord(PaintRecord&& other) { style_ = other.style_; @@ -51,7 +55,7 @@ PaintRecord::PaintRecord(PaintRecord&& other) { text_ = std::move(other.text_); metrics_ = other.metrics_; line_ = other.line_; - run_width_ = other.run_width_; + run_width_ = other.run_width_, is_ghost_ = other.is_ghost_; } PaintRecord& PaintRecord::operator=(PaintRecord&& other) { @@ -61,6 +65,7 @@ PaintRecord& PaintRecord::operator=(PaintRecord&& other) { metrics_ = other.metrics_; line_ = other.line_; run_width_ = other.run_width_; + is_ghost_ = other.is_ghost_; return *this; } diff --git a/third_party/txt/src/txt/paint_record.h b/third_party/txt/src/txt/paint_record.h index 2b9248ef7c169..c07ab6827c350 100644 --- a/third_party/txt/src/txt/paint_record.h +++ b/third_party/txt/src/txt/paint_record.h @@ -20,7 +20,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "text_style.h" -#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkFontMetrics.h" #include "third_party/skia/include/core/SkTextBlob.h" namespace txt { @@ -37,15 +37,17 @@ class PaintRecord { PaintRecord(TextStyle style, SkPoint offset, sk_sp text, - SkPaint::FontMetrics metrics, + SkFontMetrics metrics, size_t line, - double run_width); + double run_width, + bool is_ghost); PaintRecord(TextStyle style, sk_sp text, - SkPaint::FontMetrics metrics, + SkFontMetrics metrics, size_t line, - double run_width); + double run_width, + bool is_ghost); PaintRecord(PaintRecord&& other); @@ -57,7 +59,7 @@ class PaintRecord { SkTextBlob* text() const { return text_.get(); } - const SkPaint::FontMetrics& metrics() const { return metrics_; } + const SkFontMetrics& metrics() const { return metrics_; } const TextStyle& style() const { return style_; } @@ -65,6 +67,8 @@ class PaintRecord { double GetRunWidth() const { return run_width_; } + bool isGhost() const { return is_ghost_; } + private: TextStyle style_; // offset_ is the overall offset of the origin of the SkTextBlob. @@ -72,9 +76,12 @@ class PaintRecord { // SkTextBlob stores the glyphs and coordinates to draw them. sk_sp text_; // FontMetrics stores the measurements of the font used. - SkPaint::FontMetrics metrics_; + SkFontMetrics metrics_; size_t line_; double run_width_ = 0.0f; + // 'Ghost' runs represent trailing whitespace. 'Ghost' runs should not have + // decorations painted on them and do not impact layout of visible glyphs. + bool is_ghost_ = false; FML_DISALLOW_COPY_AND_ASSIGN(PaintRecord); }; diff --git a/third_party/txt/src/txt/paragraph.cc b/third_party/txt/src/txt/paragraph.cc index 2e324aa6e2b0d..cba46048d7b9e 100644 --- a/third_party/txt/src/txt/paragraph.cc +++ b/third_party/txt/src/txt/paragraph.cc @@ -38,6 +38,8 @@ #include "unicode/utf16.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkFont.h" +#include "third_party/skia/include/core/SkFontMetrics.h" #include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkTextBlob.h" @@ -62,10 +64,10 @@ class GlyphTypeface { bool operator!=(GlyphTypeface& other) { return !(*this == other); } - void apply(SkPaint& paint) { - paint.setTypeface(typeface_); - paint.setFakeBoldText(fake_bold_); - paint.setTextSkewX(fake_italic_ ? -SK_Scalar1 / 4 : 0); + void apply(SkFont& font) { + font.setTypeface(typeface_); + font.setEmbolden(fake_bold_); + font.setSkewX(fake_italic_ ? -SK_Scalar1 / 4 : 0); } private: @@ -205,7 +207,7 @@ Paragraph::CodeUnitRun::CodeUnitRun(std::vector&& p, Range cu, Range x, size_t line, - const SkPaint::FontMetrics& metrics, + const SkFontMetrics& metrics, TextDirection dir) : positions(std::move(p)), code_units(cu), @@ -288,8 +290,11 @@ bool Paragraph::ComputeLineBreaks() { std::shared_ptr collection = GetMinikinFontCollectionForStyle(run.style); if (collection == nullptr) { - FML_LOG(INFO) << "Could not find font collection for family \"" - << run.style.font_family << "\"."; + FML_LOG(INFO) << "Could not find font collection for families \"" + << (run.style.font_families.empty() + ? "" + : run.style.font_families[0]) + << "\"."; return false; } size_t run_start = std::max(run.start, block_start) - block_start; @@ -419,6 +424,58 @@ bool Paragraph::ComputeBidiRuns(std::vector* result) { return true; } +void Paragraph::ComputeStrut(StrutMetrics* strut, SkFont& font) { + strut->ascent = 0; + strut->descent = 0; + strut->leading = 0; + strut->half_leading = 0; + strut->line_height = 0; + strut->force_strut = false; + + // Font size must be positive. + bool valid_strut = + paragraph_style_.strut_enabled && paragraph_style_.strut_font_size >= 0; + if (!valid_strut) { + return; + } + // force_strut makes all lines have exactly the strut metrics, and ignores all + // actual metrics. We only force the strut if the strut is non-zero and valid. + strut->force_strut = paragraph_style_.force_strut_height && valid_strut; + minikin::FontStyle minikin_font_style( + 0, GetWeight(paragraph_style_.strut_font_weight), + paragraph_style_.strut_font_style == FontStyle::italic); + + std::shared_ptr collection = + font_collection_->GetMinikinFontCollectionForFamilies( + paragraph_style_.strut_font_families, ""); + if (!collection) { + return; + } + minikin::FakedFont faked_font = collection->baseFontFaked(minikin_font_style); + + if (faked_font.font != nullptr) { + SkString str; + static_cast(faked_font.font) + ->GetSkTypeface() + ->getFamilyName(&str); + font.setTypeface(static_cast(faked_font.font)->GetSkTypeface()); + font.setSize(paragraph_style_.strut_font_size); + SkFontMetrics strut_metrics; + font.getMetrics(&strut_metrics); + + strut->ascent = paragraph_style_.strut_height * -strut_metrics.fAscent; + strut->descent = paragraph_style_.strut_height * strut_metrics.fDescent; + strut->leading = + // Use font's leading if there is no user specified strut leading. + paragraph_style_.strut_leading < 0 + ? strut_metrics.fLeading + : (paragraph_style_.strut_leading * + (strut_metrics.fDescent - strut_metrics.fAscent)); + strut->half_leading = strut->leading / 2; + strut->line_height = strut->ascent + strut->descent + strut->leading; + } +} + void Paragraph::Layout(double width, bool force) { // Do not allow calling layout multiple times without changing anything. if (!needs_layout_ && width == width_ && !force) { @@ -435,17 +492,21 @@ void Paragraph::Layout(double width, bool force) { if (!ComputeBidiRuns(&bidi_runs)) return; - SkPaint paint; - paint.setAntiAlias(true); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setSubpixelText(true); - paint.setHinting(SkPaint::kSlight_Hinting); + SkFont font; + font.setEdging(SkFont::Edging::kAntiAlias); + font.setSubpixel(true); + font.setHinting(kSlight_SkFontHinting); records_.clear(); line_heights_.clear(); line_baselines_.clear(); glyph_lines_.clear(); code_unit_runs_.clear(); + line_max_spacings_.clear(); + line_max_descent_.clear(); + line_max_ascent_.clear(); + max_right_ = FLT_MIN; + min_left_ = FLT_MAX; minikin::Layout layout; SkTextBlobBuilder builder; @@ -453,6 +514,11 @@ void Paragraph::Layout(double width, bool force) { double prev_max_descent = 0; double max_word_width = 0; + // Compute strut minimums according to paragraph_style_. + StrutMetrics strut; + ComputeStrut(&strut, font); + + // Paragraph bounds tracking. size_t line_limit = std::min(paragraph_style_.max_lines, line_ranges_.size()); did_exceed_max_lines_ = (line_ranges_.size() > paragraph_style_.max_lines); @@ -474,11 +540,12 @@ void Paragraph::Layout(double width, bool force) { } } - // Exclude trailing whitespace from right-justified lines so the last - // visible character in the line will be flush with the right margin. + // Exclude trailing whitespace from justified lines so the last visible + // character in the line will be flush with the right margin. size_t line_end_index = (paragraph_style_.effective_align() == TextAlign::right || - paragraph_style_.effective_align() == TextAlign::center) + paragraph_style_.effective_align() == TextAlign::center || + paragraph_style_.effective_align() == TextAlign::justify) ? line_range.end_excluding_whitespace : line_range.end; @@ -491,6 +558,31 @@ void Paragraph::Layout(double width, bool force) { std::min(bidi_run.end(), line_end_index), bidi_run.direction(), bidi_run.style()); } + // A "ghost" run is a run that does not impact the layout, breaking, + // alignment, width, etc but is still "visible" though getRectsForRange. + // For example, trailing whitespace on centered text can be scrolled + // through with the caret but will not wrap the line. + // + // Here, we add an additional run for the whitespace, but dont + // let it impact metrics. After layout of the whitespace run, we do not + // add its width into the x-offset adjustment, effectively nullifying its + // impact on the layout. + if (paragraph_style_.ellipsis.empty() && + line_range.end_excluding_whitespace < line_range.end && + bidi_run.start() <= line_range.end && + bidi_run.end() > line_end_index) { + line_runs.emplace_back(std::max(bidi_run.start(), line_end_index), + std::min(bidi_run.end(), line_range.end), + bidi_run.direction(), bidi_run.style(), true); + } + } + bool line_runs_all_rtl = + line_runs.size() && + std::accumulate( + line_runs.begin(), line_runs.end(), true, + [](const bool a, const BidiRun& b) { return a && b.is_rtl(); }); + if (line_runs_all_rtl) { + std::reverse(words.begin(), words.end()); } std::vector line_glyph_positions; @@ -502,10 +594,10 @@ void Paragraph::Layout(double width, bool force) { for (auto line_run_it = line_runs.begin(); line_run_it != line_runs.end(); ++line_run_it) { const BidiRun& run = *line_run_it; - minikin::FontStyle font; + minikin::FontStyle minikin_font; minikin::MinikinPaint minikin_paint; - GetFontAndMinikinPaint(run.style(), &font, &minikin_paint); - paint.setTextSize(run.style().font_size); + GetFontAndMinikinPaint(run.style(), &minikin_font, &minikin_paint); + font.setSize(run.style().font_size); std::shared_ptr minikin_font_collection = GetMinikinFontCollectionForStyle(run.style()); @@ -526,13 +618,14 @@ void Paragraph::Layout(double width, bool force) { paragraph_style_.unlimited_lines())) { float ellipsis_width = layout.measureText( reinterpret_cast(ellipsis.data()), 0, - ellipsis.length(), ellipsis.length(), run.is_rtl(), font, + ellipsis.length(), ellipsis.length(), run.is_rtl(), minikin_font, minikin_paint, minikin_font_collection, nullptr); std::vector text_advances(text_count); - float text_width = layout.measureText( - text_ptr, text_start, text_count, text_.size(), run.is_rtl(), font, - minikin_paint, minikin_font_collection, text_advances.data()); + float text_width = + layout.measureText(text_ptr, text_start, text_count, text_.size(), + run.is_rtl(), minikin_font, minikin_paint, + minikin_font_collection, text_advances.data()); // Truncate characters from the text until the ellipsis fits. size_t truncate_count = 0; @@ -563,7 +656,7 @@ void Paragraph::Layout(double width, bool force) { } layout.doLayout(text_ptr, text_start, text_count, text_size, run.is_rtl(), - font, minikin_paint, minikin_font_collection); + minikin_font, minikin_paint, minikin_font_collection); if (layout.nGlyphs() == 0) continue; @@ -580,9 +673,11 @@ void Paragraph::Layout(double width, bool force) { for (const Range& glyph_blob : glyph_blobs) { std::vector glyph_positions; - GetGlyphTypeface(layout, glyph_blob.start).apply(paint); + GetGlyphTypeface(layout, glyph_blob.start).apply(font); const SkTextBlobBuilder::RunBuffer& blob_buffer = - builder.allocRunPos(paint, glyph_blob.end - glyph_blob.start); + builder.allocRunPos(font, glyph_blob.end - glyph_blob.start); + + double justify_x_offset_delta = 0; for (size_t glyph_index = glyph_blob.start; glyph_index < glyph_blob.end;) { @@ -597,7 +692,7 @@ void Paragraph::Layout(double width, bool force) { size_t pos_index = blob_index * 2; blob_buffer.pos[pos_index] = - layout.getX(glyph_index) + justify_x_offset; + layout.getX(glyph_index) + justify_x_offset_delta; blob_buffer.pos[pos_index + 1] = layout.getY(glyph_index); if (glyph_index == cluster_start_glyph_index) @@ -659,15 +754,26 @@ void Paragraph::Layout(double width, bool force) { grapheme_code_unit_counts[i]); } - if (word_index < words.size() && - words[word_index].start == run.start() + glyph_code_units.start) { + bool at_word_start = false; + bool at_word_end = false; + if (word_index < words.size()) { + at_word_start = + words[word_index].start == run.start() + glyph_code_units.start; + at_word_end = + words[word_index].end == run.start() + glyph_code_units.end; + if (line_runs_all_rtl) { + std::swap(at_word_start, at_word_end); + } + } + + if (at_word_start) { word_start_position = run_x_offset + glyph_x_offset; } - if (word_index < words.size() && - words[word_index].end == run.start() + glyph_code_units.end) { - if (justify_line) - justify_x_offset += word_gap_width; + if (at_word_end) { + if (justify_line) { + justify_x_offset_delta += word_gap_width; + } word_index++; if (!isnan(word_start_position)) { @@ -681,11 +787,13 @@ void Paragraph::Layout(double width, bool force) { if (glyph_positions.empty()) continue; - SkPaint::FontMetrics metrics; - paint.getFontMetrics(&metrics); - paint_records.emplace_back(run.style(), SkPoint::Make(run_x_offset, 0), - builder.make(), metrics, line_number, - layout.getAdvance()); + SkFontMetrics metrics; + font.getMetrics(&metrics); + paint_records.emplace_back( + run.style(), SkPoint::Make(run_x_offset + justify_x_offset, 0), + builder.make(), metrics, line_number, + layout.getAdvance() + justify_x_offset_delta, run.is_ghost()); + justify_x_offset += justify_x_offset_delta; line_glyph_positions.insert(line_glyph_positions.end(), glyph_positions.begin(), @@ -703,9 +811,17 @@ void Paragraph::Layout(double width, bool force) { Range(glyph_positions.front().x_pos.start, glyph_positions.back().x_pos.end), line_number, metrics, run.direction()); + + min_left_ = std::min(min_left_, glyph_positions.front().x_pos.start); + max_right_ = std::max(max_right_, glyph_positions.back().x_pos.end); } // for each in glyph_blobs - run_x_offset += layout.getAdvance(); + // Do not increase x offset for trailing ghost runs as it should not + // impact the layout of visible glyphs. We do keep the record though so + // GetRectsForRange() can find metrics for trailing spaces. + if (!run.is_ghost()) { + run_x_offset += layout.getAdvance(); + } } // for each in line_runs // Adjust the glyph positions based on the alignment of the line. @@ -727,47 +843,61 @@ void Paragraph::Layout(double width, bool force) { code_unit_runs_.insert(code_unit_runs_.end(), line_code_unit_runs.begin(), line_code_unit_runs.end()); - double max_line_spacing = 0; - double max_descent = 0; - auto update_line_metrics = [&](const SkPaint::FontMetrics& metrics, + // Calculate the amount to advance in the y direction. This is done by + // computing the maximum ascent and descent with respect to the strut. + double max_ascent = strut.ascent + strut.half_leading; + double max_descent = strut.descent + strut.half_leading; + SkScalar max_unscaled_ascent = 0; + auto update_line_metrics = [&](const SkFontMetrics& metrics, const TextStyle& style) { - double line_spacing = - (line_number == 0) ? -metrics.fAscent * style.height - : (-metrics.fAscent + metrics.fLeading) * - style.height * paragraph_style_.line_height; - if (line_spacing > max_line_spacing) { - max_line_spacing = line_spacing; - if (line_number == 0) { - alphabetic_baseline_ = line_spacing; - ideographic_baseline_ = - (metrics.fDescent - metrics.fAscent) * style.height; - } + if (!strut.force_strut) { + double ascent = + (-metrics.fAscent + metrics.fLeading / 2) * style.height; + max_ascent = std::max(ascent, max_ascent); + + double descent = + (metrics.fDescent + metrics.fLeading / 2) * style.height; + max_descent = std::max(descent, max_descent); } - max_line_spacing = std::max(line_spacing, max_line_spacing); - double descent = metrics.fDescent * style.height; - max_descent = std::max(descent, max_descent); + max_unscaled_ascent = std::max(-metrics.fAscent, max_unscaled_ascent); }; for (const PaintRecord& paint_record : paint_records) { update_line_metrics(paint_record.metrics(), paint_record.style()); } + // If no fonts were actually rendered, then compute a baseline based on the // font of the paragraph style. if (paint_records.empty()) { - SkPaint::FontMetrics metrics; + SkFontMetrics metrics; TextStyle style(paragraph_style_.GetTextStyle()); - paint.setTypeface(GetDefaultSkiaTypeface(style)); - paint.setTextSize(style.font_size); - paint.getFontMetrics(&metrics); + font.setTypeface(GetDefaultSkiaTypeface(style)); + font.setSize(style.font_size); + font.getMetrics(&metrics); update_line_metrics(metrics, style); } + // Calculate the baselines. This is only done on the first line. + if (line_number == 0) { + alphabetic_baseline_ = max_ascent; + // TODO(garyq): Ideographic baseline is currently bottom of EM + // box, which is not correct. This should be obtained from metrics. + // Skia currently does not support various baselines. + ideographic_baseline_ = (max_ascent + max_descent); + } + line_heights_.push_back((line_heights_.empty() ? 0 : line_heights_.back()) + - round(max_line_spacing + max_descent)); + round(max_ascent + max_descent)); line_baselines_.push_back(line_heights_.back() - max_descent); - y_offset += round(max_line_spacing + prev_max_descent); + y_offset += round(max_ascent + prev_max_descent); prev_max_descent = max_descent; + // The max line spacing and ascent have been multiplied by -1 to make math + // in GetRectsForRange more logical/readable. + line_max_spacings_.push_back(max_ascent); + line_max_descent_.push_back(max_descent); + line_max_ascent_.push_back(max_unscaled_ascent); + for (PaintRecord& paint_record : paint_records) { paint_record.SetOffset( SkPoint::Make(paint_record.offset().x() + line_x_offset, y_offset)); @@ -860,8 +990,8 @@ Paragraph::GetMinikinFontCollectionForStyle(const TextStyle& style) { } } - return font_collection_->GetMinikinFontCollectionForFamily(style.font_family, - locale); + return font_collection_->GetMinikinFontCollectionForFamilies( + style.font_families, locale); } sk_sp Paragraph::GetDefaultSkiaTypeface(const TextStyle& style) { @@ -880,6 +1010,11 @@ sk_sp Paragraph::GetDefaultSkiaTypeface(const TextStyle& style) { void Paragraph::Paint(SkCanvas* canvas, double x, double y) { SkPoint base_offset = SkPoint::Make(x, y); SkPaint paint; + // Paint the background first before painting any text to prevent + // potential overlap. + for (const PaintRecord& record : records_) { + PaintBackground(canvas, record, base_offset); + } for (const PaintRecord& record : records_) { if (record.style().has_foreground) { paint = record.style().foreground; @@ -888,7 +1023,6 @@ void Paragraph::Paint(SkCanvas* canvas, double x, double y) { paint.setColor(record.style().color); } SkPoint offset = base_offset + record.offset(); - PaintBackground(canvas, record, base_offset); PaintShadow(canvas, record, offset); canvas->drawTextBlob(record.text(), offset.x(), offset.y(), paint); PaintDecorations(canvas, record, base_offset); @@ -901,7 +1035,10 @@ void Paragraph::PaintDecorations(SkCanvas* canvas, if (record.style().decoration == TextDecoration::kNone) return; - const SkPaint::FontMetrics& metrics = record.metrics(); + if (record.isGhost()) + return; + + const SkFontMetrics& metrics = record.metrics(); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); if (record.style().decoration_color == SK_ColorTRANSPARENT) { @@ -917,17 +1054,11 @@ void Paragraph::PaintDecorations(SkCanvas* canvas, // Filled when drawing wavy decorations. SkPath path; - double width = 0; - if (paragraph_style_.text_align == TextAlign::justify && - record.line() != GetLineCount() - 1) { - width = width_; - } else { - width = record.GetRunWidth(); - } + double width = record.GetRunWidth(); SkScalar underline_thickness; - if ((metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags:: - kUnderlineThicknessIsValid_Flag) && + if ((metrics.fFlags & + SkFontMetrics::FontMetricsFlags::kUnderlineThicknessIsValid_Flag) && metrics.fUnderlineThickness > 0) { underline_thickness = metrics.fUnderlineThickness; } else { @@ -1002,10 +1133,11 @@ void Paragraph::PaintDecorations(SkCanvas* canvas, double y_offset_original = y_offset; // Underline if (record.style().decoration & TextDecoration::kUnderline) { - y_offset += (metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags:: - kUnderlinePositionIsValid_Flag) - ? metrics.fUnderlinePosition - : underline_thickness; + y_offset += + (metrics.fFlags & + SkFontMetrics::FontMetricsFlags::kUnderlinePositionIsValid_Flag) + ? metrics.fUnderlinePosition + : underline_thickness; if (record.style().decoration_style != TextDecorationStyle::kWavy) { canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint); } else { @@ -1031,19 +1163,20 @@ void Paragraph::PaintDecorations(SkCanvas* canvas, } // Strikethrough if (record.style().decoration & TextDecoration::kLineThrough) { - if (metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags:: - kStrikeoutThicknessIsValid_Flag) + if (metrics.fFlags & + SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag) paint.setStrokeWidth(metrics.fStrikeoutThickness * record.style().decoration_thickness_multiplier); // Make sure the double line is "centered" vertically. y_offset += (decoration_count - 1.0) * underline_thickness * kDoubleDecorationSpacing / -2.0; - y_offset += (metrics.fFlags & SkPaint::FontMetrics::FontMetricsFlags:: - kStrikeoutThicknessIsValid_Flag) - ? metrics.fStrikeoutPosition - // Backup value if the strikeoutposition metric is not - // available: - : metrics.fXHeight / -2.0; + y_offset += + (metrics.fFlags & + SkFontMetrics::FontMetricsFlags::kStrikeoutThicknessIsValid_Flag) + ? metrics.fStrikeoutPosition + // Backup value if the strikeoutposition metric is not + // available: + : metrics.fXHeight / -2.0; if (record.style().decoration_style != TextDecorationStyle::kWavy) { canvas->drawLine(x, y + y_offset, x + width, y + y_offset, paint); } else { @@ -1062,7 +1195,7 @@ void Paragraph::PaintBackground(SkCanvas* canvas, if (!record.style().has_background) return; - const SkPaint::FontMetrics& metrics = record.metrics(); + const SkFontMetrics& metrics = record.metrics(); SkRect rect(SkRect::MakeLTRB(0, metrics.fAscent, record.GetRunWidth(), metrics.fDescent)); rect.offset(base_offset + record.offset()); @@ -1093,10 +1226,31 @@ void Paragraph::PaintShadow(SkCanvas* canvas, std::vector Paragraph::GetRectsForRange( size_t start, size_t end, - RectStyle rect_style) const { - std::map> line_boxes; - + RectHeightStyle rect_height_style, + RectWidthStyle rect_width_style) const { + // Struct that holds calculated metrics for each line. + struct LineBoxMetrics { + std::vector boxes; + // Per-line metrics for max and min coordinates for left and right boxes. + // These metrics cannot be calculated in layout generically because of + // selections that do not cover the whole line. + SkScalar max_right = FLT_MIN; + SkScalar min_left = FLT_MAX; + }; + + std::map line_metrics; + // Text direction of the first line so we can extend the correct side for + // RectWidthStyle::kMax. + TextDirection first_line_dir = TextDirection::ltr; + + // Lines that are actually in the requested range. + size_t max_line = 0; + size_t min_line = INT_MAX; + size_t glyph_length = 0; + + // Generate initial boxes and calculate metrics. for (const CodeUnitRun& run : code_unit_runs_) { + // Check to see if we are finished. if (run.code_units.start >= end) break; if (run.code_units.end <= start) @@ -1106,6 +1260,10 @@ std::vector Paragraph::GetRectsForRange( SkScalar top = baseline + run.font_metrics.fAscent; SkScalar bottom = baseline + run.font_metrics.fDescent; + max_line = std::max(run.line_number, max_line); + min_line = std::min(run.line_number, min_line); + + // Calculate left and right. SkScalar left, right; if (run.code_units.start >= start && run.code_units.end <= end) { left = run.x_pos.start; @@ -1117,12 +1275,32 @@ std::vector Paragraph::GetRectsForRange( if (gp.code_units.start >= start && gp.code_units.end <= end) { left = std::min(left, static_cast(gp.x_pos.start)); right = std::max(right, static_cast(gp.x_pos.end)); + } else if (gp.code_units.end == end) { + // Calculate left and right when we are at + // the last position of a combining character. + glyph_length = (gp.code_units.end - gp.code_units.start) - 1; + if (gp.code_units.start == + std::max(0, (start - glyph_length))) { + left = std::min(left, static_cast(gp.x_pos.start)); + right = std::max(right, static_cast(gp.x_pos.end)); + } } } if (left == SK_ScalarMax || right == SK_ScalarMin) continue; } - line_boxes[run.line_number].emplace_back( + // Keep track of the min and max horizontal coordinates over all lines. Not + // needed for kTight. + if (rect_width_style == RectWidthStyle::kMax) { + line_metrics[run.line_number].max_right = + std::max(line_metrics[run.line_number].max_right, right); + line_metrics[run.line_number].min_left = + std::min(line_metrics[run.line_number].min_left, left); + if (min_line == run.line_number) { + first_line_dir = run.direction; + } + } + line_metrics[run.line_number].boxes.emplace_back( SkRect::MakeLTRB(left, top, right, bottom), run.direction); } @@ -1135,34 +1313,110 @@ std::vector Paragraph::GetRectsForRange( break; if (line.end_including_newline <= start) continue; - if (line_boxes.find(line_number) == line_boxes.end()) { + if (line_metrics.find(line_number) == line_metrics.end()) { if (line.end != line.end_including_newline && line.end >= start && line.end_including_newline <= end) { SkScalar x = line_widths_[line_number]; + // Move empty box to center if center aligned and is an empty line. + if (x == 0 && !isinf(width_) && + paragraph_style_.effective_align() == TextAlign::center) { + x = width_ / 2; + } SkScalar top = (line_number > 0) ? line_heights_[line_number - 1] : 0; SkScalar bottom = line_heights_[line_number]; - line_boxes[line_number].emplace_back( + line_metrics[line_number].boxes.emplace_back( SkRect::MakeLTRB(x, top, x, bottom), TextDirection::ltr); } } } + // "Post-process" metrics and aggregate final rects to return. std::vector boxes; - for (const auto& kv : line_boxes) { - if (rect_style & RectStyle::kTight) { + for (const auto& kv : line_metrics) { + // Handle rect_width_styles. We skip the last line because not everything is + // selected. + if (rect_width_style == RectWidthStyle::kMax && kv.first != max_line) { + if (line_metrics[kv.first].min_left > min_left_ && + (kv.first != min_line || first_line_dir == TextDirection::rtl)) { + line_metrics[kv.first].boxes.emplace_back( + SkRect::MakeLTRB( + min_left_, + line_baselines_[kv.first] - line_max_ascent_[kv.first], + line_metrics[kv.first].min_left, + line_baselines_[kv.first] + line_max_descent_[kv.first]), + TextDirection::rtl); + } + if (line_metrics[kv.first].max_right < max_right_ && + (kv.first != min_line || first_line_dir == TextDirection::ltr)) { + line_metrics[kv.first].boxes.emplace_back( + SkRect::MakeLTRB( + line_metrics[kv.first].max_right, + line_baselines_[kv.first] - line_max_ascent_[kv.first], + max_right_, + line_baselines_[kv.first] + line_max_descent_[kv.first]), + TextDirection::ltr); + } + } + + // Handle rect_height_styles. The height metrics used are all positive to + // make the signage clear here. + if (rect_height_style == RectHeightStyle::kTight) { // Ignore line max height and width and generate tight bounds. - boxes.insert(boxes.end(), kv.second.begin(), kv.second.end()); - } else { - // Set each box to the max height of each line to ensure continuity. - float min_top = DBL_MAX; - float max_bottom = 0; - for (const Paragraph::TextBox& box : kv.second) { - min_top = std::min(box.rect.fTop, min_top); - max_bottom = std::max(box.rect.fBottom, max_bottom); + boxes.insert(boxes.end(), kv.second.boxes.begin(), kv.second.boxes.end()); + } else if (rect_height_style == RectHeightStyle::kMax) { + for (const Paragraph::TextBox& box : kv.second.boxes) { + boxes.emplace_back( + SkRect::MakeLTRB( + box.rect.fLeft, + line_baselines_[kv.first] - line_max_ascent_[kv.first], + box.rect.fRight, + line_baselines_[kv.first] + line_max_descent_[kv.first]), + box.direction); } - for (const Paragraph::TextBox& box : kv.second) { - boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, min_top, - box.rect.fRight, max_bottom), + } else if (rect_height_style == + RectHeightStyle::kIncludeLineSpacingMiddle) { + SkScalar adjusted_bottom = + line_baselines_[kv.first] + line_max_descent_[kv.first]; + if (kv.first < line_ranges_.size() - 1) { + adjusted_bottom += (line_max_spacings_[kv.first + 1] - + line_max_ascent_[kv.first + 1]) / + 2; + } + SkScalar adjusted_top = + line_baselines_[kv.first] - line_max_ascent_[kv.first]; + if (kv.first != 0) { + adjusted_top -= + (line_max_spacings_[kv.first] - line_max_ascent_[kv.first]) / 2; + } + for (const Paragraph::TextBox& box : kv.second.boxes) { + boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, adjusted_top, + box.rect.fRight, adjusted_bottom), + box.direction); + } + } else if (rect_height_style == RectHeightStyle::kIncludeLineSpacingTop) { + for (const Paragraph::TextBox& box : kv.second.boxes) { + SkScalar adjusted_top = + kv.first == 0 + ? line_baselines_[kv.first] - line_max_ascent_[kv.first] + : line_baselines_[kv.first] - line_max_spacings_[kv.first]; + boxes.emplace_back( + SkRect::MakeLTRB( + box.rect.fLeft, adjusted_top, box.rect.fRight, + line_baselines_[kv.first] + line_max_descent_[kv.first]), + box.direction); + } + } else { // kIncludeLineSpacingBottom + for (const Paragraph::TextBox& box : kv.second.boxes) { + SkScalar adjusted_bottom = + line_baselines_[kv.first] + line_max_descent_[kv.first]; + if (kv.first < line_ranges_.size() - 1) { + adjusted_bottom += + -line_max_ascent_[kv.first] + line_max_spacings_[kv.first]; + } + boxes.emplace_back(SkRect::MakeLTRB(box.rect.fLeft, + line_baselines_[kv.first] - + line_max_ascent_[kv.first], + box.rect.fRight, adjusted_bottom), box.direction); } } diff --git a/third_party/txt/src/txt/paragraph.h b/third_party/txt/src/txt/paragraph.h index 637f4e4723f3a..0518bac03326a 100644 --- a/third_party/txt/src/txt/paragraph.h +++ b/third_party/txt/src/txt/paragraph.h @@ -29,6 +29,7 @@ #include "paragraph_style.h" #include "styled_runs.h" #include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck +#include "third_party/skia/include/core/SkFontMetrics.h" #include "third_party/skia/include/core/SkRect.h" #include "utils/WindowsUtils.h" @@ -59,19 +60,36 @@ class Paragraph { // Options for various types of bounding boxes provided by // GetRectsForRange(...). - // These options can be individually enabled, for example: - // - // (RectStyle::kTight | RectStyle::kExtendEndOfLine) - // - // provides tight bounding boxes and extends the last box per line to the end - // of the layout area. - enum RectStyle { - kNone = 0x0, // kNone cannot be combined with |. - - // Provide tight bounding boxes that fit heights per span. Otherwise, the - // heights of spans are the max of the heights of the line the span belongs - // in. - kTight = 0x1 + enum class RectHeightStyle { + // Provide tight bounding boxes that fit heights per run. + kTight, + + // The height of the boxes will be the maximum height of all runs in the + // line. All rects in the same line will be the same height. + kMax, + + // Extends the top and/or bottom edge of the bounds to fully cover any line + // spacing. 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 + // ParagraphStyle line_height. + // + // The top and bottom of each rect will cover half of the + // space above and half of the space below the line. + kIncludeLineSpacingMiddle, + // The line spacing will be added to the top of the rect. + kIncludeLineSpacingTop, + // The line spacing will be added to the bottom of the rect. + kIncludeLineSpacingBottom + }; + + enum class RectWidthStyle { + // Provide tight bounding boxes that fit widths to the runs of each line + // independently. + kTight, + + // Extends the width of the last rect of each line to match the position of + // the widest rect over all the lines. + kMax }; struct PositionWithAffinity { @@ -158,7 +176,8 @@ class Paragraph { // end glyph indexes, including start and excluding end. std::vector GetRectsForRange(size_t start, size_t end, - RectStyle rect_style) const; + RectHeightStyle rect_height_style, + RectWidthStyle rect_width_style) const; // Returns the index of the glyph that corresponds to the provided coordinate, // with the top left corner as the origin, and +y direction as down. @@ -193,6 +212,7 @@ class Paragraph { FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, RightAlignParagraph); FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, CenterAlignParagraph); FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyAlignParagraph); + FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyRTL); FRIEND_TEST(ParagraphTest, DecorationsParagraph); FRIEND_TEST(ParagraphTest, ItalicsParagraph); FRIEND_TEST(ParagraphTest, ChineseParagraph); @@ -208,6 +228,7 @@ class Paragraph { FRIEND_TEST(ParagraphTest, UnderlineShiftParagraph); FRIEND_TEST(ParagraphTest, SimpleShadow); FRIEND_TEST(ParagraphTest, ComplexShadow); + FRIEND_TEST(ParagraphTest, FontFallbackParagraph); // Starting data to layout. std::vector text_; @@ -240,21 +261,42 @@ class Paragraph { std::vector line_baselines_; bool did_exceed_max_lines_; + // Metrics for use in GetRectsForRange(...); + // Per-line max metrics over all runs in a given line. + std::vector line_max_spacings_; + std::vector line_max_descent_; + std::vector line_max_ascent_; + // Overall left and right extremes over all lines. + double max_right_; + double min_left_; + class BidiRun { public: + // Constructs a BidiRun with is_ghost defaulted to false. BidiRun(size_t s, size_t e, TextDirection d, const TextStyle& st) - : start_(s), end_(e), direction_(d), style_(&st) {} + : start_(s), end_(e), direction_(d), style_(&st), is_ghost_(false) {} + + // Constructs a BidiRun with a custom is_ghost flag. + BidiRun(size_t s, + size_t e, + TextDirection d, + const TextStyle& st, + bool is_ghost) + : start_(s), end_(e), direction_(d), style_(&st), is_ghost_(is_ghost) {} size_t start() const { return start_; } size_t end() const { return end_; } TextDirection direction() const { return direction_; } const TextStyle& style() const { return *style_; } bool is_rtl() const { return direction_ == TextDirection::rtl; } + // Tracks if the run represents trailing whitespace. + bool is_ghost() const { return is_ghost_; } private: size_t start_, end_; TextDirection direction_; const TextStyle* style_; + bool is_ghost_; }; struct GlyphPosition { @@ -283,14 +325,14 @@ class Paragraph { Range code_units; Range x_pos; size_t line_number; - SkPaint::FontMetrics font_metrics; + SkFontMetrics font_metrics; TextDirection direction; CodeUnitRun(std::vector&& p, Range cu, Range x, size_t line, - const SkPaint::FontMetrics& metrics, + const SkFontMetrics& metrics, TextDirection dir); void Shift(double delta); @@ -323,6 +365,16 @@ class Paragraph { : x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {} }; + // Strut metrics of zero will have no effect on the layout. + struct StrutMetrics { + double ascent = 0; // Positive value to keep signs clear. + double descent = 0; + double leading = 0; + double half_leading = 0; + double line_height = 0; + bool force_strut = false; + }; + // Passes in the text and Styled Runs. text_ and runs_ will later be passed // into breaker_ in InitBreaker(), which is called in Layout(). void SetText(std::vector text, StyledRuns runs); @@ -337,6 +389,9 @@ class Paragraph { // Break the text into runs based on LTR/RTL text direction. bool ComputeBidiRuns(std::vector* result); + // Calculates and populates strut based on paragraph_style_ strut info. + void ComputeStrut(StrutMetrics* strut, SkFont& font); + // Calculate the starting X offset of a line based on the line's width and // alignment. double GetLineXOffset(double line_total_advance); diff --git a/third_party/txt/src/txt/paragraph_builder.cc b/third_party/txt/src/txt/paragraph_builder.cc index 1a34b367a2226..cd9d1591499d3 100644 --- a/third_party/txt/src/txt/paragraph_builder.cc +++ b/third_party/txt/src/txt/paragraph_builder.cc @@ -84,6 +84,7 @@ std::unique_ptr ParagraphBuilder::Build() { paragraph->SetText(std::move(text_), std::move(runs_)); paragraph->SetParagraphStyle(paragraph_style_); paragraph->SetFontCollection(font_collection_); + SetParagraphStyle(paragraph_style_); return paragraph; } diff --git a/third_party/txt/src/txt/paragraph_style.cc b/third_party/txt/src/txt/paragraph_style.cc index fbb89cbfccdec..933a8db6d6123 100644 --- a/third_party/txt/src/txt/paragraph_style.cc +++ b/third_party/txt/src/txt/paragraph_style.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include "paragraph_style.h" namespace txt { @@ -22,10 +24,12 @@ TextStyle ParagraphStyle::GetTextStyle() const { TextStyle result; result.font_weight = font_weight; result.font_style = font_style; - result.font_family = font_family; - result.font_size = font_size; + result.font_families = std::vector({font_family}); + if (font_size >= 0) { + result.font_size = font_size; + } result.locale = locale; - result.height = line_height; + result.height = height; return result; } diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index 3b04269d8eaee..a286b4a02327f 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -43,15 +43,31 @@ enum class TextDirection { class ParagraphStyle { public: + // Default TextStyle. Used in GetTextStyle() to obtain the base TextStyle to + // inherit off of. FontWeight font_weight = FontWeight::w400; FontStyle font_style = FontStyle::normal; std::string font_family = ""; double font_size = 14; + double height = 1; + // Strut properties. strut_enabled must be set to true for the rest of the + // properties to take effect. + // TODO(garyq): Break the strut properties into a separate class. + bool strut_enabled = false; + FontWeight strut_font_weight = FontWeight::w400; + FontStyle strut_font_style = FontStyle::normal; + std::vector strut_font_families; + double strut_font_size = 14; + double strut_height = 1; + double strut_leading = -1; // Negative to use font's default leading. [0,inf) + // to use custom leading as a ratio of font size. + bool force_strut_height = false; + + // General paragraph properties. TextAlign text_align = TextAlign::start; TextDirection text_direction = TextDirection::ltr; size_t max_lines = std::numeric_limits::max(); - double line_height = 1.0; std::u16string ellipsis; std::string locale; diff --git a/third_party/txt/src/txt/platform.cc b/third_party/txt/src/txt/platform.cc index b6a2154ddf136..411e96f0f4b9c 100644 --- a/third_party/txt/src/txt/platform.cc +++ b/third_party/txt/src/txt/platform.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/third_party/txt/src/txt/platform.h b/third_party/txt/src/txt/platform.h index ed293c1967821..d45be43b0392a 100644 --- a/third_party/txt/src/txt/platform.h +++ b/third_party/txt/src/txt/platform.h @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/third_party/txt/src/txt/platform_android.cc b/third_party/txt/src/txt/platform_android.cc index e42a25dbf8e38..600d3d5ccfd9e 100644 --- a/third_party/txt/src/txt/platform_android.cc +++ b/third_party/txt/src/txt/platform_android.cc @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/third_party/txt/src/txt/platform_mac.mm b/third_party/txt/src/txt/platform_mac.mm index 9a97e06f099cc..423edddd97c07 100644 --- a/third_party/txt/src/txt/platform_mac.mm +++ b/third_party/txt/src/txt/platform_mac.mm @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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/third_party/txt/src/txt/styled_runs.h b/third_party/txt/src/txt/styled_runs.h index c3707c0c6a35b..d123525e19cbe 100644 --- a/third_party/txt/src/txt/styled_runs.h +++ b/third_party/txt/src/txt/styled_runs.h @@ -81,6 +81,7 @@ class StyledRuns { FRIEND_TEST(ParagraphTest, Ellipsize); FRIEND_TEST(ParagraphTest, SimpleShadow); FRIEND_TEST(ParagraphTest, ComplexShadow); + FRIEND_TEST(ParagraphTest, FontFallbackParagraph); struct IndexedRun { size_t style_index = 0; diff --git a/third_party/txt/src/txt/text_style.cc b/third_party/txt/src/txt/text_style.cc index b2da694ef6a4c..194dde47882e3 100644 --- a/third_party/txt/src/txt/text_style.cc +++ b/third_party/txt/src/txt/text_style.cc @@ -22,7 +22,8 @@ namespace txt { -TextStyle::TextStyle() : font_family(GetDefaultFontFamily()) {} +TextStyle::TextStyle() + : font_families(std::vector(1, GetDefaultFontFamily())) {} bool TextStyle::equals(const TextStyle& other) const { if (color != other.color) @@ -39,8 +40,6 @@ bool TextStyle::equals(const TextStyle& other) const { return false; if (font_style != other.font_style) return false; - if (font_family != other.font_family) - return false; if (letter_spacing != other.letter_spacing) return false; if (word_spacing != other.word_spacing) @@ -53,6 +52,10 @@ bool TextStyle::equals(const TextStyle& other) const { return false; if (text_shadows.size() != other.text_shadows.size()) return false; + for (size_t font_index = 0; font_index < font_families.size(); ++font_index) { + if (font_families[font_index] != other.font_families[font_index]) + return false; + } for (size_t shadow_index = 0; shadow_index < text_shadows.size(); ++shadow_index) { if (text_shadows[shadow_index] != other.text_shadows[shadow_index]) diff --git a/third_party/txt/src/txt/text_style.h b/third_party/txt/src/txt/text_style.h index 7b0d351e8fa3f..925374ab33955 100644 --- a/third_party/txt/src/txt/text_style.h +++ b/third_party/txt/src/txt/text_style.h @@ -43,7 +43,9 @@ class TextStyle { FontWeight font_weight = FontWeight::w400; FontStyle font_style = FontStyle::normal; TextBaseline text_baseline = TextBaseline::kAlphabetic; - std::string font_family; + // An ordered list of fonts in order of priority. The first font is more + // highly preferred than the last font. + std::vector font_families; double font_size = 14.0; double letter_spacing = 0.0; double word_spacing = 0.0; @@ -53,6 +55,8 @@ class TextStyle { SkPaint background; bool has_foreground = false; SkPaint foreground; + // An ordered list of shadows where the first shadow will be drawn first (at + // the bottom). std::vector text_shadows; TextStyle(); diff --git a/third_party/txt/tests/paragraph_unittests.cc b/third_party/txt/tests/paragraph_unittests.cc index 43243da4e97e8..7d09928f44f90 100644 --- a/third_party/txt/tests/paragraph_unittests.cc +++ b/third_party/txt/tests/paragraph_unittests.cc @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include "flutter/fml/logging.h" #include "render_test.h" #include "third_party/icu/source/common/unicode/unistr.h" @@ -40,7 +42,11 @@ TEST_F(ParagraphTest, SimpleParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + // We must supply a font here, as the default is Arial, and we do not + // include Arial in our test fonts as it is proprietary. We want it to + // be Arial default though as it is one of the most common fonts on host + // platforms. On real devices/apps, Arial should be able to be resolved. + text_style.font_families = std::vector(1, "Roboto"); text_style.color = SK_ColorBLACK; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -73,7 +79,7 @@ TEST_F(ParagraphTest, SimpleRedParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.color = SK_ColorRED; builder.PushStyle(text_style); @@ -127,8 +133,9 @@ TEST_F(ParagraphTest, RainbowParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style1; + text_style1.font_families = std::vector(1, "Roboto"); text_style1.color = SK_ColorRED; - text_style1.font_family = "Roboto"; + builder.PushStyle(text_style1); builder.AddText(u16_text1); @@ -139,7 +146,7 @@ TEST_F(ParagraphTest, RainbowParagraph) { text_style2.word_spacing = 30; text_style2.font_weight = txt::FontWeight::w600; text_style2.color = SK_ColorGREEN; - text_style2.font_family = "Roboto"; + text_style2.font_families = std::vector(1, "Roboto"); text_style2.decoration = TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough; @@ -149,7 +156,7 @@ TEST_F(ParagraphTest, RainbowParagraph) { builder.AddText(u16_text2); txt::TextStyle text_style3; - text_style3.font_family = "Homemade Apple"; + text_style3.font_families = std::vector(1, "Homemade Apple"); builder.PushStyle(text_style3); builder.AddText(u16_text3); @@ -157,7 +164,7 @@ TEST_F(ParagraphTest, RainbowParagraph) { txt::TextStyle text_style4; text_style4.font_size = 14; text_style4.color = SK_ColorBLUE; - text_style4.font_family = "Roboto"; + text_style4.font_families = std::vector(1, "Roboto"); text_style4.decoration = TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough; @@ -174,7 +181,6 @@ TEST_F(ParagraphTest, RainbowParagraph) { auto paragraph = builder.Build(); paragraph->Layout(GetTestCanvasWidth()); - paragraph->Paint(GetCanvas(), 0, 0); u16_text1 += u16_text2 + u16_text3 + u16_text4; @@ -233,7 +239,7 @@ TEST_F(ParagraphTest, BoldParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 60; text_style.letter_spacing = 0; text_style.font_weight = txt::FontWeight::w900; @@ -289,7 +295,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(LeftAlignParagraph)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 26; text_style.letter_spacing = 1; text_style.word_spacing = 5; @@ -386,7 +392,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(RightAlignParagraph)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 26; text_style.letter_spacing = 1; text_style.word_spacing = 5; @@ -413,7 +419,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(RightAlignParagraph)) { ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); - ASSERT_EQ(paragraph->records_.size(), paragraph_style.max_lines); + // Two records for each due to 'ghost' trailing whitespace run. + ASSERT_EQ(paragraph->records_.size(), paragraph_style.max_lines * 2); double expected_y = 24; ASSERT_TRUE(paragraph->records_[0].style().equals(text_style)); @@ -425,39 +432,39 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(RightAlignParagraph)) { paragraph->breaker_.getWidths()[paragraph->records_[0].line()], 2.0); - ASSERT_TRUE(paragraph->records_[1].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[1].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); expected_y += 30; ASSERT_NEAR( - paragraph->records_[1].offset().x(), + paragraph->records_[2].offset().x(), paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[1].line()], + paragraph->breaker_.getWidths()[paragraph->records_[2].line()], 2.0); - ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[4].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[4].offset().y(), expected_y); expected_y += 30; ASSERT_NEAR( - paragraph->records_[2].offset().x(), + paragraph->records_[4].offset().x(), paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[2].line()], + paragraph->breaker_.getWidths()[paragraph->records_[4].line()], 2.0); - ASSERT_TRUE(paragraph->records_[3].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[3].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[6].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[6].offset().y(), expected_y); expected_y += 30 * 10; ASSERT_NEAR( - paragraph->records_[3].offset().x(), + paragraph->records_[6].offset().x(), paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[3].line()], + paragraph->breaker_.getWidths()[paragraph->records_[6].line()], 2.0); - ASSERT_TRUE(paragraph->records_[13].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[13].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[26].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[26].offset().y(), expected_y); ASSERT_NEAR( - paragraph->records_[13].offset().x(), + paragraph->records_[26].offset().x(), paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[13].line()], + paragraph->breaker_.getWidths()[paragraph->records_[26].line()], 2.0); ASSERT_EQ(paragraph_style.text_align, @@ -495,7 +502,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(CenterAlignParagraph)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 26; text_style.letter_spacing = 1; text_style.word_spacing = 5; @@ -522,7 +529,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(CenterAlignParagraph)) { ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); - ASSERT_EQ(paragraph->records_.size(), paragraph_style.max_lines); + // Two records for each due to 'ghost' trailing whitespace run. + ASSERT_EQ(paragraph->records_.size(), paragraph_style.max_lines * 2); double expected_y = 24; ASSERT_TRUE(paragraph->records_[0].style().equals(text_style)); @@ -534,39 +542,39 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(CenterAlignParagraph)) { 2, 2.0); - ASSERT_TRUE(paragraph->records_[1].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[1].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); expected_y += 30; - ASSERT_NEAR(paragraph->records_[1].offset().x(), + ASSERT_NEAR(paragraph->records_[2].offset().x(), (paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[1].line()]) / + paragraph->breaker_.getWidths()[paragraph->records_[2].line()]) / 2, 2.0); - ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[4].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[4].offset().y(), expected_y); expected_y += 30; - ASSERT_NEAR(paragraph->records_[2].offset().x(), + ASSERT_NEAR(paragraph->records_[4].offset().x(), (paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[2].line()]) / + paragraph->breaker_.getWidths()[paragraph->records_[4].line()]) / 2, 2.0); - ASSERT_TRUE(paragraph->records_[3].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[3].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[6].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[6].offset().y(), expected_y); expected_y += 30 * 10; - ASSERT_NEAR(paragraph->records_[3].offset().x(), + ASSERT_NEAR(paragraph->records_[6].offset().x(), (paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[3].line()]) / + paragraph->breaker_.getWidths()[paragraph->records_[6].line()]) / 2, 2.0); - ASSERT_TRUE(paragraph->records_[13].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[13].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[26].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[26].offset().y(), expected_y); ASSERT_NEAR( - paragraph->records_[13].offset().x(), + paragraph->records_[26].offset().x(), (paragraph->width_ - - paragraph->breaker_.getWidths()[paragraph->records_[13].line()]) / + paragraph->breaker_.getWidths()[paragraph->records_[26].line()]) / 2, 2.0); @@ -603,7 +611,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(JustifyAlignParagraph)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 26; text_style.letter_spacing = 0; text_style.word_spacing = 5; @@ -630,7 +638,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(JustifyAlignParagraph)) { ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); - ASSERT_EQ(paragraph->records_.size(), paragraph_style.max_lines); + ASSERT_EQ(paragraph->records_.size(), 27ull); double expected_y = 24; ASSERT_TRUE(paragraph->records_[0].style().equals(text_style)); @@ -638,24 +646,24 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(JustifyAlignParagraph)) { expected_y += 30; ASSERT_DOUBLE_EQ(paragraph->records_[0].offset().x(), 0); - ASSERT_TRUE(paragraph->records_[1].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[1].offset().y(), expected_y); - expected_y += 30; - ASSERT_DOUBLE_EQ(paragraph->records_[1].offset().x(), 0); - ASSERT_TRUE(paragraph->records_[2].style().equals(text_style)); ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().y(), expected_y); expected_y += 30; ASSERT_DOUBLE_EQ(paragraph->records_[2].offset().x(), 0); - ASSERT_TRUE(paragraph->records_[3].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[3].offset().y(), expected_y); + ASSERT_TRUE(paragraph->records_[4].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[4].offset().y(), expected_y); + expected_y += 30; + ASSERT_DOUBLE_EQ(paragraph->records_[4].offset().x(), 0); + + ASSERT_TRUE(paragraph->records_[6].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[6].offset().y(), expected_y); expected_y += 30 * 10; - ASSERT_DOUBLE_EQ(paragraph->records_[3].offset().x(), 0); + ASSERT_DOUBLE_EQ(paragraph->records_[6].offset().x(), 0); - ASSERT_TRUE(paragraph->records_[13].style().equals(text_style)); - ASSERT_DOUBLE_EQ(paragraph->records_[13].offset().y(), expected_y); - ASSERT_DOUBLE_EQ(paragraph->records_[13].offset().x(), 0); + ASSERT_TRUE(paragraph->records_[26].style().equals(text_style)); + ASSERT_DOUBLE_EQ(paragraph->records_[26].offset().y(), expected_y); + ASSERT_DOUBLE_EQ(paragraph->records_[26].offset().x(), 0); ASSERT_EQ(paragraph_style.text_align, paragraph->GetParagraphStyle().text_align); @@ -663,6 +671,57 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(JustifyAlignParagraph)) { ASSERT_TRUE(Snapshot()); } +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(JustifyRTL)) { + const char* text = + "אאא בּבּבּבּ אאאא בּבּ אאא בּבּבּ אאאאא בּבּבּבּ אאאא בּבּבּבּבּ " + "אאאאא בּבּבּבּבּ אאאבּבּבּבּבּבּאאאאא בּבּבּבּבּבּאאאאאבּבּבּבּבּבּ אאאאא בּבּבּבּבּ " + "אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ אאאאא בּבּבּבּבּבּ"; + + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 14; + paragraph_style.text_align = TextAlign::justify; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Ahem"); + text_style.font_size = 26; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + size_t paragraph_width = GetTestCanvasWidth() - 100; + paragraph->Layout(paragraph_width); + + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_TRUE(Snapshot()); + + auto glyph_line_width = [¶graph](int index) { + size_t second_to_last_position_index = + paragraph->glyph_lines_[index].positions.size() - 2; + return paragraph->glyph_lines_[index] + .positions[second_to_last_position_index] + .x_pos.end; + }; + + // All lines except the last should be justified to the width of the + // paragraph. + for (size_t i = 0; i < paragraph->glyph_lines_.size() - 1; ++i) { + ASSERT_EQ(glyph_line_width(i), paragraph_width); + } + ASSERT_NE(glyph_line_width(paragraph->glyph_lines_.size() - 1), + paragraph_width); +} + TEST_F(ParagraphTest, DecorationsParagraph) { txt::ParagraphStyle paragraph_style; paragraph_style.max_lines = 14; @@ -670,7 +729,7 @@ TEST_F(ParagraphTest, DecorationsParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 26; text_style.letter_spacing = 0; text_style.word_spacing = 5; @@ -681,11 +740,13 @@ TEST_F(ParagraphTest, DecorationsParagraph) { TextDecoration::kLineThrough; text_style.decoration_style = txt::TextDecorationStyle::kSolid; text_style.decoration_color = SK_ColorBLACK; + text_style.decoration_thickness_multiplier = 2.0; builder.PushStyle(text_style); builder.AddText("This text should be"); text_style.decoration_style = txt::TextDecorationStyle::kDouble; text_style.decoration_color = SK_ColorBLUE; + text_style.decoration_thickness_multiplier = 1.0; builder.PushStyle(text_style); builder.AddText(" decorated even when"); @@ -696,11 +757,13 @@ TEST_F(ParagraphTest, DecorationsParagraph) { text_style.decoration_style = txt::TextDecorationStyle::kDashed; text_style.decoration_color = SK_ColorBLACK; + text_style.decoration_thickness_multiplier = 3.0; builder.PushStyle(text_style); builder.AddText(" the next line."); text_style.decoration_style = txt::TextDecorationStyle::kWavy; text_style.decoration_color = SK_ColorRED; + text_style.decoration_thickness_multiplier = 1.0; builder.PushStyle(text_style); builder.AddText(" Otherwise, bad things happen."); @@ -741,6 +804,19 @@ TEST_F(ParagraphTest, DecorationsParagraph) { ASSERT_EQ(paragraph->records_[3].style().decoration_color, SK_ColorBLACK); ASSERT_EQ(paragraph->records_[4].style().decoration_color, SK_ColorBLACK); ASSERT_EQ(paragraph->records_[5].style().decoration_color, SK_ColorRED); + + ASSERT_EQ(paragraph->records_[0].style().decoration_thickness_multiplier, + 2.0); + ASSERT_EQ(paragraph->records_[1].style().decoration_thickness_multiplier, + 1.0); + ASSERT_EQ(paragraph->records_[2].style().decoration_thickness_multiplier, + 1.0); + ASSERT_EQ(paragraph->records_[3].style().decoration_thickness_multiplier, + 3.0); + ASSERT_EQ(paragraph->records_[4].style().decoration_thickness_multiplier, + 3.0); + ASSERT_EQ(paragraph->records_[5].style().decoration_thickness_multiplier, + 1.0); } TEST_F(ParagraphTest, ItalicsParagraph) { @@ -748,7 +824,7 @@ TEST_F(ParagraphTest, ItalicsParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.color = SK_ColorRED; text_style.font_size = 10; builder.PushStyle(text_style); @@ -795,7 +871,7 @@ TEST_F(ParagraphTest, ChineseParagraph) { text_style.color = SK_ColorBLACK; text_style.font_size = 35; text_style.letter_spacing = 2; - text_style.font_family = "Source Han Serif CN"; + text_style.font_families = std::vector(1, "Source Han Serif CN"); text_style.decoration = TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough; @@ -840,7 +916,7 @@ TEST_F(ParagraphTest, DISABLED_ArabicParagraph) { text_style.color = SK_ColorBLACK; text_style.font_size = 35; text_style.letter_spacing = 2; - text_style.font_family = "Katibeh"; + text_style.font_families = std::vector(1, "Katibeh"); text_style.decoration = TextDecoration::kUnderline | TextDecoration::kOverline | TextDecoration::kLineThrough; @@ -887,12 +963,12 @@ TEST_F(ParagraphTest, GetGlyphPositionAtCoordinateParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 50; text_style.letter_spacing = 1; text_style.word_spacing = 5; text_style.color = SK_ColorBLACK; - text_style.height = 1.5; + text_style.height = 1; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -930,11 +1006,11 @@ TEST_F(ParagraphTest, GetGlyphPositionAtCoordinateParagraph) { 18ull); ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(20, -80).position, 1ull); ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 90).position, 18ull); - ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 180).position, 36ull); + ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 170).position, 36ull); ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 180).position, - 54ull); - ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(70, 180).position, 38ull); - ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 270).position, 54ull); + 72ull); + ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(70, 180).position, 56ull); + ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(1, 270).position, 72ull); ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(35, 90).position, 19ull); ASSERT_EQ(paragraph->GetGlyphPositionAtCoordinate(10000, 10000).position, 77ull); @@ -955,7 +1031,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 50; text_style.letter_spacing = 0; text_style.font_weight = FontWeight::w500; @@ -981,15 +1057,20 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { // Tests for GetRectsForRange() // NOTE: The base truth values may still need adjustment as the specifics // are adjusted. + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; paint.setColor(SK_ColorRED); std::vector boxes = - paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kNone); + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } EXPECT_EQ(boxes.size(), 0ull); - boxes = paragraph->GetRectsForRange(0, 1, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1000,7 +1081,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); paint.setColor(SK_ColorBLUE); - boxes = paragraph->GetRectsForRange(2, 8, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(2, 8, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1011,7 +1093,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); paint.setColor(SK_ColorGREEN); - boxes = paragraph->GetRectsForRange(8, 21, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(8, 21, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1022,7 +1105,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); paint.setColor(SK_ColorRED); - boxes = paragraph->GetRectsForRange(30, 100, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(30, 100, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1040,7 +1124,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { EXPECT_FLOAT_EQ(boxes[3].rect.bottom(), 295); paint.setColor(SK_ColorBLUE); - boxes = paragraph->GetRectsForRange(19, 22, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(19, 22, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1051,7 +1136,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeParagraph)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); paint.setColor(SK_ColorRED); - boxes = paragraph->GetRectsForRange(21, 21, Paragraph::RectStyle::kNone); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1075,7 +1161,7 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Noto Sans CJK JP"; + text_style.font_families = std::vector(1, "Noto Sans CJK JP"); text_style.font_size = 50; text_style.letter_spacing = 0; text_style.font_weight = FontWeight::w500; @@ -1101,15 +1187,20 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) { // Tests for GetRectsForRange() // NOTE: The base truth values may still need adjustment as the specifics // are adjusted. + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; paint.setColor(SK_ColorRED); std::vector boxes = - paragraph->GetRectsForRange(0, 0, Paragraph::RectStyle::kTight); + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } EXPECT_EQ(boxes.size(), 0ull); - boxes = paragraph->GetRectsForRange(0, 1, Paragraph::RectStyle::kTight); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1120,7 +1211,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74); paint.setColor(SK_ColorBLUE); - boxes = paragraph->GetRectsForRange(2, 8, Paragraph::RectStyle::kTight); + boxes = + paragraph->GetRectsForRange(2, 8, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1130,7 +1222,8 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) { EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 74); paint.setColor(SK_ColorGREEN); - boxes = paragraph->GetRectsForRange(8, 21, Paragraph::RectStyle::kTight); + boxes = + paragraph->GetRectsForRange(8, 21, rect_height_style, rect_width_style); for (size_t i = 0; i < boxes.size(); ++i) { GetCanvas()->drawRect(boxes[i].rect, paint); } @@ -1143,17 +1236,15 @@ TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeTight)) { ASSERT_TRUE(Snapshot()); } -SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph, - size_t pos) { - std::vector boxes = - paragraph.GetRectsForRange(pos, pos + 1, Paragraph::RectStyle::kNone); - return !boxes.empty() ? boxes.front().rect : SkRect::MakeEmpty(); -} - -TEST_F(ParagraphTest, GetWordBoundaryParagraph) { +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeIncludeLineSpacingMiddle)) { + // const char* text = + // "12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 + // 12345 " "67890 12345"; const char* text = - "12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 " - "67890 12345"; + "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); @@ -1164,12 +1255,14 @@ TEST_F(ParagraphTest, GetWordBoundaryParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; - text_style.font_size = 52; - text_style.letter_spacing = 1.19039; - text_style.word_spacing = 5; + text_style.font_families = std::vector(1, "Roboto"); + // text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.height = 1.5; + text_style.height = 1.3; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -1185,81 +1278,125 @@ TEST_F(ParagraphTest, GetWordBoundaryParagraph) { paint.setStyle(SkPaint::kStroke_Style); paint.setAntiAlias(true); paint.setStrokeWidth(1); - paint.setColor(SK_ColorRED); - - SkRect rect = GetCoordinatesForGlyphPosition(*paragraph, 0); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(0), txt::Paragraph::Range(0, 5)); - EXPECT_EQ(paragraph->GetWordBoundary(1), txt::Paragraph::Range(0, 5)); - EXPECT_EQ(paragraph->GetWordBoundary(2), txt::Paragraph::Range(0, 5)); - EXPECT_EQ(paragraph->GetWordBoundary(3), txt::Paragraph::Range(0, 5)); - EXPECT_EQ(paragraph->GetWordBoundary(4), txt::Paragraph::Range(0, 5)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 5); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(5), txt::Paragraph::Range(5, 7)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 6); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(6), txt::Paragraph::Range(5, 7)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 7); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(7), - txt::Paragraph::Range(7, 12)); - EXPECT_EQ(paragraph->GetWordBoundary(8), - txt::Paragraph::Range(7, 12)); - EXPECT_EQ(paragraph->GetWordBoundary(9), - txt::Paragraph::Range(7, 12)); - EXPECT_EQ(paragraph->GetWordBoundary(10), - txt::Paragraph::Range(7, 12)); - EXPECT_EQ(paragraph->GetWordBoundary(11), - txt::Paragraph::Range(7, 12)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 12); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(12), - txt::Paragraph::Range(12, 13)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 13); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - - EXPECT_EQ(paragraph->GetWordBoundary(13), - txt::Paragraph::Range(13, 18)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 18); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); - rect = GetCoordinatesForGlyphPosition(*paragraph, 19); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + // Tests for GetRectsForRange() + // NOTE: The base truth values may still need adjustment as the specifics + // are adjusted. + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kIncludeLineSpacingMiddle; + Paragraph::RectWidthStyle rect_width_style = Paragraph::RectWidthStyle::kMax; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); - rect = GetCoordinatesForGlyphPosition(*paragraph, 24); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 17.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 82.958008); - rect = GetCoordinatesForGlyphPosition(*paragraph, 25); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 8, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 67.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 82.958008); - rect = GetCoordinatesForGlyphPosition(*paragraph, 30); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(8, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 508.0625); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 82.958008); - EXPECT_EQ(paragraph->GetWordBoundary(30), - txt::Paragraph::Range(30, 31)); - rect = GetCoordinatesForGlyphPosition(*paragraph, 31); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(30, 150, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 8ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 82.786133); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 525.6875); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 158.95801); + + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 525.6875); + EXPECT_FLOAT_EQ(boxes[1].rect.top(), 82.786133); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[1].rect.bottom(), 158.95801); + + EXPECT_FLOAT_EQ(boxes[2].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[2].rect.top(), 158.78613); + EXPECT_FLOAT_EQ(boxes[2].rect.right(), 531.57422); + EXPECT_FLOAT_EQ(boxes[2].rect.bottom(), 234.95801); + + EXPECT_FLOAT_EQ(boxes[3].rect.left(), 531.57422); + EXPECT_FLOAT_EQ(boxes[3].rect.top(), 158.78613); + EXPECT_FLOAT_EQ(boxes[3].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[3].rect.bottom(), 234.95801); + + EXPECT_FLOAT_EQ(boxes[4].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[4].rect.top(), 234.78613); + EXPECT_FLOAT_EQ(boxes[4].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[4].rect.bottom(), 310.95801); + + EXPECT_FLOAT_EQ(boxes[5].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[5].rect.top(), 310.78613); + EXPECT_FLOAT_EQ(boxes[5].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[5].rect.bottom(), 386.95801); - rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length() - 5); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(19, 22, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 463.72656); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 530.23047); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 82.958008); - EXPECT_EQ( - paragraph->GetWordBoundary(icu_text.length() - 1), - txt::Paragraph::Range(icu_text.length() - 5, icu_text.length())); - rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length()); - GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, SpacingParagraph) { - const char* text = "H"; +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeIncludeLineSpacingTop)) { + // const char* text = + // "12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 + // 12345 " "67890 12345"; + const char* text = + "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); @@ -1270,56 +1407,17 @@ TEST_F(ParagraphTest, SpacingParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 50; - text_style.letter_spacing = 20; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.height = 1; - builder.PushStyle(text_style); - builder.AddText(u16_text); - builder.Pop(); - - text_style.font_size = 50; - text_style.letter_spacing = 10; - text_style.word_spacing = 0; + text_style.height = 1.3; builder.PushStyle(text_style); - builder.AddText(u16_text); - builder.Pop(); - text_style.font_size = 50; - text_style.letter_spacing = 20; - text_style.word_spacing = 0; - builder.PushStyle(text_style); builder.AddText(u16_text); - builder.Pop(); - text_style.font_size = 50; - text_style.letter_spacing = 0; - text_style.word_spacing = 0; - builder.PushStyle(text_style); - builder.AddText("|"); - builder.Pop(); - - text_style.font_size = 50; - text_style.letter_spacing = 0; - text_style.word_spacing = 20; - builder.PushStyle(text_style); - builder.AddText("H "); - builder.Pop(); - - text_style.font_size = 50; - text_style.letter_spacing = 0; - text_style.word_spacing = 0; - builder.PushStyle(text_style); - builder.AddText("H "); - builder.Pop(); - - text_style.font_size = 50; - text_style.letter_spacing = 0; - text_style.word_spacing = 20; - builder.PushStyle(text_style); - builder.AddText("H "); builder.Pop(); auto paragraph = builder.Build(); @@ -1331,227 +1429,1079 @@ TEST_F(ParagraphTest, SpacingParagraph) { paint.setStyle(SkPaint::kStroke_Style); paint.setAntiAlias(true); paint.setStrokeWidth(1); - paint.setColor(SK_ColorRED); - - ASSERT_TRUE(Snapshot()); - ASSERT_EQ(paragraph->records_.size(), 7ull); - ASSERT_EQ(paragraph->records_[0].style().letter_spacing, 20); - ASSERT_EQ(paragraph->records_[1].style().letter_spacing, 10); - ASSERT_EQ(paragraph->records_[2].style().letter_spacing, 20); + // Tests for GetRectsForRange() + // NOTE: The base truth values may still need adjustment as the specifics + // are adjusted. + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kIncludeLineSpacingTop; + Paragraph::RectWidthStyle rect_width_style = Paragraph::RectWidthStyle::kMax; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); - ASSERT_EQ(paragraph->records_[4].style().word_spacing, 20); - ASSERT_EQ(paragraph->records_[5].style().word_spacing, 0); - ASSERT_EQ(paragraph->records_[6].style().word_spacing, 20); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 17.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 76); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 8, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 67.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 76); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(8, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 508.0625); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 76); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(30, 150, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 8ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 75.828125); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 525.6875); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 152); + + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 525.6875); + EXPECT_FLOAT_EQ(boxes[1].rect.top(), 75.828125); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[1].rect.bottom(), 152); + + EXPECT_FLOAT_EQ(boxes[2].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[2].rect.top(), 151.82812); + EXPECT_FLOAT_EQ(boxes[2].rect.right(), 531.57422); + EXPECT_FLOAT_EQ(boxes[2].rect.bottom(), 228); + + EXPECT_FLOAT_EQ(boxes[3].rect.left(), 531.57422); + EXPECT_FLOAT_EQ(boxes[3].rect.top(), 151.82812); + EXPECT_FLOAT_EQ(boxes[3].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[3].rect.bottom(), 228); + + EXPECT_FLOAT_EQ(boxes[4].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[4].rect.top(), 227.82812); + EXPECT_FLOAT_EQ(boxes[4].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[4].rect.bottom(), 304); + + EXPECT_FLOAT_EQ(boxes[5].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[5].rect.top(), 303.82812); + EXPECT_FLOAT_EQ(boxes[5].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[5].rect.bottom(), 380); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(19, 22, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 463.72656); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 530.23047); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 76); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, LongWordParagraph) { +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeIncludeLineSpacingBottom)) { + // const char* text = + // "12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 + // 12345 " "67890 12345"; const char* text = - "A " - "veryverylongwordtoseewherethiswillwraporifitwillatallandifitdoesthenthat" - "wouldbeagoodthingbecausethebreakingisworking."; + "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(" + " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; - paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::left; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; - text_style.font_size = 31; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.height = 1; + text_style.height = 1.3; builder.PushStyle(text_style); + builder.AddText(u16_text); builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() / 2); + paragraph->Layout(550); paragraph->Paint(GetCanvas(), 0, 0); - ASSERT_TRUE(Snapshot()); - ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); - for (size_t i = 0; i < u16_text.length(); i++) { - ASSERT_EQ(paragraph->text_[i], u16_text[i]); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + // NOTE: The base truth values may still need adjustment as the specifics + // are adjusted. + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kIncludeLineSpacingBottom; + Paragraph::RectWidthStyle rect_width_style = Paragraph::RectWidthStyle::kMax; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); } - ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); - ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); - ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); - ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); - ASSERT_EQ(paragraph->GetLineCount(), 4ull); - ASSERT_TRUE(Snapshot()); -} + EXPECT_EQ(boxes.size(), 0ull); -TEST_F(ParagraphTest, KernScaleParagraph) { - float scale = 3.0f; + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 17.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 89.916016); - txt::ParagraphStyle paragraph_style; - paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; - txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 8, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 67.429688); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 89.916016); - txt::TextStyle text_style; - text_style.font_family = "Droid Serif"; - text_style.font_size = 100 / scale; - text_style.letter_spacing = 0; - text_style.word_spacing = 0; - text_style.color = SK_ColorBLACK; - text_style.height = 1; - builder.PushStyle(text_style); - builder.AddText("AVAVAWAH A0 V0 VA To The Lo"); - builder.PushStyle(text_style); - builder.AddText("A"); - builder.PushStyle(text_style); - builder.AddText("V"); - text_style.font_size = 14 / scale; - builder.PushStyle(text_style); - builder.AddText( - " Dialog Text List lots of words to see if kerning works on a bigger set " - "of characters AVAVAW"); + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(8, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 508.0625); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 89.916016); - builder.Pop(); + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(30, 150, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 8ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 190.00781); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 89.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 525.6875); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 165.91602); + + EXPECT_FLOAT_EQ(boxes[1].rect.left(), 525.6875); + EXPECT_FLOAT_EQ(boxes[1].rect.top(), 89.744141); + EXPECT_FLOAT_EQ(boxes[1].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[1].rect.bottom(), 165.91602); + + EXPECT_FLOAT_EQ(boxes[2].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[2].rect.top(), 165.74414); + EXPECT_FLOAT_EQ(boxes[2].rect.right(), 531.57422); + EXPECT_FLOAT_EQ(boxes[2].rect.bottom(), 241.91602); + + EXPECT_FLOAT_EQ(boxes[3].rect.left(), 531.57422); + EXPECT_FLOAT_EQ(boxes[3].rect.top(), 165.74414); + EXPECT_FLOAT_EQ(boxes[3].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[3].rect.bottom(), 241.91602); + + EXPECT_FLOAT_EQ(boxes[4].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[4].rect.top(), 241.74414); + EXPECT_FLOAT_EQ(boxes[4].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[4].rect.bottom(), 317.91602); + + EXPECT_FLOAT_EQ(boxes[5].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[5].rect.top(), 317.74414); + EXPECT_FLOAT_EQ(boxes[5].rect.right(), 570.02344); + EXPECT_FLOAT_EQ(boxes[5].rect.bottom(), 393.91602); - auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() / scale); - GetCanvas()->scale(scale, scale); - paragraph->Paint(GetCanvas(), 0, 0); - GetCanvas()->scale(1.0, 1.0); - ASSERT_TRUE(Snapshot()); + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(19, 22, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 463.72656); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 13.744141); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 530.23047); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 89.916016); - EXPECT_DOUBLE_EQ(paragraph->records_[0].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[2].offset().x(), 207.37109375f); - EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().x(), 230.87109375f); - EXPECT_DOUBLE_EQ(paragraph->records_[4].offset().x(), 253.36328125f); + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(NewlineParagraph)) { - txt::ParagraphStyle paragraph_style; - paragraph_style.font_family = "Roboto"; - paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; +TEST_F(ParagraphTest, GetRectsForRangeIncludeCombiningCharacter) { + const char* text = "ดีสวัสดีชาวโลกที่น่ารัก"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::left; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; - text_style.font_size = 60; - text_style.letter_spacing = 0; - text_style.word_spacing = 0; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; + text_style.letter_spacing = 1; + text_style.word_spacing = 5; text_style.color = SK_ColorBLACK; text_style.height = 1; builder.PushStyle(text_style); - builder.AddText( - "line1\nline2 test1 test2 test3 test4 test5 test6 test7\nline3\n\nline4 " - "test1 test2 test3 test4"); + + builder.AddText(u16_text); builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() - 300); + paragraph->Layout(GetTestCanvasWidth() - 100); paragraph->Paint(GetCanvas(), 0, 0); - ASSERT_TRUE(Snapshot()); - - ASSERT_EQ(paragraph->records_.size(), 6ull); - EXPECT_DOUBLE_EQ(paragraph->records_[0].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().y(), 126); - EXPECT_DOUBLE_EQ(paragraph->records_[2].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[4].offset().x(), 0); - EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().y(), 266); - EXPECT_DOUBLE_EQ(paragraph->records_[5].offset().x(), 0); -} + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; -TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(EmojiParagraph)) { - const char* text = - "😀😃😄😁😆😅😂🤣☺😇🙂😍😡😟😢😻👽💩👍👎🙏👌👋👄👁👦👼👨‍🚀👨‍🚒🙋‍♂️👳👨‍👨‍👧‍👧\ - 💼👡👠☂🐶🐰🐻🐼🐷🐒🐵🐔🐧🐦🐋🐟🐡🕸🐌🐴🐊🐄🐪🐘🌸🌏🔥🌟🌚🌝💦💧\ - ❄🍕🍔🍟🥝🍱🕶🎩🏈⚽🚴‍♀️🎻🎼🎹🚨🚎🚐⚓🛳🚀🚁🏪🏢🖱⏰📱💾💉📉🛏🔑🔓\ - 📁🗓📊❤💯🚫🔻♠♣🕓❗🏳🏁🏳️‍🌈🇮🇹🇱🇷🇺🇸🇬🇧🇨🇳🇧🇴"; - auto icu_text = icu::UnicodeString::fromUTF8(text); - std::u16string u16_text(icu_text.getBuffer(), - icu_text.getBuffer() + icu_text.length()); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 0ull); - txt::ParagraphStyle paragraph_style; + // Case when the sentence starts with a combining character + // We should get 0 box for ด because it's already been combined to ดี + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 0ull); - txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + boxes = + paragraph->GetRectsForRange(1, 2, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); - txt::TextStyle text_style; - text_style.color = SK_ColorBLACK; - text_style.font_family = "Noto Color Emoji"; - text_style.font_size = 50; - text_style.decoration = TextDecoration::kUnderline; - builder.PushStyle(text_style); - builder.AddText(u16_text); + boxes = + paragraph->GetRectsForRange(0, 2, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); - builder.Pop(); + // Case when the sentence contains a combining character + // We should get 0 box for ว because it's already been combined to วั + boxes = + paragraph->GetRectsForRange(3, 4, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 0ull); - auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth()); + boxes = + paragraph->GetRectsForRange(4, 5, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); - paragraph->Paint(GetCanvas(), 0, 0); + boxes = + paragraph->GetRectsForRange(3, 5, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); - ASSERT_TRUE(Snapshot()); + // Case when the sentence contains a combining character that contain 3 + // characters We should get 0 box for ท and ที because it's already been + // combined to ที่ + boxes = + paragraph->GetRectsForRange(14, 15, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 0ull); - for (size_t i = 0; i < u16_text.length(); i++) { - ASSERT_EQ(paragraph->text_[i], u16_text[i]); - } + boxes = + paragraph->GetRectsForRange(15, 16, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 0ull); - ASSERT_EQ(paragraph->records_.size(), 8ull); + boxes = + paragraph->GetRectsForRange(16, 17, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); - EXPECT_EQ(paragraph->records_[0].line(), 0ull); - EXPECT_EQ(paragraph->records_[1].line(), 1ull); - EXPECT_EQ(paragraph->records_[2].line(), 2ull); - EXPECT_EQ(paragraph->records_[3].line(), 3ull); - EXPECT_EQ(paragraph->records_[7].line(), 7ull); + boxes = + paragraph->GetRectsForRange(14, 17, rect_height_style, rect_width_style); + EXPECT_EQ(boxes.size(), 1ull); } -TEST_F(ParagraphTest, HyphenBreakParagraph) { - const char* text = - "A " - "very-very-long-Hyphen-word-to-see-where-this-will-wrap-or-if-it-will-at-" - "all-and-if-it-does-thent-hat-" - "would-be-a-good-thing-because-the-breaking."; +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(GetRectsForRangeCenterParagraph)) { + const char* text = "01234   "; // includes ideographic space + // and english space. auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; - paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; - + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::center; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; - text_style.font_size = 31; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; text_style.height = 1; builder.PushStyle(text_style); + builder.AddText(u16_text); builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() / 2); + paragraph->Layout(550); paragraph->Paint(GetCanvas(), 0, 0); - ASSERT_TRUE(Snapshot()); - ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); - for (size_t i = 0; i < u16_text.length(); i++) { - ASSERT_EQ(paragraph->text_[i], u16_text[i]); - } - ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); - ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 203.95508); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 232.37305); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 4, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 260.79102); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(4, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 2ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(5, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 358.49414); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeCenterParagraphNewlineCentered)) { + const char* text = "01234\n"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::center; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 203.95508); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 232.37305); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(6, 7, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 275); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 275); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), + 75); // TODO(garyq): This value can be improved... Should be + // taller, but we need a good way to obtain a height + // without any glyphs on the line. + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, + DISABLE_ON_WINDOWS(GetRectsForRangeCenterMultiLineParagraph)) { + const char* text = "01234   \n0123  "; // includes ideographic + // space and english space. + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::center; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 203.95508); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 232.37305); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLUE); + boxes = + paragraph->GetRectsForRange(2, 4, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 260.79102); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(4, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 2ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 317.62695); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(5, 6, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 346.04492); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 0.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 358.49414); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 59); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(10, 12, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 218.16406); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 59.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 275); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 118); + + paint.setColor(SK_ColorBLACK); + boxes = + paragraph->GetRectsForRange(14, 18, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 331.83594); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 59.40625); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 419.18359); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 118); + + paint.setColor(SK_ColorRED); + boxes = + paragraph->GetRectsForRange(21, 21, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + ASSERT_TRUE(Snapshot()); +} + +SkRect GetCoordinatesForGlyphPosition(const txt::Paragraph& paragraph, + size_t pos) { + std::vector boxes = + paragraph.GetRectsForRange(pos, pos + 1, Paragraph::RectHeightStyle::kMax, + Paragraph::RectWidthStyle::kTight); + return !boxes.empty() ? boxes.front().rect : SkRect::MakeEmpty(); +} + +TEST_F(ParagraphTest, GetWordBoundaryParagraph) { + const char* text = + "12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345 " + "67890 12345"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::left; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 52; + text_style.letter_spacing = 1.19039; + text_style.word_spacing = 5; + text_style.color = SK_ColorBLACK; + text_style.height = 1.5; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + paint.setColor(SK_ColorRED); + + SkRect rect = GetCoordinatesForGlyphPosition(*paragraph, 0); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(0), txt::Paragraph::Range(0, 5)); + EXPECT_EQ(paragraph->GetWordBoundary(1), txt::Paragraph::Range(0, 5)); + EXPECT_EQ(paragraph->GetWordBoundary(2), txt::Paragraph::Range(0, 5)); + EXPECT_EQ(paragraph->GetWordBoundary(3), txt::Paragraph::Range(0, 5)); + EXPECT_EQ(paragraph->GetWordBoundary(4), txt::Paragraph::Range(0, 5)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 5); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(5), txt::Paragraph::Range(5, 7)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 6); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(6), txt::Paragraph::Range(5, 7)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 7); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(7), + txt::Paragraph::Range(7, 12)); + EXPECT_EQ(paragraph->GetWordBoundary(8), + txt::Paragraph::Range(7, 12)); + EXPECT_EQ(paragraph->GetWordBoundary(9), + txt::Paragraph::Range(7, 12)); + EXPECT_EQ(paragraph->GetWordBoundary(10), + txt::Paragraph::Range(7, 12)); + EXPECT_EQ(paragraph->GetWordBoundary(11), + txt::Paragraph::Range(7, 12)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 12); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(12), + txt::Paragraph::Range(12, 13)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 13); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(13), + txt::Paragraph::Range(13, 18)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 18); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + rect = GetCoordinatesForGlyphPosition(*paragraph, 19); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + rect = GetCoordinatesForGlyphPosition(*paragraph, 24); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + rect = GetCoordinatesForGlyphPosition(*paragraph, 25); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + rect = GetCoordinatesForGlyphPosition(*paragraph, 30); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ(paragraph->GetWordBoundary(30), + txt::Paragraph::Range(30, 31)); + rect = GetCoordinatesForGlyphPosition(*paragraph, 31); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length() - 5); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + EXPECT_EQ( + paragraph->GetWordBoundary(icu_text.length() - 1), + txt::Paragraph::Range(icu_text.length() - 5, icu_text.length())); + rect = GetCoordinatesForGlyphPosition(*paragraph, icu_text.length()); + GetCanvas()->drawLine(rect.fLeft, rect.fTop, rect.fLeft, rect.fBottom, paint); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, SpacingParagraph) { + const char* text = "H"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.text_align = TextAlign::left; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 50; + text_style.letter_spacing = 20; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + builder.AddText(u16_text); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 10; + text_style.word_spacing = 0; + builder.PushStyle(text_style); + builder.AddText(u16_text); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 20; + text_style.word_spacing = 0; + builder.PushStyle(text_style); + builder.AddText(u16_text); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + builder.PushStyle(text_style); + builder.AddText("|"); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.word_spacing = 20; + builder.PushStyle(text_style); + builder.AddText("H "); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + builder.PushStyle(text_style); + builder.AddText("H "); + builder.Pop(); + + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.word_spacing = 20; + builder.PushStyle(text_style); + builder.AddText("H "); + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + paint.setColor(SK_ColorRED); + + ASSERT_TRUE(Snapshot()); + + ASSERT_EQ(paragraph->records_.size(), 7ull); + ASSERT_EQ(paragraph->records_[0].style().letter_spacing, 20); + ASSERT_EQ(paragraph->records_[1].style().letter_spacing, 10); + ASSERT_EQ(paragraph->records_[2].style().letter_spacing, 20); + + ASSERT_EQ(paragraph->records_[4].style().word_spacing, 20); + ASSERT_EQ(paragraph->records_[5].style().word_spacing, 0); + ASSERT_EQ(paragraph->records_[6].style().word_spacing, 20); +} + +TEST_F(ParagraphTest, LongWordParagraph) { + const char* text = + "A " + "veryverylongwordtoseewherethiswillwraporifitwillatallandifitdoesthenthat" + "wouldbeagoodthingbecausethebreakingisworking."; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 31; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth() / 2); + + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_TRUE(Snapshot()); + ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); + for (size_t i = 0; i < u16_text.length(); i++) { + ASSERT_EQ(paragraph->text_[i], u16_text[i]); + } + ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); + ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); + ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); + ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); + ASSERT_EQ(paragraph->GetLineCount(), 4ull); + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, KernScaleParagraph) { + float scale = 3.0f; + + txt::ParagraphStyle paragraph_style; + paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Droid Serif"); + text_style.font_size = 100 / scale; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + builder.AddText("AVAVAWAH A0 V0 VA To The Lo"); + builder.PushStyle(text_style); + builder.AddText("A"); + builder.PushStyle(text_style); + builder.AddText("V"); + text_style.font_size = 14 / scale; + builder.PushStyle(text_style); + builder.AddText( + " Dialog Text List lots of words to see if kerning works on a bigger set " + "of characters AVAVAW"); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth() / scale); + GetCanvas()->scale(scale, scale); + paragraph->Paint(GetCanvas(), 0, 0); + GetCanvas()->scale(1.0, 1.0); + ASSERT_TRUE(Snapshot()); + + EXPECT_DOUBLE_EQ(paragraph->records_[0].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[2].offset().x(), 207.37109375f); + EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().x(), 230.87109375f); + EXPECT_DOUBLE_EQ(paragraph->records_[4].offset().x(), 253.36328125f); +} + +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(NewlineParagraph)) { + txt::ParagraphStyle paragraph_style; + paragraph_style.font_family = "Roboto"; + paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; + + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 60; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + builder.AddText( + "line1\nline2 test1 test2 test3 test4 test5 test6 test7\nline3\n\nline4 " + "test1 test2 test3 test4"); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth() - 300); + + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_TRUE(Snapshot()); + + ASSERT_EQ(paragraph->records_.size(), 6ull); + EXPECT_DOUBLE_EQ(paragraph->records_[0].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[1].offset().y(), 126); + EXPECT_DOUBLE_EQ(paragraph->records_[2].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[4].offset().x(), 0); + EXPECT_DOUBLE_EQ(paragraph->records_[3].offset().y(), 266); + EXPECT_DOUBLE_EQ(paragraph->records_[5].offset().x(), 0); +} + +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(EmojiParagraph)) { + const char* text = + "😀😃😄😁😆😅😂🤣☺😇🙂😍😡😟😢😻👽💩👍👎🙏👌👋👄👁👦👼👨‍🚀👨‍🚒🙋‍♂️👳👨‍👨‍👧‍👧\ + 💼👡👠☂🐶🐰🐻🐼🐷🐒🐵🐔🐧🐦🐋🐟🐡🕸🐌🐴🐊🐄🐪🐘🌸🌏🔥🌟🌚🌝💦💧\ + ❄🍕🍔🍟🥝🍱🕶🎩🏈⚽🚴‍♀️🎻🎼🎹🚨🚎🚐⚓🛳🚀🚁🏪🏢🖱⏰📱💾💉📉🛏🔑🔓\ + 📁🗓📊❤💯🚫🔻♠♣🕓❗🏳🏁🏳️‍🌈🇮🇹🇱🇷🇺🇸🇬🇧🇨🇳🇧🇴"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.color = SK_ColorBLACK; + text_style.font_families = std::vector(1, "Noto Color Emoji"); + text_style.font_size = 50; + text_style.decoration = TextDecoration::kUnderline; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth()); + + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_TRUE(Snapshot()); + + for (size_t i = 0; i < u16_text.length(); i++) { + ASSERT_EQ(paragraph->text_[i], u16_text[i]); + } + + ASSERT_EQ(paragraph->records_.size(), 8ull); + + EXPECT_EQ(paragraph->records_[0].line(), 0ull); + EXPECT_EQ(paragraph->records_[1].line(), 1ull); + EXPECT_EQ(paragraph->records_[2].line(), 2ull); + EXPECT_EQ(paragraph->records_[3].line(), 3ull); + EXPECT_EQ(paragraph->records_[7].line(), 7ull); +} + +TEST_F(ParagraphTest, HyphenBreakParagraph) { + const char* text = + "A " + "very-very-long-Hyphen-word-to-see-where-this-will-wrap-or-if-it-will-at-" + "all-and-if-it-does-thent-hat-" + "would-be-a-good-thing-because-the-breaking."; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.break_strategy = minikin::kBreakStrategy_HighQuality; + + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.font_size = 31; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = 1; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth() / 2); + + paragraph->Paint(GetCanvas(), 0, 0); + + ASSERT_TRUE(Snapshot()); + ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); + for (size_t i = 0; i < u16_text.length(); i++) { + ASSERT_EQ(paragraph->text_[i], u16_text[i]); + } + ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); + ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); ASSERT_EQ(paragraph->GetLineCount(), 5ull); @@ -1573,7 +2523,7 @@ TEST_F(ParagraphTest, RepeatLayoutParagraph) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.font_size = 31; text_style.letter_spacing = 0; text_style.word_spacing = 0; @@ -1633,7 +2583,7 @@ TEST_F(ParagraphTest, Ellipsize) { txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "Roboto"); text_style.color = SK_ColorBLACK; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -1675,14 +2625,14 @@ TEST_F(ParagraphTest, UnderlineShiftParagraph) { txt::TextStyle text_style1; text_style1.color = SK_ColorBLACK; - text_style1.font_family = "Roboto"; + text_style1.font_families = std::vector(1, "Roboto"); builder.PushStyle(text_style1); builder.AddText(u16_text1); txt::TextStyle text_style2; text_style2.color = SK_ColorBLACK; - text_style2.font_family = "Roboto"; + text_style2.font_families = std::vector(1, "Roboto"); text_style2.decoration = TextDecoration::kUnderline; text_style2.decoration_color = SK_ColorBLACK; builder.PushStyle(text_style2); @@ -1718,149 +2668,697 @@ TEST_F(ParagraphTest, UnderlineShiftParagraph) { paragraph->records_[1].GetRunWidth(), paragraph2->records_[0].GetRunWidth()); - auto rects1 = paragraph->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone); - auto rects2 = - paragraph2->GetRectsForRange(0, 12, Paragraph::RectStyle::kNone); + auto rects1 = + paragraph->GetRectsForRange(0, 12, Paragraph::RectHeightStyle::kMax, + Paragraph::RectWidthStyle::kTight); + auto rects2 = + paragraph2->GetRectsForRange(0, 12, Paragraph::RectHeightStyle::kMax, + Paragraph::RectWidthStyle::kTight); + + for (size_t i = 0; i < 12; ++i) { + auto r1 = GetCoordinatesForGlyphPosition(*paragraph, i); + auto r2 = GetCoordinatesForGlyphPosition(*paragraph2, i); + + ASSERT_EQ(r1.fLeft, r2.fLeft); + ASSERT_EQ(r1.fRight, r2.fRight); + } +} + +TEST_F(ParagraphTest, SimpleShadow) { + const char* text = "Hello World Text Dialog"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.color = SK_ColorBLACK; + text_style.text_shadows.emplace_back(SK_ColorBLACK, SkPoint::Make(2.0, 2.0), + 1.0); + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth()); + paragraph->Paint(GetCanvas(), 10.0, 15.0); + + ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); + for (size_t i = 0; i < u16_text.length(); i++) { + ASSERT_EQ(paragraph->text_[i], u16_text[i]); + } + ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); + ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); + ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); + ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); + + ASSERT_EQ(paragraph->records_[0].style().text_shadows.size(), 1ull); + ASSERT_EQ(paragraph->records_[0].style().text_shadows[0], + text_style.text_shadows[0]); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, ComplexShadow) { + const char* text = "Text Chunk "; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "Roboto"); + text_style.color = SK_ColorBLACK; + text_style.text_shadows.emplace_back(SK_ColorBLACK, SkPoint::Make(2.0, 2.0), + 1.0); + builder.PushStyle(text_style); + builder.AddText(u16_text); + + text_style.text_shadows.emplace_back(SK_ColorRED, SkPoint::Make(2.0, 2.0), + 5.0); + text_style.text_shadows.emplace_back(SK_ColorGREEN, SkPoint::Make(10.0, -5.0), + 3.0); + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + builder.AddText(u16_text); + + text_style.text_shadows.emplace_back(SK_ColorGREEN, SkPoint::Make(0.0, -1.0), + 0.0); + builder.PushStyle(text_style); + builder.AddText(u16_text); + + builder.Pop(); + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth()); + paragraph->Paint(GetCanvas(), 10.0, 15.0); + + ASSERT_EQ(paragraph->text_.size(), std::string{text}.length() * 5); + for (size_t i = 0; i < u16_text.length(); i++) { + ASSERT_EQ(paragraph->text_[i], u16_text[i]); + } + + ASSERT_EQ(paragraph->records_[0].style().text_shadows.size(), 1ull); + ASSERT_EQ(paragraph->records_[1].style().text_shadows.size(), 3ull); + ASSERT_EQ(paragraph->records_[2].style().text_shadows.size(), 1ull); + ASSERT_EQ(paragraph->records_[3].style().text_shadows.size(), 4ull); + ASSERT_EQ(paragraph->records_[4].style().text_shadows.size(), 1ull); + for (size_t i = 0; i < 1; ++i) + ASSERT_EQ(paragraph->records_[0].style().text_shadows[i], + text_style.text_shadows[i]); + for (size_t i = 0; i < 3; ++i) + ASSERT_EQ(paragraph->records_[1].style().text_shadows[i], + text_style.text_shadows[i]); + for (size_t i = 0; i < 1; ++i) + ASSERT_EQ(paragraph->records_[2].style().text_shadows[i], + text_style.text_shadows[i]); + for (size_t i = 0; i < 4; ++i) + ASSERT_EQ(paragraph->records_[3].style().text_shadows[i], + text_style.text_shadows[i]); + for (size_t i = 0; i < 1; ++i) + ASSERT_EQ(paragraph->records_[4].style().text_shadows[i], + text_style.text_shadows[i]); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, BaselineParagraph) { + const char* text = + "左線読設Byg後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育" + "図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 14; + paragraph_style.text_align = TextAlign::justify; + paragraph_style.height = 1.5; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.color = SK_ColorBLACK; + text_style.font_size = 55; + text_style.letter_spacing = 2; + text_style.font_families = std::vector(1, "Source Han Serif CN"); + text_style.decoration_style = txt::TextDecorationStyle::kSolid; + text_style.decoration_color = SK_ColorBLACK; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth() - 100); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + paint.setColor(SK_ColorRED); + GetCanvas()->drawLine(0, paragraph->GetIdeographicBaseline(), + paragraph->GetMaxWidth(), + paragraph->GetIdeographicBaseline(), paint); + + paint.setColor(SK_ColorGREEN); + + GetCanvas()->drawLine(0, paragraph->GetAlphabeticBaseline(), + paragraph->GetMaxWidth(), + paragraph->GetAlphabeticBaseline(), paint); + ASSERT_DOUBLE_EQ(paragraph->GetIdeographicBaseline(), 79.035000801086426); + ASSERT_DOUBLE_EQ(paragraph->GetAlphabeticBaseline(), 63.305000305175781); + + ASSERT_TRUE(Snapshot()); +} + +TEST_F(ParagraphTest, FontFallbackParagraph) { + const char* text = "Roboto 字典 "; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + const char* text2 = "Homemade Apple 字典"; + icu_text = icu::UnicodeString::fromUTF8(text2); + std::u16string u16_text2(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + const char* text3 = "Chinese 字典"; + icu_text = icu::UnicodeString::fromUTF8(text3); + std::u16string u16_text3(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + // No chinese fallback provided, should not be able to render the chinese. + text_style.font_families = std::vector(1, "Not a real font"); + text_style.font_families.push_back("Also a fake font"); + text_style.font_families.push_back("So fake it is obvious"); + text_style.font_families.push_back("Next one should be a real font..."); + text_style.font_families.push_back("Roboto"); + text_style.font_families.push_back("another fake one in between"); + text_style.font_families.push_back("Homemade Apple"); + text_style.color = SK_ColorBLACK; + builder.PushStyle(text_style); + builder.AddText(u16_text); + + // Japanese version of the chinese should be rendered. + text_style.font_families = std::vector(1, "Not a real font"); + text_style.font_families.push_back("Also a fake font"); + text_style.font_families.push_back("So fake it is obvious"); + text_style.font_families.push_back("Homemade Apple"); + text_style.font_families.push_back("Next one should be a real font..."); + text_style.font_families.push_back("Roboto"); + text_style.font_families.push_back("another fake one in between"); + text_style.font_families.push_back("Noto Sans CJK JP"); + text_style.font_families.push_back("Source Han Serif CN"); + text_style.color = SK_ColorBLACK; + builder.PushStyle(text_style); + builder.AddText(u16_text2); + + // Chinese font defiend first + text_style.font_families = std::vector(1, "Not a real font"); + text_style.font_families.push_back("Also a fake font"); + text_style.font_families.push_back("So fake it is obvious"); + text_style.font_families.push_back("Homemade Apple"); + text_style.font_families.push_back("Next one should be a real font..."); + text_style.font_families.push_back("Roboto"); + text_style.font_families.push_back("another fake one in between"); + text_style.font_families.push_back("Source Han Serif CN"); + text_style.font_families.push_back("Noto Sans CJK JP"); + text_style.color = SK_ColorBLACK; + builder.PushStyle(text_style); + builder.AddText(u16_text3); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(GetTestCanvasWidth()); + + paragraph->Paint(GetCanvas(), 10.0, 15.0); + + ASSERT_TRUE(Snapshot()); + + ASSERT_EQ(paragraph->records_.size(), 5ull); + ASSERT_DOUBLE_EQ(paragraph->records_[0].GetRunWidth(), 64.19921875); + ASSERT_DOUBLE_EQ(paragraph->records_[1].GetRunWidth(), 167.1171875); + ASSERT_DOUBLE_EQ(paragraph->records_[2].GetRunWidth(), 167.1171875); + ASSERT_DOUBLE_EQ(paragraph->records_[3].GetRunWidth(), 90.24609375); + ASSERT_DOUBLE_EQ(paragraph->records_[4].GetRunWidth(), 90.24609375); + // When a different font is resolved, then the metrics are different. + ASSERT_TRUE(paragraph->records_[2].metrics().fTop - + paragraph->records_[4].metrics().fTop != + 0); + ASSERT_TRUE(paragraph->records_[2].metrics().fAscent - + paragraph->records_[4].metrics().fAscent != + 0); + ASSERT_TRUE(paragraph->records_[2].metrics().fDescent - + paragraph->records_[4].metrics().fDescent != + 0); + ASSERT_TRUE(paragraph->records_[2].metrics().fBottom - + paragraph->records_[4].metrics().fBottom != + 0); + ASSERT_TRUE(paragraph->records_[2].metrics().fAvgCharWidth - + paragraph->records_[4].metrics().fAvgCharWidth != + 0); +} + +// Disabled due to Skia depending on platform to get metrics, which +// results in presubmit runs to have different values. +// +// TODO(garyq): Re-enable strut tests, allow font metric fakery, or +// consolidate skia font metric behavior. +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(StrutParagraph1)) { + // The chinese extra height should be absorbed by the strut. + const char* text = "01234満毎冠p来É本可\nabcd\n満毎É行p昼本可"; + auto icu_text = icu::UnicodeString::fromUTF8(text); + std::u16string u16_text(icu_text.getBuffer(), + icu_text.getBuffer() + icu_text.length()); + + txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.strut_font_families = std::vector(1, "BlahFake"); + paragraph_style.strut_font_families.push_back("ahem"); + paragraph_style.strut_font_size = 50; + paragraph_style.strut_height = 1.8; + paragraph_style.strut_leading = 0.1; + paragraph_style.strut_enabled = true; + + txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); + + txt::TextStyle text_style; + text_style.font_families = std::vector(1, "ahem"); + text_style.font_families.push_back("ahem"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; + text_style.color = SK_ColorBLACK; + text_style.height = .5; + builder.PushStyle(text_style); + + builder.AddText(u16_text); + + builder.Pop(); + + auto paragraph = builder.Build(); + paragraph->Layout(550); + + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectHeightStyle rect_height_max_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); + + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 34.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_NEAR(boxes[0].rect.bottom(), 84.5, 0.0001); + + boxes = paragraph->GetRectsForRange(0, 1, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 34.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 95); - for (size_t i = 0; i < 12; ++i) { - auto r1 = GetCoordinatesForGlyphPosition(*paragraph, i); - auto r2 = GetCoordinatesForGlyphPosition(*paragraph2, i); + boxes = + paragraph->GetRectsForRange(6, 10, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 34.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_NEAR(boxes[0].rect.bottom(), 84.5, 0.0001); + ; + + boxes = paragraph->GetRectsForRange(6, 10, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 34.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 95); - ASSERT_EQ(r1.fLeft, r2.fLeft); - ASSERT_EQ(r1.fRight, r2.fRight); + boxes = paragraph->GetRectsForRange(14, 16, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 224.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 100); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 285); + + boxes = paragraph->GetRectsForRange(20, 25, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 319.5); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 300); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 380); + + ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, SimpleShadow) { - const char* text = "Hello World Text Dialog"; +// Disabled due to Skia depending on platform to get metrics, which +// results in presubmit runs to have different values. +// +// TODO(garyq): Re-enable strut tests, allow font metric fakery, or +// consolidate skia font metric behavior. +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(StrutParagraph2)) { + // This string is all one size and smaller than the strut metrics. + const char* text = "01234ABCDEFGH\nabcd\nABCDEFGH"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.strut_font_families = std::vector(1, "ahem"); + paragraph_style.strut_font_size = 50; + paragraph_style.strut_height = 1.6; + paragraph_style.strut_enabled = true; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "ahem"); + text_style.font_families.push_back("ahem"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.text_shadows.emplace_back(SK_ColorBLACK, SkPoint::Make(2.0, 2.0), - 1.0); + text_style.height = 1; builder.PushStyle(text_style); + builder.AddText(u16_text); builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth()); - paragraph->Paint(GetCanvas(), 10.0, 15.0); + paragraph->Layout(550); - ASSERT_EQ(paragraph->text_.size(), std::string{text}.length()); - for (size_t i = 0; i < u16_text.length(); i++) { - ASSERT_EQ(paragraph->text_[i], u16_text[i]); + paragraph->Paint(GetCanvas(), 0, 0); + + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectHeightStyle rect_height_max_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); } - ASSERT_EQ(paragraph->runs_.runs_.size(), 1ull); - ASSERT_EQ(paragraph->runs_.styles_.size(), 2ull); - ASSERT_TRUE(paragraph->runs_.styles_[1].equals(text_style)); - ASSERT_EQ(paragraph->records_[0].style().color, text_style.color); + EXPECT_EQ(boxes.size(), 0ull); - ASSERT_EQ(paragraph->records_[0].style().text_shadows.size(), 1ull); - ASSERT_EQ(paragraph->records_[0].style().text_shadows[0], - text_style.text_shadows[0]); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 24, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_NEAR(boxes[0].rect.bottom(), 74, 0.0001); + + boxes = paragraph->GetRectsForRange(0, 1, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 24, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 80); + + boxes = + paragraph->GetRectsForRange(6, 10, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 24, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_NEAR(boxes[0].rect.bottom(), 74, 0.0001); + + boxes = paragraph->GetRectsForRange(6, 10, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 24, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 80); + + boxes = paragraph->GetRectsForRange(14, 16, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 184, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 100); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 240); + + boxes = paragraph->GetRectsForRange(20, 25, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 264); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 300); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 320); ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, ComplexShadow) { - const char* text = "Text Chunk "; +// Disabled due to Skia depending on platform to get metrics, which +// results in presubmit runs to have different values. +// +// TODO(garyq): Re-enable strut tests, allow font metric fakery, or +// consolidate skia font metric behavior. +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(StrutParagraph3)) { + // The strut is too small to absorb the extra chinese height, but the english + // second line height is increased due to strut. + const char* text = "01234満毎p行来昼本可\nabcd\n満毎冠行来昼本可"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; + paragraph_style.max_lines = 10; + paragraph_style.strut_font_families = std::vector(1, "ahem"); + paragraph_style.strut_font_size = 50; + paragraph_style.strut_height = 1.2; + paragraph_style.strut_enabled = true; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; - text_style.font_family = "Roboto"; + text_style.font_families = std::vector(1, "ahem"); + text_style.font_families.push_back("ahem"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.font_weight = FontWeight::w500; + text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.text_shadows.emplace_back(SK_ColorBLACK, SkPoint::Make(2.0, 2.0), - 1.0); + text_style.height = 1; builder.PushStyle(text_style); - builder.AddText(u16_text); - text_style.text_shadows.emplace_back(SK_ColorRED, SkPoint::Make(2.0, 2.0), - 5.0); - text_style.text_shadows.emplace_back(SK_ColorGREEN, SkPoint::Make(10.0, -5.0), - 3.0); - builder.PushStyle(text_style); builder.AddText(u16_text); builder.Pop(); - builder.AddText(u16_text); - text_style.text_shadows.emplace_back(SK_ColorGREEN, SkPoint::Make(0.0, -1.0), - 0.0); - builder.PushStyle(text_style); - builder.AddText(u16_text); + auto paragraph = builder.Build(); + paragraph->Layout(550); - builder.Pop(); - builder.AddText(u16_text); + paragraph->Paint(GetCanvas(), 0, 0); - builder.Pop(); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setAntiAlias(true); + paint.setStrokeWidth(1); - auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth()); - paragraph->Paint(GetCanvas(), 10.0, 15.0); + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectHeightStyle rect_height_max_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; + paint.setColor(SK_ColorRED); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); - ASSERT_EQ(paragraph->text_.size(), std::string{text}.length() * 5); - for (size_t i = 0; i < u16_text.length(); i++) { - ASSERT_EQ(paragraph->text_[i], u16_text[i]); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 8, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_NEAR(boxes[0].rect.bottom(), 58, 0.0001); - ASSERT_EQ(paragraph->records_[0].style().text_shadows.size(), 1ull); - ASSERT_EQ(paragraph->records_[1].style().text_shadows.size(), 3ull); - ASSERT_EQ(paragraph->records_[2].style().text_shadows.size(), 1ull); - ASSERT_EQ(paragraph->records_[3].style().text_shadows.size(), 4ull); - ASSERT_EQ(paragraph->records_[4].style().text_shadows.size(), 1ull); - for (size_t i = 0; i < 1; ++i) - ASSERT_EQ(paragraph->records_[0].style().text_shadows[i], - text_style.text_shadows[i]); - for (size_t i = 0; i < 3; ++i) - ASSERT_EQ(paragraph->records_[1].style().text_shadows[i], - text_style.text_shadows[i]); - for (size_t i = 0; i < 1; ++i) - ASSERT_EQ(paragraph->records_[2].style().text_shadows[i], - text_style.text_shadows[i]); - for (size_t i = 0; i < 4; ++i) - ASSERT_EQ(paragraph->records_[3].style().text_shadows[i], - text_style.text_shadows[i]); - for (size_t i = 0; i < 1; ++i) - ASSERT_EQ(paragraph->records_[4].style().text_shadows[i], - text_style.text_shadows[i]); + boxes = paragraph->GetRectsForRange(0, 1, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 8, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 60); + + boxes = + paragraph->GetRectsForRange(6, 10, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 8, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_NEAR(boxes[0].rect.bottom(), 58, 0.0001); + + boxes = paragraph->GetRectsForRange(6, 10, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 8, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 60); + + boxes = paragraph->GetRectsForRange(14, 16, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 128); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 100); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 180); + + boxes = paragraph->GetRectsForRange(20, 25, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 188); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 300); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 240); ASSERT_TRUE(Snapshot()); } -TEST_F(ParagraphTest, BaselineParagraph) { - const char* text = - "左線読設Byg後碁給能上目秘使約。満毎冠行来昼本可必図将発確年。今属場育" - "図情闘陰野高備込制詩西校客。審対江置講今固残必託地集済決維駆年策。立得"; +// Disabled due to Skia depending on platform to get metrics, which +// results in presubmit runs to have different values. +// +// TODO(garyq): Re-enable strut tests, allow font metric fakery, or +// consolidate skia font metric behavior. +TEST_F(ParagraphTest, DISABLE_ON_WINDOWS(StrutForceParagraph)) { + // The strut is too small to absorb the extra chinese height, but the english + // second line height is increased due to strut. + const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可"; auto icu_text = icu::UnicodeString::fromUTF8(text); std::u16string u16_text(icu_text.getBuffer(), icu_text.getBuffer() + icu_text.length()); txt::ParagraphStyle paragraph_style; - paragraph_style.max_lines = 14; - paragraph_style.text_align = TextAlign::justify; - paragraph_style.line_height = 1.5; + paragraph_style.max_lines = 10; + paragraph_style.strut_font_families = std::vector(1, "ahem"); + paragraph_style.strut_font_size = 50; + paragraph_style.strut_height = 1.5; + paragraph_style.strut_leading = 0.1; + paragraph_style.force_strut_height = true; + paragraph_style.strut_enabled = true; txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection()); txt::TextStyle text_style; + text_style.font_families = std::vector(1, "ahem"); + text_style.font_families.push_back("ahem"); + text_style.font_size = 50; + text_style.letter_spacing = 0; + text_style.word_spacing = 0; text_style.color = SK_ColorBLACK; - text_style.font_size = 55; - text_style.letter_spacing = 2; - text_style.font_family = "Source Han Serif CN"; - text_style.decoration_style = txt::TextDecorationStyle::kSolid; - text_style.decoration_color = SK_ColorBLACK; + text_style.height = 1; builder.PushStyle(text_style); builder.AddText(u16_text); @@ -1868,7 +3366,7 @@ TEST_F(ParagraphTest, BaselineParagraph) { builder.Pop(); auto paragraph = builder.Build(); - paragraph->Layout(GetTestCanvasWidth() - 100); + paragraph->Layout(550); paragraph->Paint(GetCanvas(), 0, 0); @@ -1876,18 +3374,88 @@ TEST_F(ParagraphTest, BaselineParagraph) { paint.setStyle(SkPaint::kStroke_Style); paint.setAntiAlias(true); paint.setStrokeWidth(1); + + // Tests for GetRectsForRange() + Paragraph::RectHeightStyle rect_height_style = + Paragraph::RectHeightStyle::kTight; + Paragraph::RectHeightStyle rect_height_max_style = + Paragraph::RectHeightStyle::kMax; + Paragraph::RectWidthStyle rect_width_style = + Paragraph::RectWidthStyle::kTight; paint.setColor(SK_ColorRED); - GetCanvas()->drawLine(0, paragraph->GetIdeographicBaseline(), - paragraph->GetMaxWidth(), - paragraph->GetIdeographicBaseline(), paint); + std::vector boxes = + paragraph->GetRectsForRange(0, 0, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 0ull); - paint.setColor(SK_ColorGREEN); + boxes = + paragraph->GetRectsForRange(0, 1, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 22.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_NEAR(boxes[0].rect.bottom(), 72.5, 0.0001); - GetCanvas()->drawLine(0, paragraph->GetAlphabeticBaseline(), - paragraph->GetMaxWidth(), - paragraph->GetAlphabeticBaseline(), paint); - ASSERT_DOUBLE_EQ(paragraph->GetIdeographicBaseline(), 79.035003662109375); - ASSERT_DOUBLE_EQ(paragraph->GetAlphabeticBaseline(), 63.305000305175781); + boxes = paragraph->GetRectsForRange(0, 1, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 22.5, 0.0001); + ; + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 80); + + boxes = + paragraph->GetRectsForRange(6, 10, rect_height_style, rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 22.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_NEAR(boxes[0].rect.bottom(), 72.5, 0.0001); + + boxes = paragraph->GetRectsForRange(6, 10, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 300); + EXPECT_NEAR(boxes[0].rect.top(), 22.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 500); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 80); + + boxes = paragraph->GetRectsForRange(14, 16, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 0); + EXPECT_NEAR(boxes[0].rect.top(), 182.5, 0.0001); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 100); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 240); + + boxes = paragraph->GetRectsForRange(20, 25, rect_height_max_style, + rect_width_style); + for (size_t i = 0; i < boxes.size(); ++i) { + GetCanvas()->drawRect(boxes[i].rect, paint); + } + EXPECT_EQ(boxes.size(), 1ull); + EXPECT_FLOAT_EQ(boxes[0].rect.left(), 50); + EXPECT_FLOAT_EQ(boxes[0].rect.top(), 262.5); + EXPECT_FLOAT_EQ(boxes[0].rect.right(), 300); + EXPECT_FLOAT_EQ(boxes[0].rect.bottom(), 320); ASSERT_TRUE(Snapshot()); } diff --git a/third_party/txt/third_party/fonts/ahem.ttf b/third_party/txt/third_party/fonts/ahem.ttf new file mode 100644 index 0000000000000..306f065ef5bf1 Binary files /dev/null and b/third_party/txt/third_party/fonts/ahem.ttf differ diff --git a/tools/android_lint/.gitignore b/tools/android_lint/.gitignore new file mode 100644 index 0000000000000..e44752dc2001b --- /dev/null +++ b/tools/android_lint/.gitignore @@ -0,0 +1 @@ +lint_report/ diff --git a/tools/android_lint/baseline.xml b/tools/android_lint/baseline.xml new file mode 100644 index 0000000000000..bc6c463610096 --- /dev/null +++ b/tools/android_lint/baseline.xml @@ -0,0 +1,499 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/android_lint/bin/main.dart b/tools/android_lint/bin/main.dart new file mode 100644 index 0000000000000..f9a9e4359dc28 --- /dev/null +++ b/tools/android_lint/bin/main.dart @@ -0,0 +1,224 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:path/path.dart' as path; +import 'package:process/process.dart'; + +const LocalProcessManager processManager = LocalProcessManager(); + +/// Runs the Android SDK Lint tool on flutter/shell/platform/android. +/// +/// This script scans the flutter/shell/platform/android directory for Java +/// files to build a `project.xml` file. This file is then passed to the lint +/// tool. If an `--html` flag is also passed in, HTML output is reqeusted in the +/// directory for the optional `--out` parameter, which defaults to +/// `lint_report`. Otherwise the output is printed to STDOUT. +/// +/// The `--in` parameter may be specified to force this script to scan a +/// specific location for the engine repository, and expects to be given the +/// `src` directory that contains both `third_party` and `flutter`. +/// +/// At the time of this writing, the Android Lint tool doesn't work well with +/// Java > 1.8. This script will print a warning if you are not running +/// Java 1.8. +Future main(List args) async { + final ArgParser argParser = setupOptions(); + await checkJava1_8(); + final int exitCode = await runLint(argParser, argParser.parse(args)); + exit(exitCode); +} + +Future runLint(ArgParser argParser, ArgResults argResults) async { + final Directory androidDir = Directory(path.join( + argResults['in'], + 'flutter', + 'shell', + 'platform', + 'android', + )); + if (!androidDir.existsSync()) { + print('This command must be run from the engine/src directory, ' + 'or be passed that directory as the --in parameter.\n'); + print(argParser.usage); + return -1; + } + + final Directory androidSdkDir = Directory( + path.join(argResults['in'], 'third_party', 'android_tools', 'sdk'), + ); + + if (!androidSdkDir.existsSync()) { + print('The Android SDK for this engine is missing from the ' + 'third_party/android_tools directory. Have you run gclient sync?\n'); + print(argParser.usage); + return -1; + } + + if (argResults['rebaseline']) { + print('Removing previous baseline.xml...'); + final File baselineXml = File(baselineXmlPath); + if (baselineXml.existsSync()) { + await baselineXml.delete(); + } + } + print('Preparing projext.xml...'); + final IOSink projectXml = File(projectXmlPath).openWrite(); + projectXml.write( + ''' + + + + + +'''); + for (final FileSystemEntity entity in androidDir.listSync(recursive: true)) { + if (!entity.path.endsWith('.java')) { + continue; + } + projectXml.writeln(' '); + } + + projectXml.write(''' + +'''); + await projectXml.close(); + + print('Wrote project.xml, starting lint...'); + final List lintArgs = [ + path.join(androidSdkDir.path, 'tools', 'bin', 'lint'), + '--project', + projectXmlPath, + '--showall', + '--exitcode', // Set non-zero exit code on errors + '-Wall', + '-Werror', + '--baseline', + baselineXmlPath, + ]; + if (argResults['html']) { + lintArgs.addAll(['--html', argResults['out']]); + } + final String javaHome = await getJavaHome(); + final Process lintProcess = await processManager.start( + lintArgs, + environment: javaHome != null + ? { + 'JAVA_HOME': javaHome, + } + : null, + ); + lintProcess.stdout.pipe(stdout); + lintProcess.stderr.pipe(stderr); + return await lintProcess.exitCode; +} + +/// Prepares an [ArgParser] for this script. +ArgParser setupOptions() { + final ArgParser argParser = ArgParser(); + argParser + ..addOption( + 'in', + help: 'The path to `engine/src`.', + defaultsTo: path.relative( + path.join( + projectDir, + '..', + '..', + '..', + ), + ), + ) + ..addFlag( + 'help', + help: 'Print usage of the command.', + negatable: false, + defaultsTo: false, + ) + ..addFlag( + 'rebaseline', + help: 'Recalculates the baseline for errors and warnings ' + 'in this project.', + negatable: false, + defaultsTo: false, + ) + ..addFlag( + 'html', + help: 'Creates an HTML output for this report instead of printing ' + 'command line output.', + negatable: false, + defaultsTo: false, + ) + ..addOption( + 'out', + help: 'The path to write the generated the HTML report to. Ignored if ' + '--html is not also true.', + defaultsTo: path.join(projectDir, 'lint_report'), + ); + + return argParser; +} + +/// On macOS, we can try to find Java 1.8. +/// +/// Otherwise, default to whatever JAVA_HOME is already. +Future getJavaHome() async { + if (Platform.isMacOS) { + final ProcessResult result = await processManager.run( + ['/usr/libexec/java_home', '-v', '1.8', '-F'], + ); + if (result.exitCode == 0) { + return result.stdout.trim(); + } + } + return Platform.environment['JAVA_HOME']; +} + +/// Checks that `java` points to Java 1.8. +/// +/// The SDK lint tool may not work with Java > 1.8. +Future checkJava1_8() async { + print('Checking Java version...'); + + if (Platform.isMacOS) { + final ProcessResult result = await processManager.run( + ['/usr/libexec/java_home', '-v', '1.8', '-F'], + ); + if (result.exitCode != 0) { + print('Java 1.8 not available - the linter may not work properly.'); + } + return; + } + final ProcessResult javaResult = await processManager.run( + ['java', '-version'], + ); + if (javaResult.exitCode != 0) { + print('Could not run "java -version". ' + 'Ensure Java is installed and available on your path.'); + print(javaResult.stderr); + } + // `java -version` writes to stderr. + final String javaVersionStdout = javaResult.stderr; + if (!javaVersionStdout.contains('"1.8')) { + print('The Android SDK tools may not work properly with your Java version. ' + 'If this process fails, please retry using Java 1.8.'); + } +} + +/// The root directory of this project. +String get projectDir => path.dirname( + path.dirname( + path.fromUri(Platform.script), + ), + ); + +/// The path to use for project.xml, which tells the linter where to find source +/// files. +String get projectXmlPath => path.join(projectDir, 'project.xml'); + +/// The path to use for baseline.xml, which tells the linter what errors or +/// warnings to ignore. +String get baselineXmlPath => path.join(projectDir, 'baseline.xml'); diff --git a/tools/android_lint/lint.xml b/tools/android_lint/lint.xml new file mode 100644 index 0000000000000..241207161d5d6 --- /dev/null +++ b/tools/android_lint/lint.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/tools/android_lint/project.xml b/tools/android_lint/project.xml new file mode 100644 index 0000000000000..6d8fffe439f3e --- /dev/null +++ b/tools/android_lint/project.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/android_lint/pubspec.yaml b/tools/android_lint/pubspec.yaml new file mode 100644 index 0000000000000..b27131c37c920 --- /dev/null +++ b/tools/android_lint/pubspec.yaml @@ -0,0 +1,5 @@ +name: android_lint +dependencies: + args: 1.5.0 + path: ^1.6.2 + process: ^3.0.9 diff --git a/tools/android_support/VERSION_SUPPORT_FRAGMENT b/tools/android_support/VERSION_SUPPORT_FRAGMENT deleted file mode 100644 index f7d827f42df7f..0000000000000 --- a/tools/android_support/VERSION_SUPPORT_FRAGMENT +++ /dev/null @@ -1 +0,0 @@ -https://maven.google.com/com/android/support/support-fragment/28.0.0/support-fragment-28.0.0.aar diff --git a/tools/android_support/download_android_support.py b/tools/android_support/download_android_support.py index 57a04afbab418..e622f37ba0cd7 100644 --- a/tools/android_support/download_android_support.py +++ b/tools/android_support/download_android_support.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2018 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -8,40 +8,48 @@ import urllib2 import cStringIO import zipfile +import json # Path constants. (All of these should be absolute paths.) THIS_DIR = os.path.abspath(os.path.dirname(__file__)) FLUTTER_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..')) INSTALL_DIR = os.path.join(FLUTTER_DIR, 'third_party', 'android_support') -def GetInstalledVersion(version_stamp): - version_file = os.path.join(INSTALL_DIR, version_stamp) +def GetInstalledVersion(out_file_name): + version_file = os.path.join(INSTALL_DIR, out_file_name + '.stamp') if not os.path.exists(version_file): return None with open(version_file) as f: return f.read().strip() -def main(): +def getFile(url, out_file_name): # Read latest version. - version_stamp = 'VERSION_SUPPORT_FRAGMENT' - version = '' - with open(os.path.join(THIS_DIR, version_stamp)) as f: - version = f.read().strip() - # Return if installed binaries are up to date. - if version == GetInstalledVersion(version_stamp): + if url == GetInstalledVersion(out_file_name): return - # Download the AAR and extract the JAR. - aar = urllib2.urlopen(version).read() - aar_zip = zipfile.ZipFile(cStringIO.StringIO(aar)) + downloaded_file = urllib2.urlopen(url).read() if not os.path.exists(INSTALL_DIR): - os.mkdir(INSTALL_DIR) - with open(os.path.join(INSTALL_DIR, 'android_support_fragment.jar'), 'w') as f: - f.write(aar_zip.read('classes.jar')) + os.mkdir(INSTALL_DIR) + + if (url.endswith('.aar')): + aar_zip = zipfile.ZipFile(cStringIO.StringIO(downloaded_file)) + with open(os.path.join(INSTALL_DIR, out_file_name), 'w') as f: + f.write(aar_zip.read('classes.jar')) + else: + with open(os.path.join(INSTALL_DIR, out_file_name), 'w') as f: + f.write(downloaded_file) # Write version as the last step. - with open(os.path.join(INSTALL_DIR, version_stamp), 'w') as f: - f.write('%s\n' % version) + with open(os.path.join(INSTALL_DIR, out_file_name + '.stamp'), 'w') as f: + f.write('%s\n' % url) + + +def main(): + with open (os.path.join(THIS_DIR, 'files.json')) as f: + files = json.load(f) + + for file, url in files.items(): + getFile(url, file) if __name__ == '__main__': sys.exit(main()) diff --git a/tools/android_support/files.json b/tools/android_support/files.json new file mode 100644 index 0000000000000..6c673e7fff8e6 --- /dev/null +++ b/tools/android_support/files.json @@ -0,0 +1,8 @@ +{ + "android_arch_lifecycle_common.jar": "https://dl.google.com/dl/android/maven2/android/arch/lifecycle/common/1.1.1/common-1.1.1.jar", + "android_arch_lifecycle_viewmodel.jar": "https://dl.google.com/dl/android/maven2/android/arch/lifecycle/viewmodel/1.1.1/viewmodel-1.1.1.aar", + "android_support_fragment.jar": "https://dl.google.com/dl/android/maven2/com/android/support/support-fragment/28.0.0/support-fragment-28.0.0.aar", + "android_support_v13.jar": "https://dl.google.com/dl/android/maven2/com/android/support/support-v13/28.0.0/support-v13-28.0.0.aar", + "android_support_annotations.jar": "https://dl.google.com/dl/android/maven2/com/android/support/support-annotations/28.0.0/support-annotations-28.0.0.jar", + "android_support_compat.jar": "https://dl.google.com/dl/android/maven2/com/android/support/support-compat/28.0.0/support-compat-28.0.0.aar" +} \ No newline at end of file diff --git a/tools/create_ndk_cipd_package.sh b/tools/create_ndk_cipd_package.sh new file mode 100755 index 0000000000000..7651b195735c4 --- /dev/null +++ b/tools/create_ndk_cipd_package.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# This script requires depot_tools to be on path. + +print_usage () { + echo "Usage: create_ndk_cipd_package.sh " + echo " where:" + echo " - PATH_TO_NDK_ASSETS is the path to the unzipped NDK folder" + echo " - PLATFORM_NAME is one of linux-amd64, mac-amd64, or windows-amd64" + echo " - VERSION_TAG is the version of the NDK, e.g. r19b" +} + +if [[ $3 == "" ]]; then + print_usage + exit 1 +fi + +if [[ ! -d "$1" ]]; then + echo "Directory $1 not found." + print_usage + exit 1 +fi + +if [[ $2 != "linux-amd64" && $2 != "mac-amd64" && $2 != "windows-amd64" ]]; then + echo "Unsupported platform $2." + echo "Valid options are linux-amd64, mac-amd64, windows-amd64." + print_usage + exit 1 +fi + +cipd create -in $1 -name flutter/android/ndk/$2 -install-mode copy -tag version:$3 diff --git a/tools/create_sdk_cipd_package.sh b/tools/create_sdk_cipd_package.sh new file mode 100755 index 0000000000000..7f1ed33ad9fb9 --- /dev/null +++ b/tools/create_sdk_cipd_package.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# This script requires depot_tools to be on path. + +print_usage () { + echo "Usage: create_ndk_cipd_package.sh " + echo " where:" + echo " - PACKAGE_TYPE is one of build-tools, platform-tools, platforms, or tools" + echo " - PATH_TO_ASSETS is the path to the unzipped asset folder" + echo " - PLATFORM_NAME is one of linux-amd64, mac-amd64, or windows-amd64" + echo " - VERSION_TAG is the version of the package, e.g. 28r6 or 28.0.3" +} + +if [[ $4 == "" ]]; then + print_usage + exit 1 +fi + +if [[ $1 != "build-tools" && $1 != "platform-tools" && $1 != "platforms" && $1 != "tools" ]]; then + echo "Unrecognized paackage type $1." + print_usage + exit 1 +fi + +if [[ ! -d "$2" ]]; then + echo "Directory $1 not found." + print_usage + exit 1 +fi + +if [[ $1 != "platforms" && $3 != "linux-amd64" && $3 != "mac-amd64" && $3 != "windows-amd64" ]]; then + echo "Unsupported platform $3." + echo "Valid options are linux-amd64, mac-amd64, windows-amd64." + print_usage + exit 1 +fi + +if [[ $1 == "platforms" ]]; then + echo "Ignoring PLATFORM_NAME - this package is cross-platform." + cipd create -in $2 -name flutter/android/sdk/$1 -install-mode copy -tag version:$4 +else + cipd create -in $2 -name flutter/android/sdk/$1/$3 -install-mode copy -tag version:$4 +fi diff --git a/tools/engine_roll_pr_desc.sh b/tools/engine_roll_pr_desc.sh new file mode 100755 index 0000000000000..6965301a327c9 --- /dev/null +++ b/tools/engine_roll_pr_desc.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [[ $1 == '' ]]; then + echo 'Usage: engine_roll_pr_desc.sh ..' + exit 1 +fi +git log --oneline --no-merges --no-color $1 | sed 's/^/flutter\/engine@/g' | sed -e 's/(\(#[0-9]*)\)/\(flutter\/engine\1/g' diff --git a/tools/gen_javadoc.py b/tools/gen_javadoc.py index fbc28a8682e0c..f38f059462f3b 100755 --- a/tools/gen_javadoc.py +++ b/tools/gen_javadoc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -25,8 +25,13 @@ def main(): classpath = [ ANDROID_SRC_ROOT, - 'third_party/android_tools/sdk/extras/android/support/v4/android-support-v4.jar', - 'third_party/android_tools/sdk/platforms/android-22/android.jar', + 'third_party/android_support/android_arch_lifecycle_common.jar', + 'third_party/android_support/android_arch_lifecycle_viewmodel.jar', + 'third_party/android_support/android_support_annotations.jar', + 'third_party/android_support/android_support_compat.jar', + 'third_party/android_support/android_support_fragment.jar', + 'third_party/android_support/android_support_v13.jar', + 'third_party/android_tools/sdk/platforms/android-28/android.jar', 'base/android/java/src', 'third_party/jsr-305/src/ri/src/main/java', ] diff --git a/tools/gen_locale.dart b/tools/gen_locale.dart index 47d256c750858..bb3751fa3d8a4 100644 --- a/tools/gen_locale.dart +++ b/tools/gen_locale.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,7 +7,7 @@ // When running this script, use the output of this script to update the // comments that say when the script was last run (that comment appears twice in -// window.dart), and then replace all the "case" statements with the output from +// window.dart), and then replace all the map entries with the output from // this script (the first set for _canonicalizeLanguageCode and the second set // for _canonicalizeRegionCode). @@ -60,7 +60,7 @@ Future main() async { assert(section.containsKey('Deprecated')); final String comment = section.containsKey('Comment') ? section['Comment'].single : 'deprecated ${section['Deprecated'].single}'; final String preferredValue = section['Preferred-Value'].single; - outputs[type].add('case \'$subtag\': return \'$preferredValue\'; // ${descriptions.join(", ")}; $comment'); + outputs[type].add('\'$subtag\': \'$preferredValue\', // ${descriptions.join(", ")}; $comment'); } } print('// Mappings generated for language subtag registry as of $fileDate.'); diff --git a/tools/gen_objcdoc.sh b/tools/gen_objcdoc.sh index 0f921957e8fd3..2ec7a21913394 100755 --- a/tools/gen_objcdoc.sh +++ b/tools/gen_objcdoc.sh @@ -1,22 +1,29 @@ #!/usr/bin/env bash -# Copyright 2017 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Generates objc docs for Flutter iOS libraries. -if [ ! -d "shell/platform/darwin/ios" ] +if [[ ! -d "shell/platform/darwin/ios" ]] then echo "Error: This script must be run at the root of the Flutter source tree." exit 1 fi -if [ $# -eq 0 ] +if [[ $# -eq 0 ]] then echo "Error: Argument specifying output directory required." exit 1 fi +# If GEM_HOME is set, prefer using its copy of jazzy. +# LUCI will put jazzy here instead of on the path. +if [[ -n "${GEM_HOME}" ]] + then + PATH="${GEM_HOME}/bin:$PATH" +fi + # Use iPhoneSimulator SDK # See: https://github.com/realm/jazzy/issues/791 jazzy \ @@ -28,8 +35,7 @@ jazzy \ --github_url 'https://github.com/flutter'\ --github-file-prefix 'http://github.com/flutter/engine/blob/master'\ --module-version 1.0.0\ - --xcodebuild-arguments --objc,shell/platform/darwin/ios/framework/Headers/Flutter.h,--,-x,objective-c,-isysroot,$(xcrun --show-sdk-path --sdk iphonesimulator),-I,$(pwd)\ + --xcodebuild-arguments --objc,shell/platform/darwin/ios/framework/Headers/Flutter.h,--,-x,objective-c,-isysroot,"$(xcrun --show-sdk-path --sdk iphonesimulator)",-I,"$(pwd)"\ --module Flutter\ --root-url https://docs.flutter.io/objc/\ - --output $1\ - --no-download-badge + --output "$1" diff --git a/tools/generate_package_files.py b/tools/generate_package_files.py index b32044d272f2f..39dc60d20905d 100644 --- a/tools/generate_package_files.py +++ b/tools/generate_package_files.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2018 The Flutter Authors. All rights reserved. +# Copyright 2013 The Flutter 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/tools/gn b/tools/gn index f3d631a0c364e..7dacacf0651a4 100755 --- a/tools/gn +++ b/tools/gn @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -43,7 +43,7 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') - return os.path.join('out', '_'.join(target_dir)) + return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): def merge(key, value): @@ -91,8 +91,16 @@ def to_gn_args(args): gn_args['android_full_debug'] = args.target_os == 'android' and args.unoptimized gn_args['is_clang'] = not sys.platform.startswith(('cygwin', 'win')) + if not sys.platform.startswith(('cygwin', 'win')): + gn_args['use_clang_static_analyzer'] = args.clang_static_analyzer + gn_args['embedder_for_target'] = args.embedder_for_target + gn_args['enable_coverage'] = args.coverage + + if args.operator_new_alignment is not None: + gn_args['operator_new_alignment'] = args.operator_new_alignment + enable_lto = args.lto if args.unoptimized: # There is no point in enabling LTO in unoptimized builds. @@ -129,6 +137,10 @@ def to_gn_args(args): if args.dart_debug: gn_args['dart_debug'] = True + if args.full_dart_debug: + gn_args['dart_debug'] = True + gn_args['dart_debug_optimization_level'] = '0' + if args.target_os == 'android': gn_args['target_cpu'] = args.android_cpu elif args.target_os == 'ios': @@ -194,6 +206,11 @@ def to_gn_args(args): goma_dir = os.environ.get('GOMA_DIR') goma_home_dir = os.path.join(os.getenv('HOME', ''), 'goma') + + # GOMA has a different default (home) path on gWindows. + if not os.path.exists(goma_home_dir) and sys.platform.startswith(('cygwin', 'win')): + goma_home_dir = os.path.join('c:\\', 'src', 'goma', 'goma-win64') + if args.goma and goma_dir: gn_args['use_goma'] = True gn_args['goma_dir'] = goma_dir @@ -209,7 +226,6 @@ def to_gn_args(args): gn_args['shell_enable_vulkan'] = True # Configure Skia for Vulkan support. gn_args['skia_use_vulkan'] = True - gn_args['skia_vulkan_header'] = "flutter/vulkan/skia_vulkan_header.h" # We should not need a special case for x86, but this seems to introduce text relocations # even with -fPIC everywhere. @@ -218,6 +234,16 @@ def to_gn_args(args): if args.arm_float_abi: gn_args['arm_float_abi'] = args.arm_float_abi + # Whether to build trained Dart SDK snapshots of dart2js and dartdevc, + # including the web sdk kernel and source files. + if args.target_os is None: + # dart_platform_sdk is not declared for Android targets. + gn_args['dart_platform_sdk'] = not args.full_dart_sdk + gn_args['full_dart_sdk'] = args.full_dart_sdk + + if sys.platform == 'darwin': + gn_args['mac_sdk_path'] = args.mac_sdk_path + return gn_args def parse_args(args): @@ -229,7 +255,10 @@ def parse_args(args): parser.add_argument('--runtime-mode', type=str, choices=['debug', 'profile', 'release'], default='debug') parser.add_argument('--dynamic', default=False, action='store_true') parser.add_argument('--interpreter', default=False, action='store_true') - parser.add_argument('--dart-debug', default=False, action='store_true') + parser.add_argument('--dart-debug', default=False, action='store_true', help='Enables assertsion in the Dart VM. ' + + 'Does not affect affect optimization levels. If you need to disable optimizations in Dart, use --full-dart-debug') + parser.add_argument('--full-dart-debug', default=False, action='store_true', help='Implies --dart-debug ' + + 'and also disables optimizations in the Dart VM making it easier to step through VM code in the debugger.') parser.add_argument('--target-os', type=str, choices=['android', 'ios', 'linux']) parser.add_argument('--android', dest='target_os', action='store_const', const='android') @@ -249,15 +278,30 @@ def parse_args(args): parser.add_argument('--clang', default=True, action='store_true') parser.add_argument('--no-clang', dest='clang', action='store_false') + parser.add_argument('--clang-static-analyzer', default=False, action='store_true') + parser.add_argument('--no-clang-static-analyzer', dest='clang_static_analyzer', action='store_false') + parser.add_argument('--target-sysroot', type=str) parser.add_argument('--target-toolchain', type=str) parser.add_argument('--target-triple', type=str) parser.add_argument('--toolchain-prefix', type=str) + parser.add_argument('--operator-new-alignment', dest='operator_new_alignment', type=str, default=None) parser.add_argument('--enable-vulkan', action='store_true', default=False) parser.add_argument('--embedder-for-target', dest='embedder_for_target', action='store_true', default=False) + parser.add_argument('--coverage', default=False, action='store_true') + + parser.add_argument('--out-dir', default='', type=str) + + parser.add_argument('--full-dart-sdk', default=False, action='store_true', + help='include trained dart2js and dartdevc snapshots. Enable only on steps that create an SDK') + parser.add_argument('--no-full-dart-sdk', dest='full_dart_sdk', action='store_false') + + parser.add_argument('--mac-sdk-path', default='', type=str, + help='On the mac, the SDK is inferred from the Xcode location unless this flag is specified.') + return parser.parse_args(args) def main(argv): diff --git a/tools/gn_test.py b/tools/gn_test.py index 7bbc37c014424..f0d671984ddba 100644 --- a/tools/gn_test.py +++ b/tools/gn_test.py @@ -1,4 +1,4 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. +# Copyright 2013 The Flutter 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 unittest diff --git a/tools/licenses/README.md b/tools/licenses/README.md index 805c32f8b4bd0..ccd44c0a82f75 100644 --- a/tools/licenses/README.md +++ b/tools/licenses/README.md @@ -3,15 +3,24 @@ ## Apply patch generated by CI If you're not working on a Linux box then you can't auto-generate license files. A workaround is provided via CI. -Your build will fail one or more CI checks if your license files are not correct. Open the failing CI check. In the CI output you will find a patch diff that represents the changes that need to be made to your license files. Copy this patch to a temporary patch file wherever you'd like. Then apply the patch to your repo: +Your build will fail one or more CI checks if your license files are not correct. Open the failing CI check. In the CI output you will find a patch diff that represents the changes that need to be made to your license files. Click `Download full logs` (Github removes lines with only whitespaces, which invalidates the diff). Copy this patch to a temporary patch file wherever you'd like. Then apply the patch to your repo: ``` cd flutter/ci/licenses_golden patch -p2 < my/patch/file ``` -## Regenerate License Files (Only works on Linux) -To update the golden license files, make sure you've rebased your branch to the latest upstream master and then run the following in this directory: +## Check for license changes (Only works on Linux) + +This script has two sets of output files: "goldens", which describe +the current license state of the repository, and the actual real +LICENSE file, which is what matters. + +We look at changes to the goldens to determine if there are any actual +changes to the licenses. + +To update the goldens, make sure you've rebased your branch to the +latest upstream master and then run the following in this directory: ``` pub get @@ -20,9 +29,29 @@ rm -rf ../../../out/licenses dart lib/main.dart --src ../../.. --out ../../../out/licenses --golden ../../ci/licenses_golden ``` -Then copy any affected files from `../../../out/licenses` to `../../ci/licenses_golden` and add them to your change. +In order for the license script to work correctly, you need to remove +any untracked files inside `engine/src` (the buildroot), not just +`engine/src/flutter`. + +Once the script has finished, copy any affected files from +`../../../out/licenses` to `../../ci/licenses_golden` and add them to +your change, and examine the diffs to see what changed. + +``` +cp ../../../out/licenses/* ../../ci/licenses_golden +git diff +``` + +If the only changes are to what files are included, then you're good +to go. However, if any of the _licenses_ changed, whether new licenses +are added, old ones removed, or any have their content changed in +_any_ way (including, e.g., whitespace changes), or if the affected +libraries change, **you must update the actual license file**. -The `sky/packages/sky_engine/LICENSE` file is included in product releases and should be updated any time the golden file changes in a way that involves changes to anything other than the FILE lines. To update this file, run: +The `sky/packages/sky_engine/LICENSE` file is the one actually +included in product releases and the one that should be updated any +time the golden file changes in a way that involves changes to +anything other than the `FILE` lines. To update this file, run: ``` pub get @@ -30,4 +59,7 @@ gclient sync dart lib/main.dart --release --src ../../.. --out ../../../out/licenses > ../../sky/packages/sky_engine/LICENSE ``` -**In order for the license script to work, please remove all untracked files inside `engine/src` (not just `engine/src/flutter`)** +The bots do not verify that you did this step, it's important that you +do it! When reviewing patches, if you see a change to the golden +files, check to see if there's a corresponding change to the LICENSE +file! diff --git a/tools/licenses/analysis_options.yaml b/tools/licenses/analysis_options.yaml index 44a679430a9a3..d33675fd8a10a 100644 --- a/tools/licenses/analysis_options.yaml +++ b/tools/licenses/analysis_options.yaml @@ -1,58 +1,162 @@ # Specify analysis options. +# +# Until there are meta linter rules, each desired lint must be explicitly enabled. +# See: https://github.com/dart-lang/linter/issues/288 +# +# For a list of lints, see: http://dart-lang.github.io/linter/lints/ +# See the configuration guide for more +# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer +# +# There are other similar analysis options files in the flutter repos, +# which should be kept in sync with this file: +# +# - analysis_options.yaml +# - tools/licenses/analysis_options.yaml (this file) +# - https://github.com/flutter/flutter/blob/master/analysis_options_user.yaml +# - https://github.com/flutter/flutter/blob/master/packages/flutter/lib/analysis_options_user.yaml +# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml +# +# This file contains the analysis options used by Flutter tools, such as IntelliJ, +# Android Studio, and the `flutter analyze` command. analyzer: - language: - enableSuperMixins: true strong-mode: implicit-dynamic: false errors: - strong_mode_invalid_field_override: ignore - strong_mode_down_cast_composite: ignore + # allow having TODOs in the code todo: ignore linter: rules: - - avoid_empty_else - - comment_references - - cancel_subscriptions - - close_sinks - - control_flow_in_finally - - empty_statements - - hash_and_equals - # - invariant_booleans # Produces false positives, see https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type - - literal_only_boolean_expressions - - test_types_in_equals - - throw_in_finally - - unrelated_type_equality_checks - - valid_regexps + # these rules are documented on and in the same order as + # the Dart Lint rules page to make maintenance easier + # https://github.com/dart-lang/linter/blob/master/example/all.yaml - always_declare_return_types + - always_put_control_body_on_new_line + - always_put_required_named_parameters_first + - always_require_non_null_named_parameters - always_specify_types - annotate_overrides + # - avoid_annotating_with_dynamic # conflicts with always_specify_types - avoid_as + - 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 + # - avoid_double_and_int_checks # only useful when targeting JS runtime + - avoid_empty_else + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + - avoid_implementing_value_types - avoid_init_to_null + # - avoid_js_rounded_ints # only useful when targeting JS runtime + - avoid_null_checks_in_equality_operators + - avoid_positional_boolean_parameters + # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) + - avoid_relative_lib_imports + - avoid_renaming_method_parameters - avoid_return_types_on_setters + # - avoid_returning_null # there are plenty of valid reasons to return null + - avoid_returning_null_for_void + # - avoid_returning_this # there are plenty of valid reasons to return this + - avoid_setters_without_getters + - avoid_single_cascade_in_expression_statements + - avoid_slow_async_io + - avoid_types_as_parameter_names + # - avoid_types_on_closure_parameters # conflicts with always_specify_types + - avoid_unused_constructor_parameters + - avoid_void_async - await_only_futures - camel_case_types - - constant_identifier_names + - cancel_subscriptions + # - cascade_invocations # not yet tested + # - close_sinks # not reliable enough + # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 + # - 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 yet tested + - directives_ordering + - empty_catches - empty_constructor_bodies + - empty_statements + - file_names + - flutter_style_todos + - hash_and_equals - implementation_imports + # - 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 - library_names - library_prefixes + # - lines_longer_than_80_chars # not yet tested + - list_remove_unrelated_type + # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 + - no_adjacent_strings_in_list + - no_duplicate_case_values - non_constant_identifier_names - - one_member_abstracts + - 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 - overridden_fields + - package_api_docs + - package_names - package_prefixed_library_names + # - parameter_assignments # we do this commonly + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - 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_contains + - 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 + - prefer_final_locals + - prefer_foreach + - prefer_function_declarations_over_variables # not yet tested + - prefer_generic_function_type_aliases + - prefer_initializing_formals + - prefer_int_literals + # - prefer_interpolation_to_compose_strings # not yet tested + - prefer_is_empty - prefer_is_not_empty + - prefer_iterable_whereType + # - prefer_mixin # https://github.com/dart-lang/language/issues/32 + - prefer_single_quotes + - prefer_typing_uninitialized_variables + - prefer_void_to_null + # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml + - recursive_getters - slash_for_doc_comments - sort_constructors_first + - sort_pub_dependencies - sort_unnamed_constructors_first - super_goes_last - - type_annotate_public_apis + - test_types_in_equals + - throw_in_finally + # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - - unawaited_futures + # - unawaited_futures # too many false positives - unnecessary_brace_in_string_interps + - unnecessary_const - unnecessary_getters_setters - - package_names + # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_in_if_null_operators + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_this + - unrelated_type_equality_checks + - use_rethrow_when_possible + - use_setters_to_change_properties + # - 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/tools/licenses/data/mozilla-1.1 b/tools/licenses/data/mozilla-1.1 new file mode 100644 index 0000000000000..566908108012c --- /dev/null +++ b/tools/licenses/data/mozilla-1.1 @@ -0,0 +1,469 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the MPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (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.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] diff --git a/tools/licenses/lib/cache.dart b/tools/licenses/lib/cache.dart index 55d3928ab467c..62dcbeecf4d51 100644 --- a/tools/licenses/lib/cache.dart +++ b/tools/licenses/lib/cache.dart @@ -1,10 +1,8 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter 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:collection'; - -Map _cache = new LinkedHashMap(); +Map _cache = {}; const int _maxSize = 10; T cache(Key key, T getter()) { diff --git a/tools/licenses/lib/filesystem.dart b/tools/licenses/lib/filesystem.dart index 4bbcc781f5e4b..808c95f1f3876 100644 --- a/tools/licenses/lib/filesystem.dart +++ b/tools/licenses/lib/filesystem.dart @@ -1,9 +1,9 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:convert'; import 'dart:collection'; +import 'dart:convert'; import 'dart:io' as io; import 'package:path/path.dart' as path; @@ -58,8 +58,8 @@ bool hasSubsequence(List bytes, List signature, int limit) { const String kMultiLicenseFileHeader = 'Notices for files contained in'; bool isMultiLicenseNotice(Reader reader) { - List bytes = reader(); - return (ascii.decode(bytes.take(kMultiLicenseFileHeader.length).toList(), allowInvalid: true) == kMultiLicenseFileHeader); + final List bytes = reader(); + return ascii.decode(bytes.take(kMultiLicenseFileHeader.length).toList(), allowInvalid: true) == kMultiLicenseFileHeader; } FileType identifyFile(String name, Reader reader) { @@ -180,12 +180,12 @@ FileType identifyFile(String name, Reader reader) { // Documentation case '.md': return FileType.text; case '.txt': return FileType.text; - case '.diff': return FileType.text; case '.html': return FileType.text; // Fonts case '.ttf': return FileType.binary; // TrueType Font case '.ttcf': // (mac) case '.ttc': return FileType.binary; // TrueType Collection (windows) + case '.woff': return FileType.binary; // Web Open Font Format case '.otf': return FileType.binary; // OpenType Font // Graphics formats case '.gif': return FileType.binary; // GIF @@ -195,11 +195,13 @@ FileType identifyFile(String name, Reader reader) { case '.jpg': case '.jpeg': return FileType.binary; // JPEG case '.ico': return FileType.binary; // Windows icon format + case '.icns': return FileType.binary; // macOS icon format case '.bmp': return FileType.binary; // Windows bitmap format case '.wbmp': return FileType.binary; // Wireless bitmap format case '.webp': return FileType.binary; // WEBP case '.pdf': return FileType.binary; // PDF case '.emf': return FileType.binary; // Windows enhanced metafile format + case '.skp': return FileType.binary; // Skia picture format // Videos case '.ogg': return FileType.binary; // Ogg media case '.mp4': return FileType.binary; // MPEG media @@ -230,7 +232,7 @@ FileType identifyFile(String name, Reader reader) { return FileType.binary; } bytes ??= reader(); - assert(bytes.length > 0); + assert(bytes.isNotEmpty); if (matchesSignature(bytes, [0x1F, 0x8B])) return FileType.gz; // GZip archive if (matchesSignature(bytes, [0x42, 0x5A])) @@ -275,8 +277,10 @@ FileType identifyFile(String name, Reader reader) { return FileType.binary; // GIF89a if (matchesSignature(bytes, [0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00])) return FileType.binary; // Dalvik Executable - if (matchesSignature(bytes, [0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A])) - return FileType.binary; // Unix archiver (ar) // TODO(ianh): implement .ar parser + if (matchesSignature(bytes, [0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A])) { + // TODO(ianh): implement .ar parser, https://github.com/flutter/flutter/issues/25633 + return FileType.binary; // Unix archiver (ar) + } if (matchesSignature(bytes, [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0a])) return FileType.binary; // PNG if (matchesSignature(bytes, [0x58, 0x50, 0x43, 0x4f, 0x4d, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x62, 0x0d, 0x0a, 0x1a])) @@ -308,12 +312,11 @@ abstract class TextFile extends File { String readString(); } -// mixin -abstract class UTF8TextFile extends TextFile { +mixin UTF8TextFile implements TextFile { @override String readString() { try { - return cache(new UTF8Of(this), () => utf8.decode(readBytes())); + return cache(UTF8Of(this), () => utf8.decode(readBytes())); } on FormatException { print(fullName); rethrow; @@ -321,19 +324,20 @@ abstract class UTF8TextFile extends TextFile { } } -// mixin -abstract class Latin1TextFile extends TextFile { +mixin Latin1TextFile implements TextFile { @override String readString() { - return cache(new Latin1Of(this), () { + return cache(Latin1Of(this), () { final List bytes = readBytes(); if (bytes.any((int byte) => byte == 0x00)) throw '$fullName contains a U+0000 NULL and is probably not actually encoded as Win1252'; bool isUTF8 = false; try { - cache(new UTF8Of(this), () => utf8.decode(readBytes())); + cache(UTF8Of(this), () => utf8.decode(readBytes())); isUTF8 = true; } on FormatException { + // Exceptions are fine/expected for non-UTF8 text, which we test for + // immediately below. } if (isUTF8) throw '$fullName contains valid UTF-8 and is probably not actually encoded as Win1252'; @@ -350,14 +354,13 @@ abstract class Directory extends IoNode { // interface abstract class Link extends IoNode { } -// mixin -abstract class ZipFile extends File implements Directory { +mixin ZipFile on File implements Directory { ArchiveDirectory _root; @override Iterable get walk { try { - _root ??= ArchiveDirectory.parseArchive(new a.ZipDecoder().decodeBytes(readBytes()), fullName); + _root ??= ArchiveDirectory.parseArchive(a.ZipDecoder().decodeBytes(readBytes()), fullName); return _root.walk; } catch (exception) { print('failed to parse archive:\n$fullName'); @@ -366,14 +369,13 @@ abstract class ZipFile extends File implements Directory { } } -// mixin -abstract class TarFile extends File implements Directory { +mixin TarFile on File implements Directory { ArchiveDirectory _root; @override Iterable get walk { try { - _root ??= ArchiveDirectory.parseArchive(new a.TarDecoder().decodeBytes(readBytes()), fullName); + _root ??= ArchiveDirectory.parseArchive(a.TarDecoder().decodeBytes(readBytes()), fullName); return _root.walk; } catch (exception) { print('failed to parse archive:\n$fullName'); @@ -382,15 +384,14 @@ abstract class TarFile extends File implements Directory { } } -// mixin -abstract class GZipFile extends File implements Directory { +mixin GZipFile on File implements Directory { InMemoryFile _data; @override Iterable get walk sync* { try { - String innerName = path.basenameWithoutExtension(fullName); - _data ??= InMemoryFile.parse(fullName + '!' + innerName, new a.GZipDecoder().decodeBytes(readBytes())); + final String innerName = path.basenameWithoutExtension(fullName); + _data ??= InMemoryFile.parse(fullName + '!' + innerName, a.GZipDecoder().decodeBytes(readBytes())); if (_data != null) yield _data; } catch (exception) { @@ -400,15 +401,14 @@ abstract class GZipFile extends File implements Directory { } } -// mixin -abstract class BZip2File extends File implements Directory { +mixin BZip2File on File implements Directory { InMemoryFile _data; @override Iterable get walk sync* { try { - String innerName = path.basenameWithoutExtension(fullName); - _data ??= InMemoryFile.parse(fullName + '!' + innerName, new a.BZip2Decoder().decodeBytes(readBytes())); + final String innerName = path.basenameWithoutExtension(fullName); + _data ??= InMemoryFile.parse(fullName + '!' + innerName, a.BZip2Decoder().decodeBytes(readBytes())); if (_data != null) yield _data; } catch (exception) { @@ -425,7 +425,7 @@ class FileSystemDirectory extends IoNode implements Directory { FileSystemDirectory(this._directory); factory FileSystemDirectory.fromPath(String name) { - return new FileSystemDirectory(new io.Directory(name)); + return FileSystemDirectory(io.Directory(name)); } final io.Directory _directory; @@ -437,30 +437,30 @@ class FileSystemDirectory extends IoNode implements Directory { String get fullName => _directory.path; List _readBytes(io.File file) { - return cache/*List*/(new BytesOf(file), () => file.readAsBytesSync()); + return cache/*List*/(BytesOf(file), () => file.readAsBytesSync()); } @override Iterable get walk sync* { - List list = _directory.listSync().toList(); + final List list = _directory.listSync().toList(); list.sort((io.FileSystemEntity a, io.FileSystemEntity b) => a.path.compareTo(b.path)); for (io.FileSystemEntity entity in list) { if (entity is io.Directory) { - yield new FileSystemDirectory(entity); + yield FileSystemDirectory(entity); } else if (entity is io.Link) { - yield new FileSystemLink(entity); + yield FileSystemLink(entity); } else { assert(entity is io.File); - io.File fileEntity = entity; + final io.File fileEntity = entity; if (fileEntity.lengthSync() > 0) { switch (identifyFile(fileEntity.path, () => _readBytes(fileEntity))) { - case FileType.binary: yield new FileSystemFile(fileEntity); break; - case FileType.zip: yield new FileSystemZipFile(fileEntity); break; - case FileType.tar: yield new FileSystemTarFile(fileEntity); break; - case FileType.gz: yield new FileSystemGZipFile(fileEntity); break; - case FileType.bzip2: yield new FileSystemBZip2File(fileEntity); break; - case FileType.text: yield new FileSystemUTF8TextFile(fileEntity); break; - case FileType.latin1Text: yield new FileSystemLatin1TextFile(fileEntity); break; + case FileType.binary: yield FileSystemFile(fileEntity); break; + case FileType.zip: yield FileSystemZipFile(fileEntity); break; + case FileType.tar: yield FileSystemTarFile(fileEntity); break; + case FileType.gz: yield FileSystemGZipFile(fileEntity); break; + case FileType.bzip2: yield FileSystemBZip2File(fileEntity); break; + case FileType.text: yield FileSystemUTF8TextFile(fileEntity); break; + case FileType.latin1Text: yield FileSystemLatin1TextFile(fileEntity); break; case FileType.metadata: break; // ignore this file } } @@ -494,7 +494,7 @@ class FileSystemFile extends IoNode implements File { @override List readBytes() { - return cache(new BytesOf(_file), () => _file.readAsBytesSync()); + return cache(BytesOf(_file), () => _file.readAsBytesSync()); } } @@ -534,27 +534,27 @@ class ArchiveDirectory extends IoNode implements Directory { @override final String name; - Map _subdirectories = new SplayTreeMap(); - List _files = []; + final Map _subdirectories = SplayTreeMap(); + final List _files = []; void _add(a.ArchiveFile entry, List remainingPath) { if (remainingPath.length > 1) { final String subdirectoryName = remainingPath.removeAt(0); _subdirectories.putIfAbsent( subdirectoryName, - () => new ArchiveDirectory('$fullName/$subdirectoryName', subdirectoryName) + () => ArchiveDirectory('$fullName/$subdirectoryName', subdirectoryName) )._add(entry, remainingPath); } else { if (entry.size > 0) { final String entryFullName = fullName + '/' + path.basename(entry.name); switch (identifyFile(entry.name, () => entry.content)) { - case FileType.binary: _files.add(new ArchiveFile(entryFullName, entry)); break; - case FileType.zip: _files.add(new ArchiveZipFile(entryFullName, entry)); break; - case FileType.tar: _files.add(new ArchiveTarFile(entryFullName, entry)); break; - case FileType.gz: _files.add(new ArchiveGZipFile(entryFullName, entry)); break; - case FileType.bzip2: _files.add(new ArchiveBZip2File(entryFullName, entry)); break; - case FileType.text: _files.add(new ArchiveUTF8TextFile(entryFullName, entry)); break; - case FileType.latin1Text: _files.add(new ArchiveLatin1TextFile(entryFullName, entry)); break; + case FileType.binary: _files.add(ArchiveFile(entryFullName, entry)); break; + case FileType.zip: _files.add(ArchiveZipFile(entryFullName, entry)); break; + case FileType.tar: _files.add(ArchiveTarFile(entryFullName, entry)); break; + case FileType.gz: _files.add(ArchiveGZipFile(entryFullName, entry)); break; + case FileType.bzip2: _files.add(ArchiveBZip2File(entryFullName, entry)); break; + case FileType.text: _files.add(ArchiveUTF8TextFile(entryFullName, entry)); break; + case FileType.latin1Text: _files.add(ArchiveLatin1TextFile(entryFullName, entry)); break; case FileType.metadata: break; // ignore this file } } @@ -562,7 +562,7 @@ class ArchiveDirectory extends IoNode implements Directory { } static ArchiveDirectory parseArchive(a.Archive archive, String ownerPath) { - final ArchiveDirectory root = new ArchiveDirectory('$ownerPath!', ''); + final ArchiveDirectory root = ArchiveDirectory('$ownerPath!', ''); for (a.ArchiveFile file in archive.files) { if (file.size > 0) root._add(file, file.name.split('/')); @@ -628,13 +628,13 @@ class InMemoryFile extends IoNode implements File { if (bytes.isEmpty) return null; switch (identifyFile(fullName, () => bytes)) { - case FileType.binary: return new InMemoryFile(fullName, bytes); break; - case FileType.zip: return new InMemoryZipFile(fullName, bytes); break; - case FileType.tar: return new InMemoryTarFile(fullName, bytes); break; - case FileType.gz: return new InMemoryGZipFile(fullName, bytes); break; - case FileType.bzip2: return new InMemoryBZip2File(fullName, bytes); break; - case FileType.text: return new InMemoryUTF8TextFile(fullName, bytes); break; - case FileType.latin1Text: return new InMemoryLatin1TextFile(fullName, bytes); break; + case FileType.binary: return InMemoryFile(fullName, bytes); break; + case FileType.zip: return InMemoryZipFile(fullName, bytes); break; + case FileType.tar: return InMemoryTarFile(fullName, bytes); break; + case FileType.gz: return InMemoryGZipFile(fullName, bytes); break; + case FileType.bzip2: return InMemoryBZip2File(fullName, bytes); break; + case FileType.text: return InMemoryUTF8TextFile(fullName, bytes); break; + case FileType.latin1Text: return InMemoryLatin1TextFile(fullName, bytes); break; case FileType.metadata: break; // ignore this file } assert(false); diff --git a/tools/licenses/lib/licenses.dart b/tools/licenses/lib/licenses.dart index dc457411d6db4..718f3123c04ca 100644 --- a/tools/licenses/lib/licenses.dart +++ b/tools/licenses/lib/licenses.dart @@ -1,13 +1,15 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:convert'; import 'dart:io' as system; +import 'package:meta/meta.dart'; + import 'cache.dart'; -import 'patterns.dart'; import 'limits.dart'; +import 'patterns.dart'; class FetchedContentsOf extends Key { FetchedContentsOf(dynamic value) : super(value); } @@ -106,10 +108,14 @@ abstract class LicenseSource { } abstract class License implements Comparable { - factory License.unique(String body, LicenseType type, { bool reformatted: false, String origin, bool yesWeKnowWhatItLooksLikeButItIsNot: false }) { + factory License.unique(String body, LicenseType type, { + bool reformatted = false, + String origin, + bool yesWeKnowWhatItLooksLikeButItIsNot = false + }) { if (!reformatted) body = _reformat(body); - License result = _registry.putIfAbsent(body, () => new UniqueLicense._(body, type, origin: origin, yesWeKnowWhatItLooksLikeButItIsNot: yesWeKnowWhatItLooksLikeButItIsNot)); + final License result = _registry.putIfAbsent(body, () => UniqueLicense._(body, type, origin: origin, yesWeKnowWhatItLooksLikeButItIsNot: yesWeKnowWhatItLooksLikeButItIsNot)); assert(() { if (result is! UniqueLicense || result.type != type) throw 'tried to add a UniqueLicense $type, but it was a duplicate of a ${result.runtimeType} ${result.type}'; @@ -118,10 +124,13 @@ abstract class License implements Comparable { return result; } - factory License.template(String body, LicenseType type, { bool reformatted: false, String origin }) { + factory License.template(String body, LicenseType type, { + bool reformatted = false, + String origin + }) { if (!reformatted) body = _reformat(body); - License result = _registry.putIfAbsent(body, () => new TemplateLicense._(body, type, origin: origin)); + final License result = _registry.putIfAbsent(body, () => TemplateLicense._(body, type, origin: origin)); assert(() { if (result is! TemplateLicense || result.type != type) throw 'tried to add a TemplateLicense $type, but it was a duplicate of a ${result.runtimeType} ${result.type}'; @@ -130,10 +139,13 @@ abstract class License implements Comparable { return result; } - factory License.message(String body, LicenseType type, { bool reformatted: false, String origin }) { + factory License.message(String body, LicenseType type, { + bool reformatted = false, + String origin + }) { if (!reformatted) body = _reformat(body); - License result = _registry.putIfAbsent(body, () => new MessageLicense._(body, type, origin: origin)); + final License result = _registry.putIfAbsent(body, () => MessageLicense._(body, type, origin: origin)); assert(() { if (result is! MessageLicense || result.type != type) throw 'tried to add a MessageLicense $type, but it was a duplicate of a ${result.runtimeType} ${result.type}'; @@ -143,7 +155,7 @@ abstract class License implements Comparable { } factory License.blank(String body, LicenseType type, { String origin }) { - License result = _registry.putIfAbsent(body, () => new BlankLicense._(_reformat(body), type, origin: origin)); + final License result = _registry.putIfAbsent(body, () => BlankLicense._(_reformat(body), type, origin: origin)); assert(() { if (result is! BlankLicense || result.type != type) throw 'tried to add a BlankLicense $type, but it was a duplicate of a ${result.runtimeType} ${result.type}'; @@ -154,22 +166,25 @@ abstract class License implements Comparable { factory License.fromMultipleBlocks(List bodies, LicenseType type) { final String body = bodies.map((String s) => _reformat(s)).join('\n\n'); - return _registry.putIfAbsent(body, () => new UniqueLicense._(body, type)); + return _registry.putIfAbsent(body, () => UniqueLicense._(body, type)); } - factory License.fromBodyAndType(String body, LicenseType type, { bool reformatted: false, String origin }) { + factory License.fromBodyAndType(String body, LicenseType type, { + bool reformatted = false, + String origin + }) { if (!reformatted) body = _reformat(body); - License result = _registry.putIfAbsent(body, () { + final License result = _registry.putIfAbsent(body, () { switch (type) { case LicenseType.bsd: case LicenseType.mit: case LicenseType.zlib: case LicenseType.icu: - return new TemplateLicense._(body, type, origin: origin); + return TemplateLicense._(body, type, origin: origin); case LicenseType.unknown: case LicenseType.apacheNotice: - return new UniqueLicense._(body, type, origin: origin); + return UniqueLicense._(body, type, origin: origin); case LicenseType.afl: case LicenseType.mpl: case LicenseType.gpl: @@ -179,11 +194,11 @@ abstract class License implements Comparable { case LicenseType.eclipse: case LicenseType.ijg: case LicenseType.apsl: - return new MessageLicense._(body, type, origin: origin); + return MessageLicense._(body, type, origin: origin); case LicenseType.openssl: - return new MultiLicense._(body, type, origin: origin); + return MultiLicense._(body, type, origin: origin); case LicenseType.libpng: - return new BlankLicense._(body, type, origin: origin); + return BlankLicense._(body, type, origin: origin); } }); assert(result.type == type); @@ -195,18 +210,18 @@ abstract class License implements Comparable { LicenseType type = convertLicenseNameToType(name); if (type == LicenseType.unknown) type = convertBodyToType(body); - return new License.fromBodyAndType(body, type, origin: origin); + return License.fromBodyAndType(body, type, origin: origin); } factory License.fromBody(String body, { String origin }) { body = _reformat(body); - LicenseType type = convertBodyToType(body); - return new License.fromBodyAndType(body, type, reformatted: true, origin: origin); + final LicenseType type = convertBodyToType(body); + return License.fromBodyAndType(body, type, reformatted: true, origin: origin); } factory License.fromCopyrightAndLicense(String copyright, String template, LicenseType type, { String origin }) { - String body = '$copyright\n\n$template'; - return _registry.putIfAbsent(body, () => new TemplateLicense._(body, type, origin: origin)); + final String body = '$copyright\n\n$template'; + return _registry.putIfAbsent(body, () => TemplateLicense._(body, type, origin: origin)); } factory License.fromUrl(String url, { String origin }) { @@ -215,64 +230,72 @@ abstract class License implements Comparable { switch (url) { case 'Apache:2.0': case 'http://www.apache.org/licenses/LICENSE-2.0': - body = new system.File('data/apache-license-2.0').readAsStringSync(); + case 'https://www.apache.org/licenses/LICENSE-2.0': + body = system.File('data/apache-license-2.0').readAsStringSync(); type = LicenseType.apache; break; case 'https://developers.google.com/open-source/licenses/bsd': - body = new system.File('data/google-bsd').readAsStringSync(); + body = system.File('data/google-bsd').readAsStringSync(); type = LicenseType.bsd; break; case 'http://polymer.github.io/LICENSE.txt': - body = new system.File('data/polymer-bsd').readAsStringSync(); + body = system.File('data/polymer-bsd').readAsStringSync(); type = LicenseType.bsd; break; case 'http://www.eclipse.org/legal/epl-v10.html': - body = new system.File('data/eclipse-1.0').readAsStringSync(); + body = system.File('data/eclipse-1.0').readAsStringSync(); type = LicenseType.eclipse; break; case 'COPYING3:3': - body = new system.File('data/gpl-3.0').readAsStringSync(); + body = system.File('data/gpl-3.0').readAsStringSync(); type = LicenseType.gpl; break; case 'COPYING.LIB:2': case 'COPYING.LIother.m_:2': // blame hyatt - body = new system.File('data/library-gpl-2.0').readAsStringSync(); + body = system.File('data/library-gpl-2.0').readAsStringSync(); type = LicenseType.lgpl; break; case 'GNU Lesser:2': // there has never been such a license, but the authors said they meant the LGPL2.1 case 'GNU Lesser:2.1': - body = new system.File('data/lesser-gpl-2.1').readAsStringSync(); + body = system.File('data/lesser-gpl-2.1').readAsStringSync(); type = LicenseType.lgpl; break; case 'COPYING.RUNTIME:3.1': case 'GCC Runtime Library Exception:3.1': - body = new system.File('data/gpl-gcc-exception-3.1').readAsStringSync(); + body = system.File('data/gpl-gcc-exception-3.1').readAsStringSync(); break; case 'Academic Free License:3.0': - body = new system.File('data/academic-3.0').readAsStringSync(); + body = system.File('data/academic-3.0').readAsStringSync(); type = LicenseType.afl; break; + case 'Mozilla Public License:1.1': + body = system.File('data/mozilla-1.1').readAsStringSync(); + type = LicenseType.mpl; + break; case 'http://mozilla.org/MPL/2.0/:2.0': - body = new system.File('data/mozilla-2.0').readAsStringSync(); + body = system.File('data/mozilla-2.0').readAsStringSync(); type = LicenseType.mpl; break; case 'http://opensource.org/licenses/MIT': case 'http://opensource->org/licenses/MIT': // i don't even - body = new system.File('data/mit').readAsStringSync(); + body = system.File('data/mit').readAsStringSync(); type = LicenseType.mit; break; case 'http://www.unicode.org/copyright.html': - body = new system.File('data/unicode').readAsStringSync(); + body = system.File('data/unicode').readAsStringSync(); type = LicenseType.icu; break; default: throw 'unknown url $url'; } - return _registry.putIfAbsent(body, () => new License.fromBodyAndType(body, type, origin: origin)); + return _registry.putIfAbsent(body, () => License.fromBodyAndType(body, type, origin: origin)); } - License._(String body, this.type, { this.origin, bool yesWeKnowWhatItLooksLikeButItIsNot: false }) : body = body, authors = _readAuthors(body) { - assert(_reformat(body) == body); + License._(this.body, this.type, { + this.origin, + bool yesWeKnowWhatItLooksLikeButItIsNot = false + }) : authors = _readAuthors(body), + assert(_reformat(body) == body) { assert(() { try { switch (type) { @@ -327,19 +350,19 @@ abstract class License implements Comparable { try { latin1Encoded = latin1.encode(body); isUTF8 = false; - } on ArgumentError { } + } on ArgumentError { /* Fall through to next encoding check. */ } if (!isUTF8) { bool isAscii = false; try { ascii.decode(latin1Encoded); isAscii = true; - } on FormatException { } + } on FormatException { /* Fall through to next encoding check */ } if (isAscii) return; try { utf8.decode(latin1Encoded); isUTF8 = true; - } on FormatException { } + } on FormatException { /* We check isUTF8 below and throw if necessary */ } if (isUTF8) throw 'tried to create a License object with text that appears to have been misdecoded as Latin1 instead of as UTF-8:\n$body'; } @@ -351,15 +374,16 @@ abstract class License implements Comparable { final LicenseType type; Iterable get licensees => _licensees; - List _licensees = []; - Set _libraries = new Set(); + final List _licensees = []; + final Set _libraries = Set(); bool get isUsed => _licensees.isNotEmpty; void markUsed(String filename, String libraryName) { + assert(filename != null); + assert(filename != ''); assert(libraryName != null); assert(libraryName != ''); - filename != null; _licensees.add(filename); _libraries.add(libraryName); } @@ -395,7 +419,7 @@ abstract class License implements Comparable { return prefixes.join('\n') + '\n\n' + body; } - static final RegExp _copyrightForAuthors = new RegExp( + static final RegExp _copyrightForAuthors = RegExp( r'Copyright [-0-9 ,(cC)©]+\b(The .+ Authors)\.', caseSensitive: false ); @@ -417,7 +441,7 @@ void clearLicenseRegistry() { _registry.clear(); } -final License missingLicense = new UniqueLicense._('', LicenseType.unknown); +final License missingLicense = UniqueLicense._('', LicenseType.unknown); String _reformat(String body) { // TODO(ianh): ensure that we're stripping the same amount of leading text on each line @@ -491,42 +515,41 @@ Iterable<_LineRange> _walkLinesBackwards(String body, int start) sync* { start -= 1; if (body[start] == '\n') { if (end != null) - yield new _LineRange(start + 1, end, body); + yield _LineRange(start + 1, end, body); end = start; } } if (end != null) - yield new _LineRange(start, end, body); + yield _LineRange(start, end, body); } -Iterable<_LineRange> _walkLinesForwards(String body, { int start: 0, int end }) sync* { +Iterable<_LineRange> _walkLinesForwards(String body, { int start = 0, int end }) sync* { int startIndex = start == 0 || body[start-1] == '\n' ? start : null; int endIndex = startIndex ?? start; end ??= body.length; while (endIndex < end) { if (body[endIndex] == '\n') { if (startIndex != null) - yield new _LineRange(startIndex, endIndex, body); + yield _LineRange(startIndex, endIndex, body); startIndex = endIndex + 1; } endIndex += 1; } if (startIndex != null) - yield new _LineRange(startIndex, endIndex, body); + yield _LineRange(startIndex, endIndex, body); } class _SplitLicense { - _SplitLicense(this._body, this._split) { - assert(this._split == 0 || this._split == this._body.length || this._body[this._split] == '\n'); - } + _SplitLicense(this._body, this._split) : assert(_split == 0 || _split == _body.length || _body[_split] == '\n'); + final String _body; final int _split; String getCopyright() => _body.substring(0, _split); String getConditions() => _split >= _body.length ? '' : _body.substring(_split == 0 ? 0 : _split + 1); } -_SplitLicense _splitLicense(String body, { bool verifyResults: true }) { - Iterator<_LineRange> lines = _walkLinesForwards(body).iterator; +_SplitLicense _splitLicense(String body, { bool verifyResults = true }) { + final Iterator<_LineRange> lines = _walkLinesForwards(body).iterator; if (!lines.moveNext()) throw 'tried to split empty license'; int end = 0; @@ -586,14 +609,13 @@ _SplitLicense _splitLicense(String body, { bool verifyResults: true }) { } if (verifyResults && 'Copyright ('.allMatches(body, end).isNotEmpty && !body.startsWith('If you modify libpng')) throw 'the license seems to contain a copyright:\n===copyright===\n${body.substring(0, end)}\n===license===\n${body.substring(end)}\n========='; - return new _SplitLicense(body, end); + return _SplitLicense(body, end); } class _PartialLicenseMatch { - _PartialLicenseMatch(this._body, this.start, this.split, this.end, this._match, { this.hasCopyrights }) { - assert(split >= start); - assert(split == start || _body[split] == '\n'); - } + _PartialLicenseMatch(this._body, this.start, this.split, this.end, this._match, { this.hasCopyrights }) : assert(split >= start), + assert(split == start || _body[split] == '\n'); + final String _body; final int start; final int split; @@ -612,7 +634,7 @@ class _PartialLicenseMatch { final bool hasCopyrights; } -Iterable<_PartialLicenseMatch> _findLicenseBlocks(String body, RegExp pattern, int firstPrefixIndex, int indentPrefixIndex, { bool needsCopyright: true }) sync* { +Iterable<_PartialLicenseMatch> _findLicenseBlocks(String body, RegExp pattern, int firstPrefixIndex, int indentPrefixIndex, { bool needsCopyright = true }) sync* { // I tried doing this with one big RegExp initially, but that way lay madness. for (Match match in pattern.allMatches(body)) { assert(match.groupCount >= firstPrefixIndex); @@ -641,9 +663,9 @@ Iterable<_PartialLicenseMatch> _findLicenseBlocks(String body, RegExp pattern, i if (lastWasBlank && (foundNonBlank || !needsCopyright)) break; lastWasBlank = true; - } else if (((!isBlockCommentLine && line.startsWith('/*')) || - line.startsWith('