From 26b2835acd1e5168313e041c6fd12804fab155dc Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 8 Jul 2025 16:40:01 +0100 Subject: [PATCH 01/15] chromium: Add a chromium build/test github action Added a workflow for Chromium compile/test which is inspired from the Firefox version. I've restricted the build matrix to avoid building targets which we are not interested in. But meta-chromium-test provides recipes for building all possible combinations if neeeded. meta-chromium-test repo has the necessary infrastructure for running a basic smoke test. But test is not sufficient enough, so I decided to not to run the test step. Removed -g flags from arm builds. arm linker is unable to link the image if -g flag is used. --- .github/workflows/chromium.yml | 58 +++++++++++++++++++ .../recipes-browser/chromium/chromium-gn.inc | 7 +++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflows/chromium.yml diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml new file mode 100644 index 000000000..38e53b581 --- /dev/null +++ b/.github/workflows/chromium.yml @@ -0,0 +1,58 @@ +name: Chromium build- and smoke-test + +on: + workflow_dispatch: + inputs: + repository: + description: 'Repository to clone for the workflow' + required: true + default: 'brightsign' + branch: + description: 'Branch to checkout for the workflow' + required: true + default: 'master' + pull_request: + branches: + - master + paths: + - 'meta-chromium/**' + - '.github/workflows/chromium.yml' + +permissions: + contents: read + actions: read + checks: write + +jobs: + build: + if: ${{ github.repository_owner == 'brightsign' }} + strategy: + matrix: + yocto_version: [master] + chromium_version: [ozone-wayland, x11] + libc_flavour: [glibc] + arch: [arm, aarch64, x86-64] + runs-on: [self-hosted, chromium] + container: + image: skandigraun/yocto:latest + volumes: + - yocto:/yocto + steps: + - run: | + mkdir -p /yocto/${{ matrix.yocto_version }} + cd /yocto/${{ matrix.yocto_version }} + rm -rf meta-browser meta-chromium-test + # Clean stale pseudo state from any previous interrupted builds + rm -rf build/tmp/work/*/*/*/pseudo build/tmp/sysroots-components/*/pseudo 2>/dev/null || true + if [ "${{ github.event_name }}" = "pull_request" ]; then + GH_URL="$GITHUB_SERVER_URL/${{ github.event.pull_request.head.repo.full_name }}" + GH_REV="$GITHUB_HEAD_REF" + else + GH_URL="$GITHUB_SERVER_URL/${{ github.event.inputs.repository }}/meta-browser" + GH_REV="${{ github.event.inputs.branch }}" + fi + git clone $GH_URL + git -C meta-browser checkout $GH_REV + # clone the test repo + git clone https://github.com/brightsign/meta-chromium-test.git + ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} \ No newline at end of file diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index 98716ad3a..cfa7a464d 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -203,6 +203,13 @@ DEBUG_FLAGS:remove:x86 = "-g" DEBUG_FLAGS:append:x86 = "-g1" GN_ARGS += "symbol_level=0" +# For ARM builds, completely remove debug flags that cause binary size issues +# This prevents the "output file too large" linker error on ARM32 +CFLAGS:remove:arm = "-g" +CXXFLAGS:remove:arm = "-g" +TARGET_CFLAGS:remove:arm = "-g" +TARGET_CXXFLAGS:remove:arm = "-g" + # As of Chromium 62.0.3202.94 and Yocto Rocko (GCC 7, binutils 2.29), passing # -g to the compiler results in many linker errors on aarch64, such as: # obj/third_party/WebKit/Source/modules/payments/libpayments.a(PaymentEventDataConversion.o)(.debug_loc+0x4e25): error: relocation overflow in R_AARCH64_ABS32 From 3f783496a511a9eccbdaf42bba007868209d2558 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 15 Jul 2025 11:48:18 +0100 Subject: [PATCH 02/15] chromium.yml: Use walnascar branch of meta-chromium-test for Chromium132 master branch of meta-chromium-test will use newer versions of meta-rust and meta-clang. Newer versions of these compilers cannot compile the older Chromium. Created a walnascar branch on meta-chromium-test which will point to meta-oe, poky and meta-clang master branches which points to refs on master equilavent of walnascar branch. --- .github/workflows/chromium.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 38e53b581..a7e045a89 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -54,5 +54,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone https://github.com/brightsign/meta-chromium-test.git + git clone -b walnascar https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} \ No newline at end of file From db97e5bdd3e35f123a152f20dbf641b7ca2f04cc Mon Sep 17 00:00:00 2001 From: Ariel D'Alessandro Date: Fri, 25 Apr 2025 12:44:49 -0300 Subject: [PATCH 03/15] chromium: Update to 136.0.7103.113 Release notes: https://chromereleases.googleblog.com/2025/05/stable-channel-update-for-desktop_14.html Build and patch changes: ------------------------ Added patches: * 0014-Revert-Remove-libavif-based-AVIF-decoder.patch * 0015-Revert-Remove-third_party-libavif.patch * 0017-rust-Use-adler-instead-of-adler2.patch * 0018-third_party-node-update_node_binaries-Update-nodejs-.patch Updated patches: * 0016-Disable-crabbyavif-to-fix-build-errors.patch * Rebased remaining patches. Removed patches: * 0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch License changes: ---------------- Added licenses: * TBD Removed licenses: * TBD Updated licenses: * TBD Test-built: ----------- * chromium-ozone-wayland * master, clang, MACHINE=qemux86-64, qemuarm, qemuarm64 * chromium-x11 * master, clang, MACHINE=qemux86-64, qemuarm, qemuarm64 Signed-off-by: Ariel D'Alessandro --- .../recipes-browser/chromium/chromium-gn.inc | 28 +- ... chromium-ozone-wayland_136.0.7103.113.bb} | 0 ...4.83.bb => chromium-x11_136.0.7103.113.bb} | 0 .../recipes-browser/chromium/chromium.inc | 286 +- ...ompiler-settings-conflicting-with-OE.patch | 16 +- .../chromium/files/0002-v8-qemu-wrapper.patch | 28 +- .../files/0003-wrapper-extra-flags.patch | 2 +- ...options-not-available-in-release-ver.patch | 15 +- ...ink-latomic-failure-on-CentOS-8-host.patch | 6 +- ...0006-Don-t-pass-unknown-LLVM-options.patch | 6 +- ...riable-must-be-initialized-by-a-cons.patch | 12 +- ...rrect-path-to-libclang_rt.builtins.a.patch | 10 +- ...9-Adjust-the-Rust-build-to-our-needs.patch | 34 +- ...use-std-hardware_destructive_interf.patch} | 12 +- ...ymbol-visibility-to-hidden-when-C-s.patch} | 0 ...g-PDFiumAPIStringBufferAdapter-temp.patch} | 0 ...rt-Remove-libavif-based-AVIF-decoder.patch | 3744 +++++++++++++++++ ...he-Rust-log-crate-to-the-base-loggin.patch | 488 --- ...15-Revert-Remove-third_party-libavif.patch | 333 ++ ...able-crabbyavif-to-fix-build-errors.patch} | 0 ...017-rust-Use-adler-instead-of-adler2.patch | 38 + ...-update_node_binaries-Update-nodejs-.patch | 27 + ...6834.83.bb => gn-native_136.0.7103.113.bb} | 0 23 files changed, 4456 insertions(+), 629 deletions(-) rename meta-chromium/recipes-browser/chromium/{chromium-ozone-wayland_132.0.6834.83.bb => chromium-ozone-wayland_136.0.7103.113.bb} (100%) rename meta-chromium/recipes-browser/chromium/{chromium-x11_132.0.6834.83.bb => chromium-x11_136.0.7103.113.bb} (100%) rename meta-chromium/recipes-browser/chromium/files/{0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch => 0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch} (92%) rename meta-chromium/recipes-browser/chromium/files/{0013-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch => 0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch} (100%) rename meta-chromium/recipes-browser/chromium/files/{0014-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch => 0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch} (100%) create mode 100644 meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch rename meta-chromium/recipes-browser/chromium/files/{0011-Disable-crabbyavif-to-fix-build-errors.patch => 0016-Disable-crabbyavif-to-fix-build-errors.patch} (100%) create mode 100644 meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch rename meta-chromium/recipes-browser/chromium/{gn-native_132.0.6834.83.bb => gn-native_136.0.7103.113.bb} (100%) diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index cfa7a464d..27c9047d5 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -25,12 +25,24 @@ SRC_URI += "\ file://0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch \ file://0009-Adjust-the-Rust-build-to-our-needs.patch \ file://0010-Don-t-require-profiler_builtins.rlib.patch \ - file://0011-Disable-crabbyavif-to-fix-build-errors.patch \ - file://0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch \ - file://0013-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch \ - file://0014-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch \ - file://0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch \ + file://0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch \ + file://0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch \ + file://0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch \ + file://0014-Revert-Remove-libavif-based-AVIF-decoder.patch \ + file://0015-Revert-Remove-third_party-libavif.patch \ + file://0016-Disable-crabbyavif-to-fix-build-errors.patch \ + file://0017-rust-Use-adler-instead-of-adler2.patch \ + file://0018-third_party-node-update_node_binaries-Update-nodejs-.patch \ " + +# Missing third_party sources. +SRC_URI += "\ + git://chromium.googlesource.com/external/github.com/AOMediaCodec/libavif;protocol=https;branch=main;name=libavif;destsuffix=third_party/libavif/src \ +" + +SRCREV_FORMAT .= "_libavif" +SRCREV_libavif = "e7b34a1f5e9f7024d08311c7bae156061b889882" + # ARM/AArch64-specific patches. SRC_URI:append:aarch64 = "${@bb.utils.contains('TUNE_FEATURES', 'crypto', '', ' file://arm/0001-Fix-AES-crypto-SIGILL-on-rpi4-64.patch', d)}" @@ -482,6 +494,12 @@ do_copy_target_rustlibs () { } addtask copy_target_rustlibs after do_configure before do_compile +do_copy_missing_third_party_sources () { + rm -rf ${S}/third_party/libavif/src + cp -r ${UNPACKDIR}/third_party/libavif/src/ ${S}/third_party/libavif/ +} +addtask copy_missing_third_party_sources after do_patch before do_configure + do_configure() { cd ${S} python3 ./build/linux/unbundle/replace_gn_files.py --system-libraries ${GN_UNBUNDLE_LIBS} diff --git a/meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_132.0.6834.83.bb b/meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_136.0.7103.113.bb similarity index 100% rename from meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_132.0.6834.83.bb rename to meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_136.0.7103.113.bb diff --git a/meta-chromium/recipes-browser/chromium/chromium-x11_132.0.6834.83.bb b/meta-chromium/recipes-browser/chromium/chromium-x11_136.0.7103.113.bb similarity index 100% rename from meta-chromium/recipes-browser/chromium/chromium-x11_132.0.6834.83.bb rename to meta-chromium/recipes-browser/chromium/chromium-x11_136.0.7103.113.bb diff --git a/meta-chromium/recipes-browser/chromium/chromium.inc b/meta-chromium/recipes-browser/chromium/chromium.inc index 4b003cd4a..e2a4f585f 100644 --- a/meta-chromium/recipes-browser/chromium/chromium.inc +++ b/meta-chromium/recipes-browser/chromium/chromium.inc @@ -4,7 +4,7 @@ HOMEPAGE = "https://www.chromium.org/Home" CVE_PRODUCT = "chromium:chromium google:chrome" SRC_URI = "https://commondatastorage.googleapis.com/chromium-browser-official/chromium-${PV}.tar.xz" -SRC_URI[sha256sum] = "f98315eacbf3be106feca37f8243d8c4092d4fd832c918aa36dc229eb6ab39e0" +SRC_URI[sha256sum] = "7c765bd13df842a28bb52279b8d711411ac6082151473e07bd70b9a482c0a0ac" S = "${WORKDIR}/chromium-${PV}" @@ -70,14 +70,13 @@ LIC_FILES_CHKSUM = "\ file://${S}/base/third_party/superfasthash/LICENSE;md5=c66981f8ad23c9f279a5b9e07385128c \ file://${S}/base/third_party/symbolize/LICENSE;md5=17ae3b22fe8fa438966625593e2eea85 \ file://${S}/base/third_party/xdg_user_dirs/LICENSE;md5=d998f250c491c329a8254dd1ca62c647 \ - file://${S}/chrome/browser/resources/chromeos/accessibility/chromevox/third_party/tamachiyomi/LICENSE;md5=15772cfcf7016e701ce54554516c0688 \ + file://${S}/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/third_party/tamachiyomi/LICENSE;md5=15772cfcf7016e701ce54554516c0688 \ file://${S}/chrome/installer/mac/third_party/bsdiff/LICENSE;md5=0dbe7a50f028269750631fcbded3846a \ file://${S}/chrome/installer/mac/third_party/xz/LICENSE;md5=84982e6bf3ed99ef2647e48626ffa984 \ file://${S}/chrome/third_party/mozilla_security_manager/LICENSE;md5=0c259b853bbf067b361100ce560adce7 \ file://${S}/ios/third_party/blink/LICENSE;md5=9fdb47308c4e0a2b6d07c5af39e5da1a \ file://${S}/ios/third_party/lottie/LICENSE;md5=1e714768add1e7fdb1288bac703bad06 \ file://${S}/ios/third_party/material_components_ios/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/ios/third_party/material_font_disk_loader_ios/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/ios/third_party/material_internationalization_ios/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/ios/third_party/material_roboto_font_loader_ios/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/ios/third_party/material_sprited_animation_view_ios/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ @@ -86,97 +85,236 @@ LIC_FILES_CHKSUM = "\ file://${S}/ios/third_party/motion_interchange_objc/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/ios/third_party/motion_transitioning_objc/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/native_client_sdk/src/libraries/third_party/newlib-extras/README;md5=e944d73ca3817b7ca8656eafb9497fed \ + file://${S}/net/third_party/mozilla_security_manager/LICENSE;md5=f19cdef64a433efe7fd2ddbfc3a19313 \ file://${S}/net/third_party/mozilla_win/LICENSE;md5=9b13a17f35cae227ee726ee0108d72a2 \ file://${S}/net/third_party/nss/LICENSE;md5=3b1e88e1b9c0b5a4b2881d46cce06a18 \ file://${S}/net/third_party/quiche/src/LICENSE;md5=0fca02217a5d49a14dfe2d11837bb34d \ file://${S}/net/third_party/uri_template/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/abseil-cpp/LICENSE;md5=df52c6edb7adc22e533b2bacc3bd3915 \ - file://${S}/third_party/accessibility-audit/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth/LICENSE;md5=e207fc5a134660990a7068d5a02c7ea8 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_base/LICENSE;md5=8b3fee5c5451b70decc3ea70f7bc0024 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_blockstore/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_base/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_basement/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_fido/LICENSE;md5=ff76e1f91bd64c4593743be9d83a4c5f \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_identity_credentials/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_tasks/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_libraries_identity_googleid_googleid/LICENSE;md5=498e555b4ebef5399d944fb662c61913 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_auto_service_auto_service_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_code_findbugs_jsr305/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_guava_failureaccess/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_j2objc_j2objc_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_squareup_okio_okio_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/javax_inject_javax_inject/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_checkerframework_checker_qual/LICENSE;md5=f32f668c1f4eea36fb53fc4cc7d96385 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_jspecify_jspecify/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_android_support_support_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_android_annotations/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_api/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_auth/LICENSE;md5=c342c84a7fac9459603b5f30a5a93723 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_auth_base/LICENSE;md5=d1680c9b9927e21a8e831e443964c3ff \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_auth_blockstore/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_base/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_basement/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast/LICENSE;md5=1e61529f08860f8fa1c90bf8af8007ff \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast_framework/LICENSE;md5=6688c3d596b2e35dd1928d1ffdf5a9e2 \ + file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_backend_cct/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ + file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_runtime/LICENSE;md5=54d2f6e66dae1f9e4dbf3cb3cc197631 \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast/LICENSE;md5=d4ace05266b66c1c0ccdeba11bbe745e \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast_framework/LICENSE;md5=98aafe35230de6fc18e977f2feda5671 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_clearcut/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cloud_messaging/LICENSE;md5=fb1c3d71ca3681654ec1604b10fc1ace \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_fido/LICENSE;md5=ff76e1f91bd64c4593743be9d83a4c5f \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_flags/LICENSE;md5=92b728c587a67a1b9577e9ca141ca520 \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cloud_messaging/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_flags/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_gcm/LICENSE;md5=92b728c587a67a1b9577e9ca141ca520 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_identity_credentials/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_iid/LICENSE;md5=05fc50851d6d550ef2bb400b326353f0 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_instantapps/LICENSE;md5=cd033bdb088af16ecdbcd95bd5562a30 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_location/LICENSE;md5=6a7afdc2d1780ac0a9bd21bd59640cdf \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_location/LICENSE;md5=18ad89b4a01838496ae844c2b9bd2a7f \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_phenotype/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_stats/LICENSE;md5=92b728c587a67a1b9577e9ca141ca520 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_tasks/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_stats/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_vision/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_vision_common/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ - file://${S}/third_party/android_deps/libs/com_google_android_libraries_identity_googleid_googleid/LICENSE;md5=498e555b4ebef5399d944fb662c61913 \ file://${S}/third_party/android_deps/libs/com_google_android_material_material/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ - file://${S}/third_party/android_deps/libs/com_google_android_play_core_common/LICENSE;md5=8194dd288cb886b87752b09da8cd9287 \ - file://${S}/third_party/android_deps/libs/com_google_android_play_feature_delivery/LICENSE;md5=f2f92822aef2474358b9e1d21514e278 \ - file://${S}/third_party/android_deps/libs/com_google_auto_service_auto_service_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_code_findbugs_jsr305/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_code_gson_gson/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/com_google_android_play_core_common/LICENSE;md5=73c6700ef062ad41c3d27cab65d0c1e2 \ + file://${S}/third_party/android_deps/libs/com_google_android_play_feature_delivery/LICENSE;md5=014a1ef6f09b1a5ac887d86d5f18918d \ + file://${S}/third_party/android_deps/libs/com_google_ar_impress/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_dagger_dagger/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_dagger_hilt_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_common/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_common_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_components/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_datatransport/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders_json/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid/LICENSE;md5=18b56b380e9331ebdf0247a0b10bbaf0 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid_interop/LICENSE;md5=a5923809e496d46d8842ab7d8a52621f \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders_proto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid/LICENSE;md5=57214b126e4e648c9066e6e23d43be77 \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid_interop/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_installations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_installations_interop/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_measurement_connector/LICENSE;md5=a5923809e496d46d8842ab7d8a52621f \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_messaging/LICENSE;md5=6287e01249a45d9de9bc62ca9163c913 \ - file://${S}/third_party/android_deps/libs/com_google_guava_failureaccess/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_measurement_connector/LICENSE;md5=298bd2a9dc20becb6693cfdccd58001a \ + file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_messaging/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_guava_guava_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_protobuf_protobuf_javalite/LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b \ - file://${S}/third_party/android_deps/libs/com_squareup_okio_okio_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_api/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_binder/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_context/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_protobuf_lite/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_grpc_grpc_stub/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/io_perfmark_perfmark_api/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/jakarta_inject_jakarta_inject_api/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/javax_inject_javax_inject/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE;md5=f32f668c1f4eea36fb53fc4cc7d96385 \ - file://${S}/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE;md5=f32f668c1f4eea36fb53fc4cc7d96385 \ file://${S}/third_party/android_deps/libs/org_checkerframework_checker_util/LICENSE;md5=87abbc18e66acb445c00810347051776 \ file://${S}/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations/LICENSE;md5=a5dd953e661e22a77f7b8062ae790f6a \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_android_extensions_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_jspecify_jspecify/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_play_services/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_media/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_opengl/LICENSE;md5=d10e92761a860d4113a7a525c78daf13 \ file://${S}/third_party/android_provider/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_sdk/LICENSE;md5=a9559ed17808a8b10eec6672f993ce75 \ file://${S}/third_party/android_swipe_refresh/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_toolchain/NOTICE;md5=350d21a23b5ad67f48caa7a8e55d32c0 \ + file://${S}/third_party/androidx/committed/libs/androidx_activity_activity/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_activity_activity_compose/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_activity_activity_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_appcompat_appcompat/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_appsearch_appsearch/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_arch_core_core_common/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_asynclayoutinflater_asynclayoutinflater/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_asynclayoutinflater_asynclayoutinflater_appcompat/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_autofill_autofill/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_biometric_biometric/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_browser_browser/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_cardview_cardview/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_collection_collection_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_collection_collection_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_coordinatorlayout_coordinatorlayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_core_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_core_core_animation/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_core_core_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_core_core_viewtree/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_credentials_credentials/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_customview_customview/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_customview_customview_poolingcontainer/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_datastore_datastore_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_documentfile_documentfile/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_dynamicanimation_dynamicanimation/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_emoji2_emoji2/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_emoji2_emoji2_views_helper/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_emoji_emoji/LICENSE;md5=7773bd2f52bfeaf2c4f9eac7cb9227e6 \ + file://${S}/third_party/androidx/committed/libs/androidx_exifinterface_exifinterface/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_fragment_fragment/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_graphics_graphics_path/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_gridlayout_gridlayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_interpolator_interpolator/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_leanback_leanback/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_leanback_leanback_preference/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_legacy_legacy_support_core_ui/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_legacy_legacy_support_core_utils/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_legacy_legacy_support_v4/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_loader_loader/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_localbroadcastmanager_localbroadcastmanager/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_common/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_container/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_database/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_datasource/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_decoder/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_exoplayer/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_extractor/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_session/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media3_media3_ui/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_media_media/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_multidex_multidex/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_palette_palette/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_preference_preference/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_print_print/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_privacysandbox_ads_ads_adservices/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_privacysandbox_ads_ads_adservices_java/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_room_room_common_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_room_room_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_startup_startup_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_swiperefreshlayout_swiperefreshlayout/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_tracing_tracing_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_transition_transition/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_tvprovider_tvprovider/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_vectordrawable_vectordrawable/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_vectordrawable_vectordrawable_animated/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_versionedparcelable_versionedparcelable/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_viewpager_viewpager/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_webkit_webkit/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_window_extensions_extensions/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_window_window/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_window_window_core_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_xr_arcore_arcore/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_xr_runtime_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_xr_runtime_runtime_openxr/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_xr_scenecore_scenecore/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/angle/LICENSE;md5=7abdb66a6948f39c2f469140db5184e2 \ file://${S}/third_party/angle/src/common/third_party/xxhash/LICENSE;md5=cb91c07001f1ca6fd50b6bd4f042946a \ file://${S}/third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/etc_decoder/LICENSE;md5=100356b560682b0ae7739e09db42f319 \ file://${S}/third_party/angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE;md5=64356ee6f79c94525e102a3264a62653 \ file://${S}/third_party/angle/src/third_party/libXNVCtrl/LICENSE;md5=9e6c209899aa4986d090320bbca2fc76 \ file://${S}/third_party/angle/src/third_party/volk/LICENSE.md;md5=af213f738e88117b90238ef76ad25ae6 \ - file://${S}/third_party/angle/third_party/android_system_sdk/LICENSE;md5=cbb64e76df0f98fc138b1918c089417e \ file://${S}/third_party/angle/third_party/flatbuffers/LICENSE;md5=a873c5645c184d51e0f9b34e1d7cf559 \ file://${S}/third_party/angle/third_party/spirv-headers/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/angle/third_party/spirv-tools/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -192,26 +330,29 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/bidimapper/licenses/LICENSE.mitt;md5=4ed316158806c1e39b6b6e457c85b10f \ file://${S}/third_party/bidimapper/licenses/LICENSE.zod;md5=7aa01d261c2ac4ca667875e22474c798 \ file://${S}/third_party/blink/LICENSE_FOR_ABOUT_CREDITS;md5=11e90d553b211de885f245900c4ccf89 \ - file://${S}/third_party/boringssl/src/LICENSE;md5=2ca501bc96ce9ed0814e2c592c3f9593 \ - file://${S}/third_party/boringssl/src/third_party/fiat/LICENSE;md5=8eb8c1cdeb53faab99e4673182bed9f7 \ + file://${S}/third_party/boringssl/src/LICENSE;md5=bfca80950e49496c340e3327990eb7e7 \ + file://${S}/third_party/boringssl/src/third_party/fiat/LICENSE;md5=7a7086e01a29257a15f3b0c9a42a12ff \ file://${S}/third_party/brotli/LICENSE;md5=941ee9cd1609382f946352712a319b4b \ file://${S}/third_party/bspatch/LICENSE;md5=3e837ede9697ce4c789c3ca32aabe003 \ file://${S}/third_party/cardboard/LICENSE;md5=3446ed11f63049b8f1e60e833dcdc5b7 \ file://${S}/third_party/cast_core/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ file://${S}/third_party/catapult/experimental/trace_on_tap/third_party/pako/LICENSE;md5=6b8c8aad0a85200097ec8f989636baf0 \ file://${S}/third_party/catapult/third_party/apiclient/LICENSE;md5=94023d14f6b58272fd885e4e3f2f08b3 \ + file://${S}/third_party/catapult/third_party/beautifulsoup4/COPYING.txt;md5=83e365dc17176bd72ba7d08ca0555efa \ file://${S}/third_party/catapult/third_party/cachetools/LICENSE;md5=27f7518eb6f7dc686d0f953b2f28dae5 \ - file://${S}/third_party/catapult/third_party/chardet/LICENSE;md5=a6f89e2100d9b6cdffcea4f398e37343 \ file://${S}/third_party/catapult/third_party/cloudstorage/COPYING;md5=dcbf253b3d6d09ae7e64cb34b4d0ec33 \ file://${S}/third_party/catapult/third_party/coverage/LICENSE.txt;md5=2ee41112a44fe7014dce33e26468ba93 \ file://${S}/third_party/catapult/third_party/flot/LICENSE.txt;md5=5bc600a435aadbd7dcde045ccb3208bf \ file://${S}/third_party/catapult/third_party/google-auth/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327 \ + file://${S}/third_party/catapult/third_party/graphy/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327 \ file://${S}/third_party/catapult/third_party/gsutil/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/catapult/third_party/idna/LICENSE.rst;md5=782775b32f96098512e283fb5d4546cd \ file://${S}/third_party/catapult/third_party/ijson/LICENSE.txt;md5=2809bd5857eee6be054555222fd89712 \ - file://${S}/third_party/catapult/third_party/oauth2client/LICENSE;md5=88f599f710b9d48dad0929ebd090fc1a \ + file://${S}/third_party/catapult/third_party/jquery/LICENSE.txt;md5=18d9b3a646e7608b8951c4f6aa1bbf25 \ + file://${S}/third_party/catapult/third_party/mapreduce/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ file://${S}/third_party/catapult/third_party/pipeline/LICENSE;md5=8f7bb094c7232b058c7e9f2e431f389c \ file://${S}/third_party/catapult/third_party/polymer/LICENSE.polymer;md5=324f45ce459ffd97e41d175a4e95a4be \ - file://${S}/third_party/catapult/third_party/polymer3/LICENSE.polymer;md5=a798ede8c314cbc7a31b41bce0008d57 \ + file://${S}/third_party/catapult/third_party/pyasn1_modules/LICENSE.txt;md5=a14482d15c2249de3b6f0e8a47e021fd \ file://${S}/third_party/catapult/third_party/pyfakefs/COPYING;md5=34400b68072d710fecd0a2940a0d1658 \ file://${S}/third_party/catapult/third_party/pyparsing/LICENSE;md5=657a566233888513e1f07ba13e2f47f1 \ file://${S}/third_party/catapult/third_party/python_gflags/COPYING;md5=c80d1a3b623f72bb85a4c75b556551df \ @@ -223,6 +364,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.v8;md5=1ea35644f0ec0d9767897115667e901f \ file://${S}/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.valgrind;md5=df3b3d49700e781f7508895abd114277 \ file://${S}/third_party/catapult/third_party/webapp2/LICENSE;md5=40b32c2af961e8ca20e66cdea65bcb36 \ + file://${S}/third_party/catapult/third_party/webtest/license.rst;md5=64f013a9d7a2a8ffc8d016a2d4214bcd \ file://${S}/third_party/catapult/tracing/third_party/jpeg-js/LICENSE;md5=11ed819ab28c14377693b9c990511293 \ file://${S}/third_party/catapult/tracing/third_party/oboe/LICENCE;md5=a50188ab5dc0b5b3963791e0c5f43c6b \ file://${S}/third_party/catapult/tracing/third_party/pako/LICENSE;md5=a4f08d6b2d1bf3f3a1bc296a6109a25b \ @@ -232,6 +374,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/chromevox/third_party/sre/LICENSE;md5=2ee41112a44fe7014dce33e26468ba93 \ file://${S}/third_party/cld_3/LICENSE;md5=d8b32ba83f8b11e4badd979f4319e706 \ file://${S}/third_party/cldr/LICENSE;md5=fe2500bb52e825476d4b93d6f1458232 \ + file://${S}/third_party/compiler-rt/src/LICENSE.TXT;md5=d846d1d65baf322d4c485d6ee54e877a \ file://${S}/third_party/content_analysis_sdk/LICENSE;md5=ee5fc272be9139e1816c73ce09611ca5 \ file://${S}/third_party/coremltools/mlmodel/format/LICENSE;md5=646b3097f3a7222ada5b4561c39d52e8 \ file://${S}/third_party/cpu_features/src/LICENSE;md5=31a8379f6fe09baf921e654832ac5700 \ @@ -259,6 +402,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/devtools-frontend/src/front_end/third_party/i18n/LICENSE;md5=a873c5645c184d51e0f9b34e1d7cf559 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/intl-messageformat/LICENSE;md5=913015c0e75ff89ef9461fadca555f65 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/json5/LICENSE;md5=d80f2808a405d641840b50a06f80e93c \ + file://${S}/third_party/devtools-frontend/src/front_end/third_party/legacy-javascript/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/lighthouse/LICENSE;md5=a873c5645c184d51e0f9b34e1d7cf559 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/lit/LICENSE;md5=4b390b7b932ca7872d1de2c834797cad \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/marked/LICENSE;md5=449f8b1cf0bfef1e5ec7824a4179ac6f \ @@ -295,14 +439,12 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/google_input_tools/LICENSE;md5=ff0b0f0202337c9d4ae2d869797049d3 \ file://${S}/third_party/google_input_tools/third_party/closure_library/LICENSE;md5=24a5bad64ae7d51b99ecb8864af80e84 \ file://${S}/third_party/google_toolbox_for_mac/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/google_trust_services/src/LICENSE;md5=cfaf8febfe7b4f3e07a08dd4d0db1914 \ - file://${S}/third_party/grpc/src/LICENSE;md5=731e401b36f8077ae0c134b59be5c906 \ + file://${S}/third_party/grpc/source/LICENSE;md5=731e401b36f8077ae0c134b59be5c906 \ file://${S}/third_party/harfbuzz-ng/src/COPYING;md5=b98429b8e8e3c2a67cfef01e99e4893d \ file://${S}/third_party/highway/LICENSE;md5=2b42edef8fa55315f34f2370b4715ca9 \ file://${S}/third_party/hunspell/COPYING.MPL;md5=bfe1f75d606912a4111c90743d6c7325 \ file://${S}/third_party/hyphenation-patterns/LICENSE;md5=23d9bd4878e6f3669ed39b2095b01c13 \ file://${S}/third_party/iaccessible2/LICENSE;md5=43bbd0bfb581347ec10def720000a645 \ - file://${S}/third_party/iccjpeg/LICENSE;md5=26834d132689a03abf860f4572705494 \ file://${S}/third_party/icu/LICENSE;md5=08dc3852df8fffa807301902ad899ff8 \ file://${S}/third_party/ink/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/ink_stroke_modeler/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -311,23 +453,21 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/isimpledom/LICENSE;md5=822502c0e010ccdbb6a3e681dd47888e \ file://${S}/third_party/jni_zero/LICENSE;md5=70dc659673a50c63e07b2287222230e6 \ file://${S}/third_party/jsoncpp/LICENSE;md5=c56ee55c03a55f8105b969d8270632ce \ - file://${S}/third_party/jstemplate/COPYING;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/jszip/LICENSE;md5=401d270383d75b05d76fc13fc1750765 \ file://${S}/third_party/khronos/LICENSE;md5=1d2ef853a9ae7ace4e16fda0d48f597b \ file://${S}/third_party/lens_server_proto/LICENSE;md5=57b69a83c4a9b2401aa4de147cb22bd4 \ file://${S}/third_party/leveldatabase/src/LICENSE;md5=92d1b128950b11ba8495b64938fc164d \ file://${S}/third_party/libaddressinput/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/libaom/source/libaom/LICENSE;md5=6ea91368c1bbdf877159435572b931f5 \ - file://${S}/third_party/libavif/LICENSE;md5=15744a27d810133340dc64ad411ec4cd \ + file://${S}/third_party/libaom/source/libaom/PATENTS;md5=a111d47497d3bb49e04eef71377eb8ba \ file://${S}/third_party/libbrlapi/LICENSE;md5=fad9b3332be894bab9bc501572864b29 \ file://${S}/third_party/libc++/src/LICENSE.TXT;md5=55d89dd7eec8d3b4204b680e27da3953 \ file://${S}/third_party/libc++abi/src/LICENSE.TXT;md5=7b9334635b542c56868400a46b272b1e \ file://${S}/third_party/libei/LICENSE;md5=a98fa76460f96f41696611d6f07e8d49 \ - file://${S}/third_party/libevent/LICENSE;md5=a2b0302a710150293fb85d2bfabd3de6 \ file://${S}/third_party/libgav1/src/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/libipp/LICENSE;md5=af9e58383a1b2b17c75c6c9ff561ca9d \ file://${S}/third_party/libjingle_xmpp/LICENSE;md5=ad296492125bc71530d06234d9bfebe0 \ - file://${S}/third_party/libjpeg_turbo/LICENSE.md;md5=2a8e0d8226a102f07ab63ed7fd6ce155 \ + file://${S}/third_party/libjpeg_turbo/LICENSE.md;md5=023c2e8942020502d6ea709e2a5453f7 \ file://${S}/third_party/liblouis/LICENSE;md5=8ab69863de0d3a0e00c4f97a4d78dd4a \ file://${S}/third_party/libphonenumber/LICENSE;md5=e23fadd6ceef8c618fc1c65191d846fa \ file://${S}/third_party/libpng/LICENSE;md5=b0085051bf265bac2bfc38bc89f50000 \ @@ -341,8 +481,11 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/libusb/src/COPYING;md5=fbc093901857fcd118f065f900982c24 \ file://${S}/third_party/libva_protected_content/LICENSE;md5=2e48940f94acb0af582e5ef03537800f \ file://${S}/third_party/libvpx/source/libvpx/LICENSE;md5=d5b04755015be901744a78cc30d390d4 \ + file://${S}/third_party/libvpx/source/libvpx/PATENTS;md5=c6926d0cb07d296f886ab6e0cc5a85b7 \ file://${S}/third_party/libwebm/source/LICENSE.TXT;md5=6e8dee932c26f2dab503abf70c96d8bb \ - file://${S}/third_party/libwebp/LICENSE;md5=72dceabd78c6f435bb304047aff1745a \ + file://${S}/third_party/libwebm/source/PATENTS.TXT;md5=c6926d0cb07d296f886ab6e0cc5a85b7 \ + file://${S}/third_party/libwebp/src/COPYING;md5=6e8dee932c26f2dab503abf70c96d8bb \ + file://${S}/third_party/libwebp/src/PATENTS;md5=c6926d0cb07d296f886ab6e0cc5a85b7 \ file://${S}/third_party/libx11/LICENSE;md5=28a27b3e1a66e95206d5645b787dad69 \ file://${S}/third_party/libxcb-keysyms/LICENSE;md5=5b0382211c6a98f8e4861daa0f3f4322 \ file://${S}/third_party/libxml/src/Copyright;md5=f437ed9058e8e5135e47c01e973376ba \ @@ -352,7 +495,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/lit/LICENSE;md5=4b390b7b932ca7872d1de2c834797cad \ file://${S}/third_party/llvm-libc/src/LICENSE.TXT;md5=ff42885ed2ab98f1ecb8c1fc41205343 \ file://${S}/third_party/lottie/LICENSE;md5=827837648055a0bfb7782b91ab42eed1 \ - file://${S}/third_party/lss/LICENSE;md5=dcd794613f580ae04e9633662024c7a2 \ + file://${S}/third_party/lss/LICENSE;md5=31b650551268648966d774a3ecc710cf \ file://${S}/third_party/lzma_sdk/LICENSE;md5=eceed1d308734c8051664bc3d1ca175e \ file://${S}/third_party/material_color_utilities/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ file://${S}/third_party/material_design_icons/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ @@ -365,7 +508,9 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/modp_b64/LICENSE;md5=eb7e2e0af1d4971360553aedadee8d86 \ file://${S}/third_party/nearby/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/neon_2_sse/LICENSE;md5=53abad1ded16f44100126962f4bbef6c \ - file://${S}/third_party/node/LICENSE;md5=f08301c3b31b4f85e464f95ce1d8bf81 \ + file://${S}/third_party/node/Apache-LICENSE-2.0.txt;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/node/node_modules/@azure/msal-browser/LICENSE;md5=4f9c2c296f77b3096b6c11a16fa7c66e \ + file://${S}/third_party/node/node_modules/lit/LICENSE;md5=4b390b7b932ca7872d1de2c834797cad \ file://${S}/third_party/omnibox_proto/LICENSE;md5=9956e3ce49f0eea45461cec47a5f96b0 \ file://${S}/third_party/one_euro_filter/LICENSE;md5=5b4d092181a412ae42c63e7fc1e53366 \ file://${S}/third_party/openh264/src/LICENSE;md5=bb6d3771da6a07d33fd50d4d9aa73bcf \ @@ -378,6 +523,8 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/pdfium/third_party/agg23/copying;md5=d501f87982fe576fa1bfdf4f0ae741af \ file://${S}/third_party/pdfium/third_party/fp16/LICENSE;md5=855dd24c28c76c916c5c2301b1958728 \ file://${S}/third_party/pdfium/third_party/freetype/FTL.TXT;md5=d479e83797f699fe873b38dadd0fcd4c \ + file://${S}/third_party/pdfium/third_party/highway/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327 \ + file://${S}/third_party/pdfium/third_party/lcms/LICENSE;md5=e9ce323c4b71c943a785db90142b228a \ file://${S}/third_party/pdfium/third_party/libopenjpeg/LICENSE;md5=c648878b4840d7babaade1303e7f108c \ file://${S}/third_party/pdfium/third_party/libtiff/LICENSE.md;md5=a3e32d664d6db1386b4689c8121531c3 \ file://${S}/third_party/perfetto/LICENSE;md5=65fc11c16d093b463bafae828ec00d41 \ @@ -389,17 +536,18 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/protobuf/LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b \ file://${S}/third_party/pthreadpool/src/LICENSE;md5=1609499688b503850848d795dce4da2d \ file://${S}/third_party/puffin/LICENSE;md5=b1ed361f9fc790c1054d81a7ef041a34 \ - file://${S}/third_party/qcms/src/COPYING;md5=65636fa951328d3be95f6047ec3936ce \ file://${S}/third_party/re2/LICENSE;md5=3b5c31eb512bdf3cb11ffd5713963760 \ file://${S}/third_party/rjsmin/LICENSE;md5=34f8c1142fd6208a8be89399cb521df9 \ file://${S}/third_party/rnnoise/COPYING;md5=1890bf89a18f8339491894a0b45428bf \ file://${S}/third_party/ruy/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/screen-ai/THIRD_PARTY_LICENSES;md5=c1060306f371a5ac9a35cfba2d55f902 \ file://${S}/third_party/securemessage/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/selenium-atoms/LICENSE;md5=5cd827bdaf8605a596a7ac9dcf808ea1 \ file://${S}/third_party/selenium-atoms/LICENSE.sizzle;md5=7a9495742f21b7624515e120b720cc65 \ file://${S}/third_party/selenium-atoms/LICENSE.wgxpath;md5=73a4131394317651a4370c0507a9ccb5 \ file://${S}/third_party/sentencepiece/LICENSE;md5=5cb9ee2840d20280145e929f30f32060 \ file://${S}/third_party/shell-encryption/src/LICENSE;md5=c96a2157133614bf8a135bd27cd230d6 \ + file://${S}/third_party/simdutf/LICENSE;md5=44b8879887e312bc332a205805fb41d1 \ file://${S}/third_party/simplejson/LICENSE.txt;md5=8a9f8dcfcd28c34daa443e5b1d29813b \ file://${S}/third_party/skia/LICENSE;md5=822f02cc7736281816581cd064afbb1c \ file://${S}/third_party/smhasher/LICENSE;md5=0d948322ab524e2b74f55eb8ef57c74a \ @@ -430,6 +578,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/vulkan-headers/LICENSE.txt;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/vulkan-loader/src/LICENSE.txt;md5=7dbefed23242760aa3475ee42801c5ac \ file://${S}/third_party/vulkan_memory_allocator/LICENSE.txt;md5=6eed3e06ed116324029b9de40eaf0ee5 \ + file://${S}/third_party/wasm_tts_engine/EIGEN_LICENSE;md5=fb0a45fc2d5c3c92cf3e5c8e19e0e758 \ file://${S}/third_party/wayland-protocols/gtk/COPYING;md5=5f30f0716dfdd0d91eb439ebec522ec2 \ file://${S}/third_party/wayland-protocols/kde/COPYING.LIB;md5=2d5025d4aa3495befef8f17206a5b0a1 \ file://${S}/third_party/wayland-protocols/src/COPYING;md5=c7b12b6702da38ca028ace54aae3d484 \ @@ -437,6 +586,9 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/webrtc/LICENSE;md5=ad296492125bc71530d06234d9bfebe0 \ file://${S}/third_party/webrtc/common_audio/third_party/ooura/LICENSE;md5=08458468f7a32acf1657071c5396f0c7 \ file://${S}/third_party/webrtc/common_audio/third_party/spl_sqrt_floor/LICENSE;md5=aa45275d6069f8d96c1f99741f72ea3f \ + file://${S}/third_party/webrtc/modules/third_party/fft/LICENSE;md5=0d4cd8529c0756f037fe890b12d1f6f8 \ + file://${S}/third_party/webrtc/modules/third_party/g711/LICENSE;md5=b36ce10e2d3d47fd5e3a465cbc9626da \ + file://${S}/third_party/webrtc/modules/third_party/g722/LICENSE;md5=b6ec8735a974f440bc0cd4d93929336e \ file://${S}/third_party/webrtc/modules/third_party/portaudio/LICENSE;md5=9f48147ba9bd3e7ca9a2392d2a3099c1 \ file://${S}/third_party/webrtc/rtc_base/third_party/sigslot/LICENSE;md5=d31e106d9b0e980d8b5773f151e33ec7 \ file://${S}/third_party/woff2/LICENSE;md5=027c71da9e4664fdf192e6ec615f4d18 \ @@ -450,5 +602,5 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/zxcvbn-cpp/LICENSE.txt;md5=50f9303d0b3adb4952217b0d8c00d83f \ file://${S}/tools/mac/power/protos/third_party/pprof/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/url/third_party/mozilla/LICENSE.txt;md5=437ced1e9b232651b0912a9594da43b2 \ - file://${S}/v8/LICENSE;md5=f38a2942edcfe4abb45e9a83a1ad2f82 \ + file://${S}/v8/LICENSE;md5=3f722db07a0a940a6c859c5c9c2c78fd \ " diff --git a/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch b/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch index ea996acef..ed58c3371 100644 --- a/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch +++ b/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch @@ -1,4 +1,4 @@ -From 2e867ac9e97b5ae2090b4de212d9c0e50eee653b Mon Sep 17 00:00:00 2001 +From feb3b8299f312e4074fc76fe7b6dfa236a5823d8 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 25 Jun 2024 11:06:19 +0000 Subject: [PATCH] Drop GN compiler settings conflicting with OE @@ -19,10 +19,10 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 46 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index f58320b88f..309c3078a2 100644 +index e7a60d56b5..b0e9e04bf3 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -1240,26 +1240,6 @@ config("compiler_cpu_abi") { +@@ -1211,26 +1211,6 @@ config("compiler_cpu_abi") { "-msse3", ] } @@ -49,7 +49,7 @@ index f58320b88f..309c3078a2 100644 } else if (current_cpu == "mipsel" && !is_nacl) { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { -@@ -1267,9 +1247,6 @@ config("compiler_cpu_abi") { +@@ -1238,9 +1218,6 @@ config("compiler_cpu_abi") { if (is_android) { cflags += [ "--target=mipsel-linux-android" ] ldflags += [ "--target=mipsel-linux-android" ] @@ -59,7 +59,7 @@ index f58320b88f..309c3078a2 100644 } } else { cflags += [ "-EL" ] -@@ -1349,8 +1326,6 @@ config("compiler_cpu_abi") { +@@ -1320,8 +1297,6 @@ config("compiler_cpu_abi") { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { if (is_clang) { @@ -68,7 +68,7 @@ index f58320b88f..309c3078a2 100644 } else { cflags += [ "-EB" ] ldflags += [ "-EB" ] -@@ -1399,8 +1374,6 @@ config("compiler_cpu_abi") { +@@ -1370,8 +1345,6 @@ config("compiler_cpu_abi") { cflags += [ "--target=mips64el-linux-android" ] ldflags += [ "--target=mips64el-linux-android" ] } else { @@ -77,7 +77,7 @@ index f58320b88f..309c3078a2 100644 } } else { cflags += [ -@@ -1458,8 +1431,6 @@ config("compiler_cpu_abi") { +@@ -1429,8 +1402,6 @@ config("compiler_cpu_abi") { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { if (is_clang) { @@ -86,7 +86,7 @@ index f58320b88f..309c3078a2 100644 } else { cflags += [ "-EB", -@@ -1628,23 +1599,6 @@ config("compiler_deterministic") { +@@ -1599,23 +1570,6 @@ config("compiler_deterministic") { } } } diff --git a/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch b/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch index 080de6e16..9261b47c1 100644 --- a/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch +++ b/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch @@ -1,4 +1,4 @@ -From 60233d0570b4c9c9a9827616b224c9c93cbf6254 Mon Sep 17 00:00:00 2001 +From d326bbe195c6853c600d01b9d438b5ee430b55c2 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Tue, 7 Nov 2017 15:24:32 +0100 Subject: [PATCH] v8: qemu wrapper @@ -17,22 +17,22 @@ Signed-off-by: Maksim Sisov 2 files changed, 5 insertions(+) diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn -index f46bec2eb5..54383655c0 100644 +index 70c32b2fc9..88497d1436 100644 --- a/tools/v8_context_snapshot/BUILD.gn +++ b/tools/v8_context_snapshot/BUILD.gn -@@ -45,6 +45,7 @@ if (use_v8_context_snapshot) { - output_path = rebase_path(output_file, root_build_dir) +@@ -49,6 +49,7 @@ if (use_v8_context_snapshot) { + output_path = rebase_path(output_file, root_build_dir) - args = [ -+ "./v8-qemu-wrapper.sh", - "./" + rebase_path(get_label_info(":v8_context_snapshot_generator", - "root_out_dir") + - "/v8_context_snapshot_generator", + args = [ ++ "./v8-qemu-wrapper.sh", + "./" + rebase_path( + get_label_info( + ":v8_context_snapshot_generator($v8_snapshot_toolchain)", diff --git a/v8/BUILD.gn b/v8/BUILD.gn -index 0c559713ea..c8c95eb3a9 100644 +index 9a2b2cdd94..8c217b57c9 100644 --- a/v8/BUILD.gn +++ b/v8/BUILD.gn -@@ -2274,6 +2274,7 @@ template("run_torque") { +@@ -2228,6 +2228,7 @@ template("run_torque") { } args = [ @@ -40,7 +40,7 @@ index 0c559713ea..c8c95eb3a9 100644 "./" + rebase_path( get_label_info(":torque($toolchain)", "root_out_dir") + "/torque", root_build_dir), -@@ -2437,6 +2438,7 @@ action("generate_bytecode_builtins_list") { +@@ -2391,6 +2392,7 @@ action("generate_bytecode_builtins_list") { outputs = [ "$target_gen_dir/builtins-generated/bytecodes-builtins-list.h" ] deps = [ ":bytecode_builtins_list_generator($v8_generator_toolchain)" ] args = [ @@ -48,7 +48,7 @@ index 0c559713ea..c8c95eb3a9 100644 "./" + rebase_path( get_label_info( ":bytecode_builtins_list_generator($v8_generator_toolchain)", -@@ -2509,6 +2511,7 @@ template("run_mksnapshot") { +@@ -2470,6 +2472,7 @@ template("run_mksnapshot") { } args += [ @@ -56,7 +56,7 @@ index 0c559713ea..c8c95eb3a9 100644 "./" + rebase_path(get_label_info(":mksnapshot($v8_snapshot_toolchain)", "root_out_dir") + "/mksnapshot", root_build_dir), -@@ -7451,6 +7454,7 @@ if (v8_enable_i18n_support) { +@@ -7427,6 +7430,7 @@ if (v8_enable_i18n_support) { outputs = [ output_file ] args = [ diff --git a/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch b/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch index 56493bbc4..5f012500a 100644 --- a/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch +++ b/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch @@ -1,4 +1,4 @@ -From 71eca88e5d42d4732dfe53ea8fadf2199db519a2 Mon Sep 17 00:00:00 2001 +From a8c669d57bd409ba07255bd52fe3c9f0817de2ac Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Wed, 8 Nov 2017 16:43:47 +0100 Subject: [PATCH] wrapper: extra flags diff --git a/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch b/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch index 1b3e23dcb..55401d2f2 100644 --- a/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch +++ b/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch @@ -1,4 +1,4 @@ -From fc3a1af7d8d6ed6bc54d096e72cbf3d35ff4fa97 Mon Sep 17 00:00:00 2001 +From 0a052a88bf96542e0bb7585ccbfd39e681a32e3d Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Wed, 4 Dec 2019 19:06:54 -0800 Subject: [PATCH] Delete compiler options not available in release versions of @@ -17,29 +17,28 @@ Signed-off-by: Randy MacLeod Signed-off-by: Ariel D'Alessandro --- - build/config/compiler/BUILD.gn | 13 +++++-------- - 1 file changed, 5 insertions(+), 8 deletions(-) + build/config/compiler/BUILD.gn | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index 309c3078a2647..5eeb241fc9038 100644 +index b0e9e04bf3..46ed84d73c 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -615,14 +615,6 @@ config("compiler") { +@@ -614,13 +614,6 @@ config("compiler") { } else { cflags += [ "-ffp-contract=off" ] } - - # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF - # (excluding toolchains that use an older version of LLVM). -- # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. -- if (is_linux && !llvm_android_mainline && current_cpu != "arm" && +- if (is_linux && !llvm_android_mainline && - default_toolchain != "//build/toolchain/cros:target") { - cflags += [ "-Wa,--crel,--allow-experimental-crel" ] - } } # C11/C++11 compiler flags setup. -@@ -1924,6 +1916,11 @@ config("default_warnings") { +@@ -1934,6 +1927,11 @@ config("default_warnings") { } cflags += [ diff --git a/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch b/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch index 9a4359307..ff4660210 100644 --- a/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch +++ b/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch @@ -1,4 +1,4 @@ -From c68f4f68490005e1576ca2339b1124dca5f83867 Mon Sep 17 00:00:00 2001 +From be9a610454f3a90e2610443a424efcb34b4903e5 Mon Sep 17 00:00:00 2001 From: Hongxu Jia Date: Fri, 22 Jan 2021 00:02:25 +0800 Subject: [PATCH] avoid link latomic failure on CentOS 8 host @@ -18,10 +18,10 @@ Signed-off-by: Randy MacLeod 2 files changed, 4 insertions(+) diff --git a/base/BUILD.gn b/base/BUILD.gn -index 5dee8a9226..bc3e57164a 100644 +index 57a6af0061..2703e81421 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn -@@ -1077,7 +1077,9 @@ component("base") { +@@ -1104,7 +1104,9 @@ component("base") { # Needed for if using newer C++ library than sysroot, except if # building inside the cros_sdk environment - use host_toolchain as a # more robust check for this. diff --git a/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch b/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch index 4c9bf722a..de6266158 100644 --- a/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch +++ b/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch @@ -1,4 +1,4 @@ -From 651736ea42939ae67169d3429489ae9f304eae3c Mon Sep 17 00:00:00 2001 +From 8e9bf93215630604eed49476d6e848ad3ec3bdba Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Fri, 8 Dec 2023 11:47:43 +0000 Subject: [PATCH] Don't pass unknown LLVM options @@ -17,10 +17,10 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 18 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index 8951f6006c..cc300e7122 100644 +index 46ed84d73c..5f7cfb69e9 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -591,24 +591,6 @@ config("compiler") { +@@ -590,24 +590,6 @@ config("compiler") { } } diff --git a/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch b/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch index 46aec8a3d..c0dcffa6c 100644 --- a/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch +++ b/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch @@ -1,4 +1,4 @@ -From 5a28cb783a33391476839a5b904f0eab7fb9b13d Mon Sep 17 00:00:00 2001 +From 713169dbbcb3081744ea103749597885bb1b97a2 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Fri, 22 Mar 2024 10:43:47 +0000 Subject: [PATCH] Fix "constexpr variable must be initialized by a constant @@ -10,13 +10,13 @@ change the problematic expressions to be const instead. Upstream-Status: Inappropriate [specific to older versions of clang] Signed-off-by: Max Ihlenfeldt --- - .../autofill/core/browser/data_model/autofill_i18n_api.h | 4 ++-- + .../core/browser/data_model/addresses/autofill_i18n_api.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) -diff --git a/components/autofill/core/browser/data_model/autofill_i18n_api.h b/components/autofill/core/browser/data_model/autofill_i18n_api.h -index fa1a06bdad..8e1fe01bfc 100644 ---- a/components/autofill/core/browser/data_model/autofill_i18n_api.h -+++ b/components/autofill/core/browser/data_model/autofill_i18n_api.h +diff --git a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h +index e926bdca5b..799c4b3f80 100644 +--- a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h ++++ b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.h @@ -16,8 +16,8 @@ namespace autofill::i18n_model_definition { // Country code that represents autofill's legacy address hierarchy model as // stored `kAutofillModelRules`. As a workaround for GCC we declare the diff --git a/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch b/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch index 421758343..465bc5412 100644 --- a/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch +++ b/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch @@ -1,4 +1,4 @@ -From b9b4dffd99fd57dc3acbce7304284c26a10e695e Mon Sep 17 00:00:00 2001 +From 6fcf4a20eef34ad3577505b8c36cd2ecd5e97676 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 19 Dec 2023 12:14:05 +0000 Subject: [PATCH] Use the correct path to libclang_rt.builtins.a @@ -21,12 +21,12 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn -index 44bd202d2b..6789beccdc 100644 +index 0d648fa310..4652cb5127 100644 --- a/build/config/clang/BUILD.gn +++ b/build/config/clang/BUILD.gn -@@ -166,14 +166,15 @@ template("clang_lib") { - } else if (is_apple) { - _dir = "darwin" +@@ -199,14 +199,15 @@ template("clang_lib") { + assert(false) # Unhandled cpu type + } } else if (is_linux || is_chromeos) { + _dir = "linux" if (current_cpu == "x64") { diff --git a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch index 495a56ada..d25447620 100644 --- a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch +++ b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch @@ -1,4 +1,4 @@ -From f4f50a5de596d13d18f1f9b80f83e446936a4afb Mon Sep 17 00:00:00 2001 +From 394ff496602bf9695c7f815f5f73c0027ab4ddc7 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 16 Jan 2024 12:29:30 +0000 Subject: [PATCH] Adjust the Rust build to our needs @@ -16,14 +16,14 @@ Subject: [PATCH] Adjust the Rust build to our needs Upstream-Status: Inappropriate [specific to our build setup] Signed-off-by: Max Ihlenfeldt --- - build/config/rust.gni | 26 +++++++++++++++++++------- + build/config/rust.gni | 28 ++++++++++++++++++++-------- build/rust/rustc_wrapper.py | 1 + build/rust/std/BUILD.gn | 28 +++++++++++++++++++++------- build/rust/std/find_std_rlibs.py | 16 +++++++++++----- - 4 files changed, 52 insertions(+), 19 deletions(-) + 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/build/config/rust.gni b/build/config/rust.gni -index 60856e609d..4baeaecb9c 100644 +index 74b3b9ced5..5affd0b445 100644 --- a/build/config/rust.gni +++ b/build/config/rust.gni @@ -102,6 +102,11 @@ declare_args() { @@ -63,12 +63,16 @@ index 60856e609d..4baeaecb9c 100644 cargo_target_abi = "" } else if (current_cpu == "arm") { if (arm_float_abi == "hard") { -@@ -191,18 +203,18 @@ if (is_linux || is_chromeos) { - } - if (arm_arch == "armv7-a" || arm_arch == "armv7") { - # No way to inform Rust about the -a suffix. -- rust_abi_target = "armv7-unknown-linux-gnueabi" + float_suffix -+ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix +@@ -201,21 +213,21 @@ if (is_linux || is_chromeos) { + # targets in fact target Thumb, see: + # https://github.com/rust-lang/rust/issues/44722 + if (arm_use_neon) { +- rust_abi_target = "thumbv7neon-unknown-linux-gnueabi" + float_suffix ++ rust_abi_target = "thumbv7neon" + vendor + "-linux-gnueabi" + float_suffix + } else { +- rust_abi_target = "armv7-unknown-linux-gnueabi" + float_suffix ++ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix + } cargo_target_abi = "eabi" + float_suffix } else { - rust_abi_target = "arm-unknown-linux-gnueabi" + float_suffix @@ -99,10 +103,10 @@ index 8f2096dfe5..de43d44eed 100755 abs_build_root = os.getcwd().replace('\\', '/') + '/' is_windows = sys.platform == 'win32' or args.target_windows diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn -index b5f5291283..9619dccc45 100644 +index 6b996aa1fe..146f6633ce 100644 --- a/build/rust/std/BUILD.gn +++ b/build/rust/std/BUILD.gn -@@ -191,7 +191,8 @@ if (toolchain_has_rust) { +@@ -207,7 +207,8 @@ if (toolchain_has_rust) { # our locally-built std. Both reside in root_out_dir: we must only have one of # each per GN toolchain anyway. @@ -112,7 +116,7 @@ index b5f5291283..9619dccc45 100644 if (!rust_prebuilt_stdlib) { local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" -@@ -320,12 +321,12 @@ if (toolchain_has_rust) { +@@ -336,12 +337,12 @@ if (toolchain_has_rust) { rust_abi_target, ] @@ -128,7 +132,7 @@ index b5f5291283..9619dccc45 100644 } visibility = [ ":*" ] -@@ -338,8 +339,18 @@ if (toolchain_has_rust) { +@@ -354,8 +355,18 @@ if (toolchain_has_rust) { "enable_rust=false") deps = [ ":find_stdlib" ] sources = get_target_outputs(":find_stdlib") @@ -149,7 +153,7 @@ index b5f5291283..9619dccc45 100644 visibility = [ ":*" ] } -@@ -379,7 +390,10 @@ if (toolchain_has_rust) { +@@ -395,7 +406,10 @@ if (toolchain_has_rust) { ":prebuilt_stdlib_libs", ":stdlib_public_dependent_libs", ] diff --git a/meta-chromium/recipes-browser/chromium/files/0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch b/meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch similarity index 92% rename from meta-chromium/recipes-browser/chromium/files/0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch rename to meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch index c9a94ffd5..0dca0b54d 100644 --- a/meta-chromium/recipes-browser/chromium/files/0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch +++ b/meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch @@ -21,7 +21,7 @@ Signed-off-by: Max Ihlenfeldt 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/components/media_router/common/providers/cast/channel/enum_table.h b/components/media_router/common/providers/cast/channel/enum_table.h -index fba9c05770..9cd7596443 100644 +index fdf8941c32..9340ba2857 100644 --- a/components/media_router/common/providers/cast/channel/enum_table.h +++ b/components/media_router/common/providers/cast/channel/enum_table.h @@ -12,7 +12,6 @@ @@ -32,7 +32,7 @@ index fba9c05770..9cd7596443 100644 #include #include #include -@@ -366,7 +365,8 @@ class EnumTable { +@@ -368,7 +367,8 @@ class EnumTable { private: #ifdef ARCH_CPU_64_BITS @@ -43,10 +43,10 @@ index fba9c05770..9cd7596443 100644 std::initializer_list data_; bool is_sorted_; diff --git a/styleguide/c++/c++-features.md b/styleguide/c++/c++-features.md -index 8bde1b84da..a9222082f6 100644 +index fec5e73654..ffaf71fd54 100644 --- a/styleguide/c++/c++-features.md +++ b/styleguide/c++/c++-features.md -@@ -574,6 +574,35 @@ Overlaps with utilities in `base/strings/string_number_conversions.h`, which are +@@ -587,6 +587,35 @@ Overlaps with utilities in `base/strings/string_number_conversions.h`, which are easier to use correctly. *** @@ -79,10 +79,10 @@ index 8bde1b84da..a9222082f6 100644 +[Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/cwktrFxxUY4) +*** + - ### std::in_place{_type,_index}[_t] [banned] + ### std::{pmr::memory_resource,polymorphic_allocator} [banned] ```c++ -@@ -1200,31 +1229,6 @@ avoiding the need to use the `erase(remove(...` paradigm. +@@ -1170,31 +1199,6 @@ avoiding the need to use the `erase(remove(...` paradigm. [Migration bug](https://crbug.com/1414639) *** diff --git a/meta-chromium/recipes-browser/chromium/files/0013-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch b/meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch similarity index 100% rename from meta-chromium/recipes-browser/chromium/files/0013-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch rename to meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch diff --git a/meta-chromium/recipes-browser/chromium/files/0014-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch b/meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch similarity index 100% rename from meta-chromium/recipes-browser/chromium/files/0014-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch rename to meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch diff --git a/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch b/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch new file mode 100644 index 000000000..d7f2d2abf --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch @@ -0,0 +1,3744 @@ +From 75e1653ce9bf360e4c1cb5b86872ebff0b67a35b Mon Sep 17 00:00:00 2001 +From: Ariel D'Alessandro +Date: Thu, 17 Jul 2025 12:51:22 -0300 +Subject: [PATCH] Revert "Remove libavif based AVIF decoder" + +This reverts commit ce78002f97a433e53ab16805e16b895f9b52ea24. + +In order to disable crabbyavif to fix build errors, re-enable libavif so +it can be used in replacement. + +Upstream-Status: Inappropriate [upstream ticket https://crbug.com/357017325] +Signed-off-by: Ariel D'Alessandro +--- + .../common/ProductionSupportedFlagList.java | 3 + + chrome/browser/about_flags.cc | 4 + + chrome/browser/flag-metadata.json | 5 + + chrome/browser/flag_descriptions.cc | 5 + + chrome/browser/flag_descriptions.h | 3 + + third_party/blink/common/features.cc | 2 + + third_party/blink/public/common/features.h | 3 + + third_party/blink/renderer/platform/BUILD.gn | 13 + + .../renderer/platform/image-decoders/BUILD.gn | 12 +- + .../image-decoders/avif/avif_image_decoder.cc | 1295 ++++++++++++ + .../image-decoders/avif/avif_image_decoder.h | 191 ++ + .../avif/avif_image_decoder_fuzzer.cc | 30 + + .../avif/avif_image_decoder_test.cc | 1758 +++++++++++++++++ + .../avif/gen_crabbyavif_wrapper.py | 164 ++ + .../platform/image-decoders/image_decoder.cc | 17 +- + .../image_decoder_fuzzer_utils.cc | 8 + + .../image_decoder_fuzzer_utils.h | 1 + + 17 files changed, 3508 insertions(+), 6 deletions(-) + create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc + create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h + create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc + create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc + create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py + +diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +index def02f59887c1..3bfa4dc335cb1 100644 +--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java ++++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java +@@ -434,6 +434,9 @@ public final class ProductionSupportedFlagList { + Flag.baseFeature( + BaseFeatures.RUN_TASKS_BY_BATCHES, + "Run tasks in queue for 8ms before before sending a system message."), ++ Flag.baseFeature( ++ BlinkFeatures.CRABBY_AVIF, ++ "If enabled, CrabbyAvif will be used instead of libavif for decoding AVIF images."), + Flag.baseFeature( + NetworkServiceFeatures.DEPRECATE_UNLOAD, + "If false prevents the gradual deprecation of the unload event."), +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 5156e5d3f2c74..1a0e2039bda1d 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -7780,6 +7780,10 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kAvifGainmapHdrImagesDescription, kOsAll, + FEATURE_VALUE_TYPE(blink::features::kAvifGainmapHdrImages)}, + ++ {"crabbyavif", flag_descriptions::kCrabbyAvifName, ++ flag_descriptions::kCrabbyAvifDescription, kOsAll, ++ FEATURE_VALUE_TYPE(blink::features::kCrabbyAvif)}, ++ + {"file-handling-icons", flag_descriptions::kFileHandlingIconsName, + flag_descriptions::kFileHandlingIconsDescription, kOsDesktop, + FEATURE_VALUE_TYPE(blink::features::kFileHandlingIcons)}, +diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json +index d6851e527feb1..bd09150e90221 100644 +--- a/chrome/browser/flag-metadata.json ++++ b/chrome/browser/flag-metadata.json +@@ -1658,6 +1658,11 @@ + "owners": ["sebsg@chromium.org", "bling-transactions-eng@google.com"], + "expiry_milestone": 135 + }, ++ { ++ "name": "crabbyavif", ++ "owners": [ "vigneshv@google.com", "image-codecs-eng@google.com" ], ++ "expiry_milestone": 133 ++ }, + { + "name": "cras-processor-wav-dump", + "owners": ["aaronyu@google.com", "chromeos-audio@google.com" ], +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index c06c6c65b1398..1050fe6c31ca1 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -446,6 +446,11 @@ const char kAvifGainmapHdrImagesDescription[] = + "If enabled, Chrome uses the gainmap (if present) in AVIF images to render " + "the HDR version on HDR displays and the SDR version on SDR displays."; + ++const char kCrabbyAvifName[] = "CrabbyAvif for decoding AVIF images"; ++const char kCrabbyAvifDescription[] = ++ "If enabled, CrabbyAvif will be used instead of libavif for decoding AVIF " ++ "images"; ++ + const char kTangibleSyncName[] = "Tangible Sync"; + const char kTangibleSyncDescription[] = + "Enables the tangible sync when a user starts the sync consent flow"; +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index be3f7a6e96f05..0e4e01f106ba8 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -312,6 +312,9 @@ extern const char kForestFeatureDescription[]; + extern const char kAvifGainmapHdrImagesName[]; + extern const char kAvifGainmapHdrImagesDescription[]; + ++extern const char kCrabbyAvifName[]; ++extern const char kCrabbyAvifDescription[]; ++ + extern const char kTangibleSyncName[]; + extern const char kTangibleSyncDescription[]; + +diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc +index 720c38313bd13..e4d60ff6ade3e 100644 +--- a/third_party/blink/common/features.cc ++++ b/third_party/blink/common/features.cc +@@ -510,6 +510,8 @@ BASE_FEATURE(kCorrectFloatExtensionTestForWebGL, + "CorrectFloatExtensionTestForWebGL", + base::FEATURE_ENABLED_BY_DEFAULT); + ++BASE_FEATURE(kCrabbyAvif, "CrabbyAvif", base::FEATURE_ENABLED_BY_DEFAULT); ++ + // When enabled, add a new option, {imageOrientation: 'none'}, to + // createImageBitmap, which ignores the image orientation metadata of the source + // and renders the image as encoded. +diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h +index 9970347ef0ccb..220f447a0725e 100644 +--- a/third_party/blink/public/common/features.h ++++ b/third_party/blink/public/common/features.h +@@ -366,6 +366,9 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM( + // See http://crbug.com/40788570. + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDevToolsImprovedNetworkError); + ++// Enables the use of CrabbyAvif for decoding AVIF images. ++BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCrabbyAvif); ++ + // Enables input IPC to directly target the renderer's compositor thread without + // hopping through the IO thread first. + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDirectCompositorThreadIpc); +diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn +index 291676c658372..57505f9d2622c 100644 +--- a/third_party/blink/renderer/platform/BUILD.gn ++++ b/third_party/blink/renderer/platform/BUILD.gn +@@ -2786,6 +2786,19 @@ fuzzer_test("web_icon_sizes_fuzzer") { + additional_configs = [ "//third_party/blink/renderer:inside_blink" ] + } + ++# Fuzzer for blink::AVIFImageDecoder ++fuzzer_test("blink_avif_decoder_fuzzer") { ++ sources = [ "image-decoders/avif/avif_image_decoder_fuzzer.cc" ] ++ deps = [ ++ ":blink_fuzzer_test_support", ++ ":blink_image_decoder_fuzzer_test_support", ++ ":platform", ++ ] ++ seed_corpuses = [ "//third_party/blink/web_tests/images/resources/avif" ] ++ libfuzzer_options = [ "rss_limit_mb=8192" ] ++ additional_configs = [ "//third_party/blink/renderer:inside_blink" ] ++} ++ + # Fuzzer for blink::CrabbyAVIFImageDecoder + fuzzer_test("blink_crabbyavif_decoder_fuzzer") { + sources = [ "image-decoders/avif/crabbyavif_image_decoder_fuzzer.cc" ] +diff --git a/third_party/blink/renderer/platform/image-decoders/BUILD.gn b/third_party/blink/renderer/platform/image-decoders/BUILD.gn +index 870c7e8537249..e638c6bfcd93a 100644 +--- a/third_party/blink/renderer/platform/image-decoders/BUILD.gn ++++ b/third_party/blink/renderer/platform/image-decoders/BUILD.gn +@@ -79,11 +79,16 @@ component("image_decoders") { + + if (enable_av1_decoder) { + sources += [ ++ "avif/avif_image_decoder.cc", ++ "avif/avif_image_decoder.h", + "avif/crabbyavif_image_decoder.cc", + "avif/crabbyavif_image_decoder.h", + ] + +- deps += [ "//third_party/crabbyavif" ] ++ deps += [ ++ "//third_party/crabbyavif", ++ "//third_party/libavif", ++ ] + } + + if (enable_rust_png) { +@@ -130,6 +135,9 @@ source_set("unit_tests") { + } + + if (enable_av1_decoder) { +- sources += [ "avif/crabbyavif_image_decoder_test.cc" ] ++ sources += [ ++ "avif/avif_image_decoder_test.cc", ++ "avif/crabbyavif_image_decoder_test.cc", ++ ] + } + } +diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc +new file mode 100644 +index 0000000000000..eb9448d06d30a +--- /dev/null ++++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc +@@ -0,0 +1,1295 @@ ++// Copyright 2020 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifdef UNSAFE_BUFFERS_BUILD ++// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. ++#pragma allow_unsafe_buffers ++#endif ++ ++#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "base/bits.h" ++#include "base/containers/adapters.h" ++#include "base/feature_list.h" ++#include "base/functional/bind.h" ++#include "base/logging.h" ++#include "base/memory/scoped_refptr.h" ++#include "base/metrics/histogram_functions.h" ++#include "base/numerics/safe_conversions.h" ++#include "base/timer/elapsed_timer.h" ++#include "build/build_config.h" ++#include "cc/base/math_util.h" ++#include "media/base/video_color_space.h" ++#include "skia/ext/cicp.h" ++#include "third_party/blink/public/common/features.h" ++#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_animation.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" ++#include "third_party/blink/renderer/platform/image-decoders/rw_buffer.h" ++#include "third_party/libavif/src/include/avif/avif.h" ++#include "third_party/libyuv/include/libyuv.h" ++#include "third_party/skia/include/core/SkColorSpace.h" ++#include "third_party/skia/include/core/SkTypes.h" ++#include "third_party/skia/include/private/SkXmp.h" ++#include "ui/gfx/color_space.h" ++#include "ui/gfx/icc_profile.h" ++ ++#if defined(ARCH_CPU_BIG_ENDIAN) ++#error Blink assumes a little-endian target. ++#endif ++ ++namespace blink { ++ ++namespace { ++ ++// The maximum AVIF file size we are willing to decode. This helps libavif ++// detect invalid sizes and offsets in an AVIF file before the file size is ++// known. ++constexpr uint64_t kMaxAvifFileSize = 0x10000000; // 256 MB ++ ++const char* AvifDecoderErrorMessage(const avifDecoder* decoder) { ++ // decoder->diag.error is a char array that stores a null-terminated C string. ++ return *decoder->diag.error != '\0' ? decoder->diag.error ++ : "(no error message)"; ++} ++ ++// Builds a gfx::ColorSpace from the ITU-T H.273 (CICP) color description. ++gfx::ColorSpace GetColorSpace( ++ avifColorPrimaries color_primaries, ++ avifTransferCharacteristics transfer_characteristics, ++ avifMatrixCoefficients matrix_coefficients, ++ avifRange yuv_range, ++ bool grayscale) { ++ // (As of ISO/IEC 23000-22:2019 Amendment 2) MIAF Section 7.3.6.4 says: ++ // If a coded image has no associated colour property, the default property ++ // is defined as having colour_type equal to 'nclx' with properties as ++ // follows: ++ // – colour_primaries equal to 1, ++ // - transfer_characteristics equal to 13, ++ // - matrix_coefficients equal to 5 or 6 (which are functionally identical), ++ // and ++ // - full_range_flag equal to 1. ++ // ... ++ // These values correspond to AVIF_COLOR_PRIMARIES_BT709, ++ // AVIF_TRANSFER_CHARACTERISTICS_SRGB, and AVIF_MATRIX_COEFFICIENTS_BT601, ++ // respectively. ++ // ++ // Note that this only specifies the default color property when the color ++ // property is absent. It does not really specify the default values for ++ // colour_primaries, transfer_characteristics, and matrix_coefficients when ++ // they are equal to 2 (unspecified). But we will interpret it as specifying ++ // the default values for these variables because we must choose some defaults ++ // and these are the most reasonable defaults to choose. We also advocate that ++ // all AVIF decoders choose these defaults: ++ // https://github.com/AOMediaCodec/av1-avif/issues/84 ++ const auto primaries = color_primaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED ++ ? AVIF_COLOR_PRIMARIES_BT709 ++ : color_primaries; ++ const auto transfer = ++ transfer_characteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED ++ ? AVIF_TRANSFER_CHARACTERISTICS_SRGB ++ : transfer_characteristics; ++ const auto matrix = ++ (grayscale || matrix_coefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) ++ ? AVIF_MATRIX_COEFFICIENTS_BT601 ++ : matrix_coefficients; ++ const auto range = yuv_range == AVIF_RANGE_FULL ++ ? gfx::ColorSpace::RangeID::FULL ++ : gfx::ColorSpace::RangeID::LIMITED; ++ media::VideoColorSpace color_space(primaries, transfer, matrix, range); ++ if (color_space.IsSpecified()) { ++ return color_space.ToGfxColorSpace(); ++ } ++ // media::VideoColorSpace and gfx::ColorSpace do not support CICP ++ // MatrixCoefficients 12, 13, 14. ++ DCHECK_GE(matrix, 12); ++ DCHECK_LE(matrix, 14); ++ if (yuv_range == AVIF_RANGE_FULL) { ++ return gfx::ColorSpace::CreateJpeg(); ++ } ++ return gfx::ColorSpace::CreateREC709(); ++} ++ ++// Builds a gfx::ColorSpace from the ITU-T H.273 (CICP) color description in the ++// image. ++gfx::ColorSpace GetColorSpace(const avifImage* image) { ++ const bool grayscale = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400; ++ return GetColorSpace(image->colorPrimaries, image->transferCharacteristics, ++ image->matrixCoefficients, image->yuvRange, grayscale); ++} ++ ++// |y_size| is the width or height of the Y plane. Returns the width or height ++// of the U and V planes. |chroma_shift| represents the subsampling of the ++// chroma (U and V) planes in the x (for width) or y (for height) direction. ++int UVSize(int y_size, int chroma_shift) { ++ DCHECK(chroma_shift == 0 || chroma_shift == 1); ++ return (y_size + chroma_shift) >> chroma_shift; ++} ++ ++float FractionToFloat(auto numerator, uint32_t denominator) { ++ // First cast to double and not float because uint32_t->float conversion can ++ // cause precision loss. ++ return static_cast(numerator) / denominator; ++} ++ ++// If the image has a gain map, returns the alternate image's color space, if ++// it's different from the base image's and can be converted to a SkColorSpace. ++// If the alternate image color space is the same as the base image, there is no ++// need to specify it in SkGainmapInfo, and using the base image's color space ++// may be more accurate if the profile cannot be exactly represented as a ++// SkColorSpace object. ++sk_sp GetAltImageColorSpace(const avifImage& image) { ++ const avifGainMap* gain_map = image.gainMap; ++ if (!gain_map) { ++ return nullptr; ++ } ++ sk_sp color_space; ++ if (gain_map->altICC.size) { ++ if (image.icc.size == gain_map->altICC.size && ++ memcmp(gain_map->altICC.data, image.icc.data, gain_map->altICC.size) == ++ 0) { ++ // Same ICC as the base image, no need to specify it. ++ return nullptr; ++ } ++ std::unique_ptr profile = ColorProfile::Create( ++ base::span(gain_map->altICC.data, gain_map->altICC.size)); ++ if (!profile) { ++ DVLOG(1) << "Failed to parse gain map ICC profile"; ++ return nullptr; ++ } ++ const skcms_ICCProfile* icc_profile = profile->GetProfile(); ++ if (icc_profile->has_CICP) { ++ color_space = ++ skia::CICPGetSkColorSpace(icc_profile->CICP.color_primaries, ++ icc_profile->CICP.transfer_characteristics, ++ icc_profile->CICP.matrix_coefficients, ++ icc_profile->CICP.video_full_range_flag, ++ /*prefer_srgb_trfn=*/true); ++ } else if (icc_profile->has_toXYZD50) { ++ // The transfer function is irrelevant for gain map tone mapping, ++ // set it to something standard in case it's not set or not ++ // supported. ++ skcms_ICCProfile with_srgb = *icc_profile; ++ skcms_SetTransferFunction(&with_srgb, skcms_sRGB_TransferFunction()); ++ color_space = SkColorSpace::Make(with_srgb); ++ } ++ } else if (gain_map->altColorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED) { ++ if (image.icc.size == 0 && ++ image.colorPrimaries == gain_map->altColorPrimaries) { ++ // Same as base image, no need to specify it. ++ return nullptr; ++ } ++ const bool grayscale = (gain_map->altPlaneCount == 1); ++ const gfx::ColorSpace alt_color_space = GetColorSpace( ++ gain_map->altColorPrimaries, gain_map->altTransferCharacteristics, ++ gain_map->altMatrixCoefficients, gain_map->altYUVRange, grayscale); ++ color_space = alt_color_space.GetAsFullRangeRGB().ToSkColorSpace(); ++ } ++ ++ if (!color_space) { ++ DVLOG(1) << "Gain map image contains an unsupported color space"; ++ } ++ ++ return color_space; ++} ++ ++} // namespace ++ ++AVIFImageDecoder::AVIFImageDecoder(AlphaOption alpha_option, ++ HighBitDepthDecodingOption hbd_option, ++ ColorBehavior color_behavior, ++ cc::AuxImage aux_image, ++ wtf_size_t max_decoded_bytes, ++ AnimationOption animation_option) ++ : ImageDecoder(alpha_option, ++ hbd_option, ++ color_behavior, ++ aux_image, ++ max_decoded_bytes), ++ animation_option_(animation_option) {} ++ ++AVIFImageDecoder::~AVIFImageDecoder() = default; ++ ++String AVIFImageDecoder::FilenameExtension() const { ++ return "avif"; ++} ++ ++const AtomicString& AVIFImageDecoder::MimeType() const { ++ DEFINE_STATIC_LOCAL(const AtomicString, avif_mime_type, ("image/avif")); ++ return avif_mime_type; ++} ++ ++bool AVIFImageDecoder::ImageIsHighBitDepth() { ++ return bit_depth_ > 8; ++} ++ ++void AVIFImageDecoder::OnSetData(scoped_refptr data) { ++ have_parsed_current_data_ = false; ++ const bool all_data_received = IsAllDataReceived(); ++ avif_io_data_.reader = data_; ++ avif_io_data_.all_data_received = all_data_received; ++ avif_io_.sizeHint = all_data_received ? data_->size() : kMaxAvifFileSize; ++ ++ // ImageFrameGenerator::GetYUVAInfo() and ImageFrameGenerator::DecodeToYUV() ++ // assume that allow_decode_to_yuv_ and other image metadata are available ++ // after calling ImageDecoder::Create() with data_complete=true. ++ if (all_data_received) { ++ ParseMetadata(); ++ } ++} ++ ++cc::YUVSubsampling AVIFImageDecoder::GetYUVSubsampling() const { ++ switch (avif_yuv_format_) { ++ case AVIF_PIXEL_FORMAT_YUV420: ++ return cc::YUVSubsampling::k420; ++ case AVIF_PIXEL_FORMAT_YUV422: ++ return cc::YUVSubsampling::k422; ++ case AVIF_PIXEL_FORMAT_YUV444: ++ return cc::YUVSubsampling::k444; ++ case AVIF_PIXEL_FORMAT_YUV400: ++ return cc::YUVSubsampling::kUnknown; ++ case AVIF_PIXEL_FORMAT_NONE: ++ // avif_yuv_format_ is initialized to AVIF_PIXEL_FORMAT_NONE in the ++ // constructor. If we have called SetSize() successfully at the end ++ // of UpdateDemuxer(), avif_yuv_format_ cannot possibly be ++ // AVIF_PIXEL_FORMAT_NONE. ++ CHECK(!IsDecodedSizeAvailable()); ++ return cc::YUVSubsampling::kUnknown; ++ default: ++ break; ++ } ++ NOTREACHED() << "Invalid YUV format: " << avif_yuv_format_; ++} ++ ++gfx::Size AVIFImageDecoder::DecodedYUVSize(cc::YUVIndex index) const { ++ DCHECK(IsDecodedSizeAvailable()); ++ if (index == cc::YUVIndex::kU || index == cc::YUVIndex::kV) { ++ return gfx::Size(UVSize(Size().width(), chroma_shift_x_), ++ UVSize(Size().height(), chroma_shift_y_)); ++ } ++ return Size(); ++} ++ ++wtf_size_t AVIFImageDecoder::DecodedYUVWidthBytes(cc::YUVIndex index) const { ++ DCHECK(IsDecodedSizeAvailable()); ++ // Try to return the same width bytes as used by the dav1d library. This will ++ // allow DecodeToYUV() to copy each plane with a single memcpy() call. ++ // ++ // The comments for Dav1dPicAllocator in dav1d/picture.h require the pixel ++ // width be padded to a multiple of 128 pixels. ++ wtf_size_t aligned_width = static_cast( ++ base::bits::AlignUpDeprecatedDoNotUse(Size().width(), 128)); ++ if (index == cc::YUVIndex::kU || index == cc::YUVIndex::kV) { ++ aligned_width >>= chroma_shift_x_; ++ } ++ // When the stride is a multiple of 1024, dav1d_default_picture_alloc() ++ // slightly pads the stride to avoid a reduction in cache hit rate in most ++ // L1/L2 cache implementations. Match that trick here. (Note that this padding ++ // is not documented in dav1d/picture.h.) ++ if ((aligned_width & 1023) == 0) { ++ aligned_width += 64; ++ } ++ ++ // High bit depth YUV is stored as a uint16_t, double the number of bytes. ++ if (bit_depth_ > 8) { ++ DCHECK_LE(bit_depth_, 16); ++ aligned_width *= 2; ++ } ++ ++ return aligned_width; ++} ++ ++SkYUVColorSpace AVIFImageDecoder::GetYUVColorSpace() const { ++ DCHECK(CanDecodeToYUV()); ++ DCHECK_NE(yuv_color_space_, SkYUVColorSpace::kIdentity_SkYUVColorSpace); ++ return yuv_color_space_; ++} ++ ++uint8_t AVIFImageDecoder::GetYUVBitDepth() const { ++ DCHECK(CanDecodeToYUV()); ++ return bit_depth_; ++} ++ ++std::optional AVIFImageDecoder::GetHDRMetadata() const { ++ return hdr_metadata_; ++} ++ ++void AVIFImageDecoder::DecodeToYUV() { ++ DCHECK(image_planes_); ++ DCHECK(CanDecodeToYUV()); ++ ++ if (Failed()) { ++ return; ++ } ++ ++ DCHECK(decoder_); ++ DCHECK_EQ(decoded_frame_count_, 1u); // Not animation. ++ ++ // If the image is decoded progressively, just render the highest progressive ++ // frame in image_planes_ because the callers of DecodeToYUV() assume that a ++ // complete scan will not be updated. ++ const int frame_index = progressive_ ? (decoder_->imageCount - 1) : 0; ++ // TODO(crbug.com/943519): Implement YUV incremental decoding as in Decode(). ++ decoder_->allowIncremental = AVIF_FALSE; ++ ++ // libavif cannot decode to an external buffer. So we need to copy from ++ // libavif's internal buffer to |image_planes_|. ++ // TODO(crbug.com/1099825): Enhance libavif to decode to an external buffer. ++ auto ret = DecodeImage(frame_index); ++ if (ret != AVIF_RESULT_OK) { ++ if (ret != AVIF_RESULT_WAITING_ON_IO) { ++ SetFailed(); ++ } ++ return; ++ } ++ const avifImage* image = decoded_image_; ++ ++ DCHECK(!image->alphaPlane); ++ static_assert(cc::YUVIndex::kY == static_cast(AVIF_CHAN_Y), ""); ++ static_assert(cc::YUVIndex::kU == static_cast(AVIF_CHAN_U), ""); ++ static_assert(cc::YUVIndex::kV == static_cast(AVIF_CHAN_V), ""); ++ ++ // Disable subnormal floats which can occur when converting to half float. ++ std::unique_ptr disable_subnormals; ++ const bool is_f16 = image_planes_->color_type() == kA16_float_SkColorType; ++ if (is_f16) { ++ disable_subnormals = std::make_unique(); ++ } ++ const float kHighBitDepthMultiplier = ++ (is_f16 ? 1.0f : 65535.0f) / ((1 << bit_depth_) - 1); ++ ++ // Initialize |width| and |height| to the width and height of the luma plane. ++ uint32_t width = image->width; ++ uint32_t height = image->height; ++ ++ for (wtf_size_t plane_index = 0; plane_index < cc::kNumYUVPlanes; ++ ++plane_index) { ++ const cc::YUVIndex plane = static_cast(plane_index); ++ const wtf_size_t src_row_bytes = ++ base::strict_cast(image->yuvRowBytes[plane_index]); ++ const wtf_size_t dst_row_bytes = image_planes_->RowBytes(plane); ++ ++ if (bit_depth_ == 8) { ++ DCHECK_EQ(image_planes_->color_type(), kGray_8_SkColorType); ++ const uint8_t* src = image->yuvPlanes[plane_index]; ++ uint8_t* dst = static_cast(image_planes_->Plane(plane)); ++ libyuv::CopyPlane(src, src_row_bytes, dst, dst_row_bytes, width, height); ++ } else { ++ DCHECK_GT(bit_depth_, 8u); ++ DCHECK_LE(bit_depth_, 16u); ++ const uint16_t* src = ++ reinterpret_cast(image->yuvPlanes[plane_index]); ++ uint16_t* dst = static_cast(image_planes_->Plane(plane)); ++ if (image_planes_->color_type() == kA16_unorm_SkColorType) { ++ const wtf_size_t src_stride = src_row_bytes / 2; ++ const wtf_size_t dst_stride = dst_row_bytes / 2; ++ for (uint32_t j = 0; j < height; ++j) { ++ for (uint32_t i = 0; i < width; ++i) { ++ dst[j * dst_stride + i] = ++ src[j * src_stride + i] * kHighBitDepthMultiplier + 0.5f; ++ } ++ } ++ } else if (image_planes_->color_type() == kA16_float_SkColorType) { ++ // Note: Unlike CopyPlane_16, HalfFloatPlane wants the stride in bytes. ++ libyuv::HalfFloatPlane(src, src_row_bytes, dst, dst_row_bytes, ++ kHighBitDepthMultiplier, width, height); ++ } else { ++ NOTREACHED() << "Unsupported color type: " ++ << static_cast(image_planes_->color_type()); ++ } ++ } ++ if (plane == cc::YUVIndex::kY) { ++ // Having processed the luma plane, change |width| and |height| to the ++ // width and height of the chroma planes. ++ width = UVSize(width, chroma_shift_x_); ++ height = UVSize(height, chroma_shift_y_); ++ } ++ } ++ image_planes_->SetHasCompleteScan(); ++} ++ ++int AVIFImageDecoder::RepetitionCount() const { ++ if (decoded_frame_count_ > 1) { ++ switch (decoder_->repetitionCount) { ++ case AVIF_REPETITION_COUNT_INFINITE: ++ return kAnimationLoopInfinite; ++ case AVIF_REPETITION_COUNT_UNKNOWN: ++ // The AVIF file does not have repetitions specified using an EditList ++ // box. Loop infinitely for backward compatibility with older versions ++ // of Chrome. ++ return kAnimationLoopInfinite; ++ default: ++ return decoder_->repetitionCount; ++ } ++ } ++ return kAnimationNone; ++} ++ ++bool AVIFImageDecoder::FrameIsReceivedAtIndex(wtf_size_t index) const { ++ if (!IsDecodedSizeAvailable()) { ++ return false; ++ } ++ if (decoded_frame_count_ == 1) { ++ return ImageDecoder::FrameIsReceivedAtIndex(index); ++ } ++ if (index >= frame_buffer_cache_.size()) { ++ return false; ++ } ++ if (IsAllDataReceived()) { ++ return true; ++ } ++ avifExtent data_extent; ++ if (avifDecoderNthImageMaxExtent(decoder_.get(), index, &data_extent) != ++ AVIF_RESULT_OK) { ++ return false; ++ } ++ return data_extent.size == 0 || ++ data_extent.offset + data_extent.size <= data_->size(); ++} ++ ++std::optional AVIFImageDecoder::FrameTimestampAtIndex( ++ wtf_size_t index) const { ++ return index < frame_buffer_cache_.size() ++ ? frame_buffer_cache_[index].Timestamp() ++ : std::nullopt; ++} ++ ++base::TimeDelta AVIFImageDecoder::FrameDurationAtIndex(wtf_size_t index) const { ++ return index < frame_buffer_cache_.size() ++ ? frame_buffer_cache_[index].Duration() ++ : base::TimeDelta(); ++} ++ ++bool AVIFImageDecoder::ImageHasBothStillAndAnimatedSubImages() const { ++ // Per MIAF, all animated AVIF files must have a still image, even if it's ++ // just a pointer to the first frame of the animation. ++ return decoder_ && decoder_->imageSequenceTrackPresent; ++} ++ ++// static ++bool AVIFImageDecoder::MatchesAVIFSignature( ++ const FastSharedBufferReader& fast_reader) { ++ // avifPeekCompatibleFileType() clamps compatible brands at 32 when reading in ++ // the ftyp box in ISO BMFF for the 'avif' or 'avis' brand. So the maximum ++ // number of bytes read is 144 bytes (size 4 bytes, type 4 bytes, major brand ++ // 4 bytes, minor version 4 bytes, and 4 bytes * 32 compatible brands). ++ char buffer[144]; ++ avifROData input; ++ input.size = std::min(sizeof(buffer), fast_reader.size()); ++ input.data = reinterpret_cast( ++ fast_reader.GetConsecutiveData(0, input.size, buffer)); ++ return avifPeekCompatibleFileType(&input); ++} ++ ++gfx::ColorSpace AVIFImageDecoder::GetColorSpaceForTesting() const { ++ const auto* image = GetDecoderImage(); ++ CHECK(image); ++ return GetColorSpace(image); ++} ++ ++void AVIFImageDecoder::ParseMetadata() { ++ if (!UpdateDemuxer()) { ++ SetFailed(); ++ } ++} ++ ++void AVIFImageDecoder::DecodeSize() { ++ ParseMetadata(); ++} ++ ++wtf_size_t AVIFImageDecoder::DecodeFrameCount() { ++ if (!Failed()) { ++ ParseMetadata(); ++ } ++ return IsDecodedSizeAvailable() ? decoded_frame_count_ ++ : frame_buffer_cache_.size(); ++} ++ ++void AVIFImageDecoder::InitializeNewFrame(wtf_size_t index) { ++ auto& buffer = frame_buffer_cache_[index]; ++ if (decode_to_half_float_) { ++ buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16); ++ } ++ ++ // For AVIFs, the frame always fills the entire image. ++ buffer.SetOriginalFrameRect(gfx::Rect(Size())); ++ ++ avifImageTiming timing; ++ auto ret = avifDecoderNthImageTiming(decoder_.get(), index, &timing); ++ DCHECK_EQ(ret, AVIF_RESULT_OK); ++ buffer.SetTimestamp(base::Seconds(timing.pts)); ++ buffer.SetDuration(base::Seconds(timing.duration)); ++} ++ ++void AVIFImageDecoder::Decode(wtf_size_t index) { ++ if (Failed()) { ++ return; ++ } ++ ++ UpdateAggressivePurging(index); ++ ++ int frame_index = index; ++ // If the image is decoded progressively, find the highest progressive ++ // frame that we have received and decode from that frame index. Internally ++ // decoder_ still decodes the lower progressive frames, but they are only used ++ // as reference frames and not rendered. ++ if (progressive_) { ++ DCHECK_EQ(index, 0u); ++ // decoder_->imageIndex is the current image index. decoder_->imageIndex is ++ // initialized to -1. decoder_->imageIndex + 1 is the next image index. ++ DCHECK_LT(decoder_->imageIndex + 1, decoder_->imageCount); ++ for (frame_index = decoder_->imageIndex + 1; ++ frame_index + 1 < decoder_->imageCount; ++frame_index) { ++ avifExtent data_extent; ++ auto rv = avifDecoderNthImageMaxExtent(decoder_.get(), frame_index + 1, ++ &data_extent); ++ if (rv != AVIF_RESULT_OK) { ++ DVLOG(1) << "avifDecoderNthImageMaxExtent(" << frame_index + 1 ++ << ") failed: " << avifResultToString(rv) << ": " ++ << AvifDecoderErrorMessage(decoder_.get()); ++ SetFailed(); ++ return; ++ } ++ if (data_extent.size != 0 && ++ data_extent.offset + data_extent.size > data_->size()) { ++ break; ++ } ++ } ++ } ++ ++ // Allow AVIF frames to be partially decoded before all data is received. ++ // Only enabled for non-progressive still images because animations look ++ // better without incremental decoding and because progressive decoding makes ++ // incremental decoding unnecessary. ++ decoder_->allowIncremental = (decoder_->imageCount == 1); ++ ++ auto ret = DecodeImage(frame_index); ++ if (ret != AVIF_RESULT_OK && ret != AVIF_RESULT_WAITING_ON_IO) { ++ SetFailed(); ++ return; ++ } ++ const avifImage* image = decoded_image_; ++ ++ // ImageDecoder::SizeCalculationMayOverflow(), called by UpdateDemuxer() ++ // before being here, made sure the image height fits in an int. ++ int displayable_height = ++ static_cast(avifDecoderDecodedRowCount(decoder_.get())); ++ if (image == cropped_image_.get()) { ++ displayable_height -= clap_origin_.y(); ++ displayable_height = ++ std::clamp(displayable_height, 0, static_cast(image->height)); ++ } ++ ++ if (displayable_height == 0) { ++ return; // There is nothing to display. ++ } ++ ++ ImageFrame& buffer = frame_buffer_cache_[index]; ++ DCHECK_NE(buffer.GetStatus(), ImageFrame::kFrameComplete); ++ ++ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) { ++ if (!InitFrameBuffer(index)) { ++ DVLOG(1) << "Failed to create frame buffer..."; ++ SetFailed(); ++ return; ++ } ++ DCHECK_EQ(buffer.GetStatus(), ImageFrame::kFramePartial); ++ // The buffer is transparent outside the decoded area while the image is ++ // loading. The correct alpha value for the frame will be set when it is ++ // fully decoded. ++ buffer.SetHasAlpha(true); ++ if (decoder_->allowIncremental) { ++ // In case of buffer disposal after decoding. ++ incrementally_displayed_height_ = 0; ++ } ++ } ++ ++ const int last_displayed_height = ++ decoder_->allowIncremental ? incrementally_displayed_height_ : 0; ++ if (displayable_height == last_displayed_height) { ++ return; // There is no new row to display. ++ } ++ DCHECK_GT(displayable_height, last_displayed_height); ++ ++ // Only render the newly decoded rows. ++ if (!RenderImage(image, last_displayed_height, &displayable_height, ++ &buffer)) { ++ SetFailed(); ++ return; ++ } ++ if (displayable_height == last_displayed_height) { ++ return; // There is no new row to display. ++ } ++ DCHECK_GT(displayable_height, last_displayed_height); ++ ColorCorrectImage(last_displayed_height, displayable_height, &buffer); ++ buffer.SetPixelsChanged(true); ++ if (decoder_->allowIncremental) { ++ incrementally_displayed_height_ = displayable_height; ++ } ++ ++ if (static_cast(displayable_height) == image->height && ++ (!progressive_ || frame_index + 1 == decoder_->imageCount)) { ++ buffer.SetHasAlpha(!!image->alphaPlane); ++ buffer.SetStatus(ImageFrame::kFrameComplete); ++ PostDecodeProcessing(index); ++ } ++} ++ ++bool AVIFImageDecoder::CanReusePreviousFrameBuffer(wtf_size_t index) const { ++ // (a) Technically we can reuse the bitmap of the previous frame because the ++ // AVIF decoder handles frame dependence internally and we never need to ++ // preserve previous frames to decode later ones, and (b) since this function ++ // will not currently be called, this is really more for the reader than any ++ // functional purpose. ++ return true; ++} ++ ++// static ++avifResult AVIFImageDecoder::ReadFromSegmentReader(avifIO* io, ++ uint32_t read_flags, ++ uint64_t offset, ++ size_t size, ++ avifROData* out) { ++ if (read_flags != 0) { ++ // Unsupported read_flags ++ return AVIF_RESULT_IO_ERROR; ++ } ++ ++ AvifIOData* io_data = static_cast(io->data); ++ ++ // Sanitize/clamp incoming request ++ if (offset > io_data->reader->size()) { ++ // The offset is past the end of the buffer or available data. ++ return io_data->all_data_received ? AVIF_RESULT_IO_ERROR ++ : AVIF_RESULT_WAITING_ON_IO; ++ } ++ ++ // It is more convenient to work with a variable of the size_t type. Since ++ // offset <= io_data->reader->size() <= SIZE_MAX, this cast is safe. ++ size_t position = static_cast(offset); ++ const size_t available_size = io_data->reader->size() - position; ++ if (size > available_size) { ++ if (!io_data->all_data_received) { ++ return AVIF_RESULT_WAITING_ON_IO; ++ } ++ size = available_size; ++ } ++ ++ out->size = size; ++ ++ base::span data = io_data->reader->GetSomeData(position); ++ if (data.size() >= size) { ++ out->data = data.data(); ++ return AVIF_RESULT_OK; ++ } ++ ++ io_data->buffer.clear(); ++ io_data->buffer.reserve(size); ++ ++ while (size != 0) { ++ data = io_data->reader->GetSomeData(position); ++ size_t copy_size = std::min(data.size(), size); ++ io_data->buffer.insert(io_data->buffer.end(), data.begin(), data.end()); ++ position += copy_size; ++ size -= copy_size; ++ } ++ ++ out->data = io_data->buffer.data(); ++ return AVIF_RESULT_OK; ++} ++ ++bool AVIFImageDecoder::UpdateDemuxer() { ++ DCHECK(!Failed()); ++ if (IsDecodedSizeAvailable()) { ++ return true; ++ } ++ ++ if (have_parsed_current_data_) { ++ return true; ++ } ++ have_parsed_current_data_ = true; ++ ++ if (!decoder_) { ++ decoder_.reset(avifDecoderCreate()); ++ if (!decoder_) { ++ return false; ++ } ++ ++ // For simplicity, use a hardcoded maxThreads of 2, independent of the image ++ // size and processor count. Note: even if we want maxThreads to depend on ++ // the image size, it is impossible to do so because maxThreads is passed to ++ // dav1d_open() inside avifDecoderParse(), but the image size is not known ++ // until avifDecoderParse() returns successfully. See ++ // https://github.com/AOMediaCodec/libavif/issues/636. ++ decoder_->maxThreads = 2; ++ ++ if (animation_option_ != AnimationOption::kUnspecified && ++ avifDecoderSetSource( ++ decoder_.get(), ++ animation_option_ == AnimationOption::kPreferAnimation ++ ? AVIF_DECODER_SOURCE_TRACKS ++ : AVIF_DECODER_SOURCE_PRIMARY_ITEM) != AVIF_RESULT_OK) { ++ return false; ++ } ++ ++ // Chrome doesn't use XMP and Exif metadata. Ignoring XMP and Exif will ++ // ensure avifDecoderParse() isn't waiting for some tiny Exif payload hiding ++ // at the end of a file. ++ decoder_->ignoreXMP = AVIF_TRUE; ++ decoder_->ignoreExif = AVIF_TRUE; ++ ++ // Turn off libavif's 'clap' (clean aperture) property validation. We ++ // validate 'clap' ourselves and ignore invalid 'clap' properties. ++ decoder_->strictFlags &= ~AVIF_STRICT_CLAP_VALID; ++ // Allow the PixelInformationProperty ('pixi') to be missing in AV1 image ++ // items. libheif v1.11.0 or older does not add the 'pixi' item property to ++ // AV1 image items. (This issue has been corrected in libheif v1.12.0.) See ++ // crbug.com/1198455. ++ decoder_->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; ++ ++ if (base::FeatureList::IsEnabled(features::kAvifGainmapHdrImages) && ++ aux_image_ == cc::AuxImage::kGainmap) { ++ decoder_->imageContentToDecode = AVIF_IMAGE_CONTENT_GAIN_MAP; ++ } ++ ++ avif_io_.destroy = nullptr; ++ avif_io_.read = ReadFromSegmentReader; ++ avif_io_.write = nullptr; ++ avif_io_.persistent = AVIF_FALSE; ++ avif_io_.data = &avif_io_data_; ++ avifDecoderSetIO(decoder_.get(), &avif_io_); ++ } ++ ++ // If all data is received, there is no point in decoding progressively. ++ decoder_->allowProgressive = !IsAllDataReceived(); ++ ++ auto ret = avifDecoderParse(decoder_.get()); ++ if (ret == AVIF_RESULT_WAITING_ON_IO) { ++ return true; ++ } ++ if (ret != AVIF_RESULT_OK) { ++ DVLOG(1) << "avifDecoderParse failed: " << avifResultToString(ret) << ". " ++ << decoder_->diag.error; ++ return false; ++ } ++ ++ // Image metadata is available in decoder_->image after avifDecoderParse() ++ // even though decoder_->imageIndex is invalid (-1). ++ DCHECK_EQ(decoder_->imageIndex, -1); ++ // This variable is named |container| to emphasize the fact that the current ++ // contents of decoder_->image come from the container, not any frame. ++ const auto* container = GetDecoderImage(); ++ if (!container) { ++ return false; ++ } ++ ++ // The container width and container height are read from either the tkhd ++ // (track header) box of a track or the ispe (image spatial extents) property ++ // of an image item, both of which are mandatory in the spec. ++ if (container->width == 0 || container->height == 0) { ++ DVLOG(1) << "Container width and height must be present"; ++ return false; ++ } ++ ++ // The container depth is read from either the av1C box of a track or the av1C ++ // property of an image item, both of which are mandatory in the spec. ++ if (container->depth == 0) { ++ DVLOG(1) << "Container depth must be present"; ++ return false; ++ } ++ ++ DCHECK_GT(decoder_->imageCount, 0); ++ progressive_ = decoder_->progressiveState == AVIF_PROGRESSIVE_STATE_ACTIVE; ++ // If the image is progressive, decoder_->imageCount is the number of ++ // progressive frames, but there is only one still image. ++ decoded_frame_count_ = progressive_ ? 1 : decoder_->imageCount; ++ container_width_ = container->width; ++ container_height_ = container->height; ++ bit_depth_ = container->depth; ++ decode_to_half_float_ = ++ ImageIsHighBitDepth() && ++ high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat; ++ ++ // Verify that AVIF_PIXEL_FORMAT_{YUV444,YUV422,YUV420,YUV400} are ++ // consecutive. ++ static_assert(AVIF_PIXEL_FORMAT_YUV422 == AVIF_PIXEL_FORMAT_YUV444 + 1); ++ static_assert(AVIF_PIXEL_FORMAT_YUV420 == AVIF_PIXEL_FORMAT_YUV422 + 1); ++ static_assert(AVIF_PIXEL_FORMAT_YUV400 == AVIF_PIXEL_FORMAT_YUV420 + 1); ++ // Assert that after avifDecoderParse() returns AVIF_RESULT_OK, ++ // decoder_->image->yuvFormat (the same as container->yuvFormat) is one of the ++ // four YUV formats in AV1. ++ CHECK(container->yuvFormat >= AVIF_PIXEL_FORMAT_YUV444 && ++ container->yuvFormat <= AVIF_PIXEL_FORMAT_YUV400) ++ << "Invalid YUV format: " << container->yuvFormat; ++ avif_yuv_format_ = container->yuvFormat; ++ avifPixelFormatInfo format_info; ++ avifGetPixelFormatInfo(container->yuvFormat, &format_info); ++ chroma_shift_x_ = format_info.chromaShiftX; ++ chroma_shift_y_ = format_info.chromaShiftY; ++ ++ if (container->clli.maxCLL || container->clli.maxPALL) { ++ hdr_metadata_ = gfx::HDRMetadata(); ++ hdr_metadata_->cta_861_3 = gfx::HdrMetadataCta861_3( ++ container->clli.maxCLL, container->clli.maxPALL); ++ } ++ ++ // SetEmbeddedColorProfile() must be called before IsSizeAvailable() becomes ++ // true. So call SetEmbeddedColorProfile() before calling SetSize(). The color ++ // profile is either an ICC profile or the CICP color description. ++ ++ if (!IgnoresColorSpace()) { ++ // The CICP color description is always present because we can always get it ++ // from the AV1 sequence header for the frames. If an ICC profile is ++ // present, use it instead of the CICP color description. ++ if (container->icc.size) { ++ std::unique_ptr profile = ColorProfile::Create( ++ base::span(container->icc.data, container->icc.size)); ++ if (!profile) { ++ DVLOG(1) << "Failed to parse image ICC profile"; ++ return false; ++ } ++ uint32_t data_color_space = profile->GetProfile()->data_color_space; ++ const bool is_mono = container->yuvFormat == AVIF_PIXEL_FORMAT_YUV400; ++ if (is_mono) { ++ if (data_color_space != skcms_Signature_Gray && ++ data_color_space != skcms_Signature_RGB) { ++ profile = nullptr; ++ } ++ } else { ++ if (data_color_space != skcms_Signature_RGB) { ++ profile = nullptr; ++ } ++ } ++ if (!profile) { ++ DVLOG(1) ++ << "Image contains ICC profile that does not match its color space"; ++ return false; ++ } ++ SetEmbeddedColorProfile(std::move(profile)); ++ } else if (container->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED || ++ container->transferCharacteristics != ++ AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { ++ gfx::ColorSpace frame_cs = GetColorSpace(container); ++ ++ sk_sp sk_color_space = ++ frame_cs.GetAsFullRangeRGB().ToSkColorSpace(); ++ if (!sk_color_space) { ++ DVLOG(1) << "Image contains an unsupported color space"; ++ return false; ++ } ++ ++ skcms_ICCProfile profile; ++ sk_color_space->toProfile(&profile); ++ SetEmbeddedColorProfile(std::make_unique(profile)); ++ } ++ } ++ ++ // |angle| * 90 specifies the angle of anti-clockwise rotation in degrees. ++ // Legal values: [0-3]. ++ int angle = 0; ++ if (container->transformFlags & AVIF_TRANSFORM_IROT) { ++ angle = container->irot.angle; ++ CHECK_LT(angle, 4); ++ } ++ // |axis| specifies how the mirroring is performed. ++ // -1: No mirroring. ++ // 0: The top and bottom parts of the image are exchanged. ++ // 1: The left and right parts of the image are exchanged. ++ int axis = -1; ++ if (container->transformFlags & AVIF_TRANSFORM_IMIR) { ++ axis = container->imir.axis; ++ CHECK_LT(axis, 2); ++ } ++ // MIAF Section 7.3.6.7 (Clean aperture, rotation and mirror) says: ++ // These properties, if used, shall be indicated to be applied in the ++ // following order: clean aperture first, then rotation, then mirror. ++ // ++ // In the kAxisAngleToOrientation array, the first dimension is axis (with an ++ // offset of 1). The second dimension is angle. ++ constexpr std::array, 3> ++ kAxisAngleToOrientation = {{ ++ // No mirroring. ++ {ImageOrientationEnum::kOriginTopLeft, ++ ImageOrientationEnum::kOriginLeftBottom, ++ ImageOrientationEnum::kOriginBottomRight, ++ ImageOrientationEnum::kOriginRightTop}, ++ // Top-to-bottom mirroring. Change Top<->Bottom in the first row. ++ {ImageOrientationEnum::kOriginBottomLeft, ++ ImageOrientationEnum::kOriginLeftTop, ++ ImageOrientationEnum::kOriginTopRight, ++ ImageOrientationEnum::kOriginRightBottom}, ++ // Left-to-right mirroring. Change Left<->Right in the first row. ++ {ImageOrientationEnum::kOriginTopRight, ++ ImageOrientationEnum::kOriginRightBottom, ++ ImageOrientationEnum::kOriginBottomLeft, ++ ImageOrientationEnum::kOriginLeftTop}, ++ }}; ++ orientation_ = kAxisAngleToOrientation[axis + 1][angle]; ++ ++ // Determine whether the image can be decoded to YUV. ++ // * Alpha channel is not supported. ++ // * Multi-frame images (animations) are not supported. (The DecodeToYUV() ++ // method does not have an 'index' parameter.) ++ allow_decode_to_yuv_ = ++ avif_yuv_format_ != AVIF_PIXEL_FORMAT_YUV400 && !decoder_->alphaPresent && ++ decoded_frame_count_ == 1 && ++ GetColorSpace(container).ToSkYUVColorSpace(container->depth, ++ &yuv_color_space_) && ++ // TODO(crbug.com/911246): Support color space transforms for YUV decodes. ++ !ColorTransform(); ++ ++ // Record bpp information only for 8-bit, color, still images that do not have ++ // alpha. ++ if (container->depth == 8 && avif_yuv_format_ != AVIF_PIXEL_FORMAT_YUV400 && ++ !decoder_->alphaPresent && decoded_frame_count_ == 1) { ++ static constexpr char kType[] = "Avif"; ++ update_bpp_histogram_callback_ = base::BindOnce(&UpdateBppHistogram); ++ } ++ ++ unsigned width = container->width; ++ unsigned height = container->height; ++ // If the image is cropped, pass the size of the cropped image (the clean ++ // aperture) to SetSize(). ++ if (container->transformFlags & AVIF_TRANSFORM_CLAP) { ++ AVIFCleanApertureType clap_type; ++ avifCropRect crop_rect; ++ avifDiagnostics diag; ++ avifBool valid_clap = avifCropRectConvertCleanApertureBox( ++ &crop_rect, &container->clap, container->width, container->height, ++ container->yuvFormat, &diag); ++ if (!valid_clap) { ++ DVLOG(1) << "Invalid 'clap' property: " << diag.error ++ << "; showing the full image."; ++ clap_type = AVIFCleanApertureType::kInvalid; ++ ignore_clap_ = true; ++ } else if (crop_rect.x != 0 || crop_rect.y != 0) { ++ // To help discourage the creation of files with privacy risks, also ++ // consider 'clap' properties whose origins are not at (0, 0) as invalid. ++ // See https://github.com/AOMediaCodec/av1-avif/issues/188 and ++ // https://github.com/AOMediaCodec/av1-avif/issues/189. ++ DVLOG(1) << "Origin of 'clap' property anchored to (" << crop_rect.x ++ << ", " << crop_rect.y << "); showing the full image."; ++ clap_type = AVIFCleanApertureType::kNonzeroOrigin; ++ ignore_clap_ = true; ++ } else { ++ clap_type = AVIFCleanApertureType::kZeroOrigin; ++ clap_origin_.SetPoint(crop_rect.x, crop_rect.y); ++ width = crop_rect.width; ++ height = crop_rect.height; ++ } ++ clap_type_ = clap_type; ++ } ++ return SetSize(width, height); ++} ++ ++avifResult AVIFImageDecoder::DecodeImage(wtf_size_t index) { ++ const auto ret = avifDecoderNthImage(decoder_.get(), index); ++ // |index| should be less than what DecodeFrameCount() returns, so we should ++ // not get the AVIF_RESULT_NO_IMAGES_REMAINING error. ++ DCHECK_NE(ret, AVIF_RESULT_NO_IMAGES_REMAINING); ++ if (ret != AVIF_RESULT_OK && ret != AVIF_RESULT_WAITING_ON_IO) { ++ DVLOG(1) << "avifDecoderNthImage(" << index ++ << ") failed: " << avifResultToString(ret) << ": " ++ << AvifDecoderErrorMessage(decoder_.get()); ++ return ret; ++ } ++ ++ const auto* image = GetDecoderImage(); ++ CHECK(image); ++ // Frame size must be equal to container size. ++ if (image->width != container_width_ || image->height != container_height_) { ++ DVLOG(1) << "Frame size " << image->width << "x" << image->height ++ << " differs from container size " << container_width_ << "x" ++ << container_height_; ++ return AVIF_RESULT_UNKNOWN_ERROR; ++ } ++ // Frame bit depth must be equal to container bit depth. ++ if (image->depth != bit_depth_) { ++ DVLOG(1) << "Frame bit depth must be equal to container bit depth"; ++ return AVIF_RESULT_UNKNOWN_ERROR; ++ } ++ // Frame YUV format must be equal to container YUV format. ++ if (image->yuvFormat != avif_yuv_format_) { ++ DVLOG(1) << "Frame YUV format must be equal to container YUV format"; ++ return AVIF_RESULT_UNKNOWN_ERROR; ++ } ++ ++ decoded_image_ = image; ++ if ((image->transformFlags & AVIF_TRANSFORM_CLAP) && !ignore_clap_) { ++ CropDecodedImage(); ++ } ++ ++ if (ret == AVIF_RESULT_OK) { ++ if (IsAllDataReceived() && update_bpp_histogram_callback_) { ++ std::move(update_bpp_histogram_callback_).Run(Size(), data_->size()); ++ } ++ ++ if (clap_type_.has_value()) { ++ base::UmaHistogramEnumeration("Blink.ImageDecoders.Avif.CleanAperture", ++ clap_type_.value()); ++ clap_type_.reset(); ++ } ++ } ++ return ret; ++} ++ ++void AVIFImageDecoder::CropDecodedImage() { ++ DCHECK_NE(decoded_image_, cropped_image_.get()); ++ if (!cropped_image_) { ++ cropped_image_.reset(avifImageCreateEmpty()); ++ } ++ avifCropRect rect; ++ rect.x = clap_origin_.x(); ++ rect.y = clap_origin_.y(); ++ rect.width = Size().width(); ++ rect.height = Size().height(); ++ const avifResult result = ++ avifImageSetViewRect(cropped_image_.get(), decoded_image_, &rect); ++ CHECK_EQ(result, AVIF_RESULT_OK); ++ decoded_image_ = cropped_image_.get(); ++} ++ ++bool AVIFImageDecoder::RenderImage(const avifImage* image, ++ int from_row, ++ int* to_row, ++ ImageFrame* buffer) { ++ DCHECK_LT(from_row, *to_row); ++ ++ // libavif uses libyuv for the YUV 4:2:0 to RGB upsampling and/or conversion ++ // as follows: ++ // - convert the top RGB row 0, ++ // - convert the RGB rows 1 and 2, then RGB rows 3 and 4 etc., ++ // - convert the bottom (odd) RGB row if there is an even number of RGB rows. ++ // ++ // Unfortunately this cannot be applied incrementally as is. The RGB values ++ // would differ because the first and last RGB rows have a formula using only ++ // one UV row, while the other RGB rows use two UV rows as input each. ++ // See https://crbug.com/libyuv/934. ++ // ++ // The workaround is a backup of the last converted even RGB row, called top ++ // row, located right before |from_row|. The conversion is then called ++ // starting at this top row, overwriting it with invalid values. The remaining ++ // pairs of rows are correctly aligned and their freshly converted values are ++ // valid. Then the backed up row is put back, fixing the issue. ++ // The bottom row is postponed if the other half of the pair it belongs to is ++ // not yet decoded. ++ // ++ // UV rows | Y/RGB rows ++ // | all | first decoding | second decoding ++ // ____ 0 ____ 0 (from_row) ++ // 0 ---- ____ 1 ____ 1 ++ // ____ 2 ____ 2 ____ 2 (backed up) ++ // 1 ---- ____ 3 ____ 3 (postponed) ____ 3 (from_row) ++ // ____ 4 4 (*to_row) ____ 4 ++ // 2 ---- ____ 5 ____ 5 ++ // 6 (*to_row) ++ ++ const bool use_libyuv_bilinear_upsampling = ++ !decode_to_half_float_ && image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420; ++ const bool save_top_row = use_libyuv_bilinear_upsampling && from_row > 0; ++ const bool postpone_bottom_row = ++ use_libyuv_bilinear_upsampling && ++ static_cast(*to_row) < image->height; ++ if (postpone_bottom_row) { ++ // libavif outputs an even number of rows because 4:2:0 samples are decoded ++ // in pairs. ++ DCHECK(!(*to_row & 1)); ++ --*to_row; ++ if (from_row == *to_row) { ++ return true; // Nothing to do. ++ } ++ } ++ if (save_top_row) { ++ // |from_row| is odd because it is equal to the output value of |*to_row| ++ // from the previous RenderImage() call, and |*to_row| was even and then ++ // decremented at that time. ++ DCHECK(from_row & 1); ++ --from_row; ++ } ++ ++ // Focus |image| on rows [from_row, *to_row). ++ std::unique_ptr view( ++ nullptr, avifImageDestroy); ++ if (from_row > 0 || static_cast(*to_row) < image->height) { ++ const avifCropRect rect = {0, static_cast(from_row), image->width, ++ static_cast(*to_row - from_row)}; ++ view.reset(avifImageCreateEmpty()); ++ const avifResult result = avifImageSetViewRect(view.get(), image, &rect); ++ CHECK_EQ(result, AVIF_RESULT_OK); ++ image = view.get(); ++ } ++ ++ avifRGBImage rgb_image; ++ avifRGBImageSetDefaults(&rgb_image, image); ++ ++ if (decode_to_half_float_) { ++ rgb_image.depth = 16; ++ rgb_image.isFloat = AVIF_TRUE; ++ rgb_image.pixels = ++ reinterpret_cast(buffer->GetAddrF16(0, from_row)); ++ rgb_image.rowBytes = image->width * sizeof(uint64_t); ++ // When decoding to half float, the pixel ordering is always RGBA on all ++ // platforms. ++ rgb_image.format = AVIF_RGB_FORMAT_RGBA; ++ } else { ++ rgb_image.depth = 8; ++ rgb_image.pixels = reinterpret_cast(buffer->GetAddr(0, from_row)); ++ rgb_image.rowBytes = image->width * sizeof(uint32_t); ++ // When decoding to 8-bit, Android uses little-endian RGBA pixels. All other ++ // platforms use BGRA pixels. ++ static_assert(SK_B32_SHIFT == 16 - SK_R32_SHIFT); ++ static_assert(SK_G32_SHIFT == 8); ++ static_assert(SK_A32_SHIFT == 24); ++#if SK_B32_SHIFT ++ rgb_image.format = AVIF_RGB_FORMAT_RGBA; ++#else ++ rgb_image.format = AVIF_RGB_FORMAT_BGRA; ++#endif ++ } ++ rgb_image.alphaPremultiplied = buffer->PremultiplyAlpha(); ++ rgb_image.maxThreads = decoder_->maxThreads; ++ ++ if (save_top_row) { ++ previous_last_decoded_row_.resize(rgb_image.rowBytes); ++ memcpy(previous_last_decoded_row_.data(), rgb_image.pixels, ++ rgb_image.rowBytes); ++ } ++ const avifResult result = avifImageYUVToRGB(image, &rgb_image); ++ if (save_top_row) { ++ memcpy(rgb_image.pixels, previous_last_decoded_row_.data(), ++ rgb_image.rowBytes); ++ } ++ return result == AVIF_RESULT_OK; ++} ++ ++void AVIFImageDecoder::ColorCorrectImage(int from_row, ++ int to_row, ++ ImageFrame* buffer) { ++ // Postprocess the image data according to the profile. ++ const ColorProfileTransform* const transform = ColorTransform(); ++ if (!transform) { ++ return; ++ } ++ const auto alpha_format = (buffer->HasAlpha() && buffer->PremultiplyAlpha()) ++ ? skcms_AlphaFormat_PremulAsEncoded ++ : skcms_AlphaFormat_Unpremul; ++ if (decode_to_half_float_) { ++ const skcms_PixelFormat color_format = skcms_PixelFormat_RGBA_hhhh; ++ for (int y = from_row; y < to_row; ++y) { ++ ImageFrame::PixelDataF16* const row = buffer->GetAddrF16(0, y); ++ const bool success = skcms_Transform( ++ row, color_format, alpha_format, transform->SrcProfile(), row, ++ color_format, alpha_format, transform->DstProfile(), Size().width()); ++ DCHECK(success); ++ } ++ } else { ++ const skcms_PixelFormat color_format = XformColorFormat(); ++ for (int y = from_row; y < to_row; ++y) { ++ ImageFrame::PixelData* const row = buffer->GetAddr(0, y); ++ const bool success = skcms_Transform( ++ row, color_format, alpha_format, transform->SrcProfile(), row, ++ color_format, alpha_format, transform->DstProfile(), Size().width()); ++ DCHECK(success); ++ } ++ } ++} ++ ++bool AVIFImageDecoder::GetGainmapInfoAndData( ++ SkGainmapInfo& out_gainmap_info, ++ scoped_refptr& out_gainmap_data) const { ++ if (!base::FeatureList::IsEnabled(features::kAvifGainmapHdrImages)) { ++ return false; ++ } ++ // Ensure that parsing succeeded. ++ if (!IsDecodedSizeAvailable()) { ++ return false; ++ } ++ if (!decoder_->image->gainMap) { ++ return false; ++ } ++ const avifGainMap& gain_map = *decoder_->image->gainMap; ++ if (gain_map.baseHdrHeadroom.d == 0 || gain_map.alternateHdrHeadroom.d == 0) { ++ DVLOG(1) << "Invalid gainmap metadata: a denominator value is zero"; ++ return false; ++ } ++ const float base_headroom = std::exp2( ++ FractionToFloat(gain_map.baseHdrHeadroom.n, gain_map.baseHdrHeadroom.d)); ++ const float alternate_headroom = std::exp2(FractionToFloat( ++ gain_map.alternateHdrHeadroom.n, gain_map.alternateHdrHeadroom.d)); ++ const bool base_is_hdr = base_headroom > alternate_headroom; ++ out_gainmap_info.fDisplayRatioSdr = ++ base_is_hdr ? alternate_headroom : base_headroom; ++ out_gainmap_info.fDisplayRatioHdr = ++ base_is_hdr ? base_headroom : alternate_headroom; ++ out_gainmap_info.fBaseImageType = base_is_hdr ++ ? SkGainmapInfo::BaseImageType::kHDR ++ : SkGainmapInfo::BaseImageType::kSDR; ++ if (!gain_map.useBaseColorSpace) { ++ // Try to use the alternate image's color space. ++ out_gainmap_info.fGainmapMathColorSpace = ++ GetAltImageColorSpace(*decoder_->image); ++ } ++ for (int i = 0; i < 3; ++i) { ++ if (gain_map.gainMapMin[i].d == 0 || gain_map.gainMapMax[i].d == 0 || ++ gain_map.gainMapGamma[i].d == 0 || gain_map.baseOffset[i].d == 0 || ++ gain_map.alternateOffset[i].d == 0) { ++ DVLOG(1) << "Invalid gainmap metadata: a denominator value is zero"; ++ return false; ++ } ++ if (gain_map.gainMapGamma[i].n == 0) { ++ DVLOG(1) << "Invalid gainmap metadata: gamma is zero"; ++ return false; ++ } ++ ++ const float min_log2 = ++ FractionToFloat(gain_map.gainMapMin[i].n, gain_map.gainMapMin[i].d); ++ const float max_log2 = ++ FractionToFloat(gain_map.gainMapMax[i].n, gain_map.gainMapMax[i].d); ++ out_gainmap_info.fGainmapRatioMin[i] = std::exp2(min_log2); ++ out_gainmap_info.fGainmapRatioMax[i] = std::exp2(max_log2); ++ ++ // Numerator and denominator intentionally swapped to get 1.0/gamma. ++ out_gainmap_info.fGainmapGamma[i] = ++ FractionToFloat(gain_map.gainMapGamma[i].d, gain_map.gainMapGamma[i].n); ++ const float base_offset = ++ FractionToFloat(gain_map.baseOffset[i].n, gain_map.baseOffset[i].d); ++ const float alternate_offset = FractionToFloat( ++ gain_map.alternateOffset[i].n, gain_map.alternateOffset[i].d); ++ out_gainmap_info.fEpsilonSdr[i] = ++ base_is_hdr ? alternate_offset : base_offset; ++ out_gainmap_info.fEpsilonHdr[i] = ++ base_is_hdr ? base_offset : alternate_offset; ++ } ++ out_gainmap_data = data_; ++ return true; ++} ++ ++avifImage* AVIFImageDecoder::GetDecoderImage() const { ++ if (aux_image_ == cc::AuxImage::kGainmap) { ++ if (!decoder_->image->gainMap) { ++ DVLOG(1) << "Attempted to access gain map image, but gainMap is nullptr"; ++ return nullptr; ++ } ++ return decoder_->image->gainMap->image; ++ } ++ return decoder_->image; ++} ++ ++AVIFImageDecoder::AvifIOData::AvifIOData() = default; ++AVIFImageDecoder::AvifIOData::AvifIOData( ++ scoped_refptr reader, ++ bool all_data_received) ++ : reader(std::move(reader)), all_data_received(all_data_received) {} ++AVIFImageDecoder::AvifIOData::~AvifIOData() = default; ++ ++} // namespace blink +diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h +new file mode 100644 +index 0000000000000..75247663ef158 +--- /dev/null ++++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h +@@ -0,0 +1,191 @@ ++// Copyright 2020 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ ++#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ ++ ++#include ++#include ++ ++#include "base/functional/callback.h" ++#include "third_party/blink/renderer/platform/allow_discouraged_type.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" ++#include "third_party/blink/renderer/platform/wtf/vector.h" ++#include "third_party/libavif/src/include/avif/avif.h" ++#include "third_party/skia/include/core/SkImageInfo.h" ++#include "ui/gfx/color_space.h" ++#include "ui/gfx/geometry/point.h" ++ ++namespace blink { ++ ++class FastSharedBufferReader; ++ ++class PLATFORM_EXPORT AVIFImageDecoder final : public ImageDecoder { ++ public: ++ AVIFImageDecoder(AlphaOption, ++ HighBitDepthDecodingOption, ++ ColorBehavior, ++ cc::AuxImage, ++ wtf_size_t max_decoded_bytes, ++ AnimationOption); ++ AVIFImageDecoder(const AVIFImageDecoder&) = delete; ++ AVIFImageDecoder& operator=(const AVIFImageDecoder&) = delete; ++ ~AVIFImageDecoder() override; ++ ++ // ImageDecoder: ++ String FilenameExtension() const override; ++ const AtomicString& MimeType() const override; ++ bool ImageIsHighBitDepth() override; ++ void OnSetData(scoped_refptr data) override; ++ bool GetGainmapInfoAndData( ++ SkGainmapInfo& out_gainmap_info, ++ scoped_refptr& out_gainmap_data) const override; ++ cc::YUVSubsampling GetYUVSubsampling() const override; ++ gfx::Size DecodedYUVSize(cc::YUVIndex) const override; ++ wtf_size_t DecodedYUVWidthBytes(cc::YUVIndex) const override; ++ SkYUVColorSpace GetYUVColorSpace() const override; ++ uint8_t GetYUVBitDepth() const override; ++ std::optional GetHDRMetadata() const override; ++ void DecodeToYUV() override; ++ int RepetitionCount() const override; ++ bool FrameIsReceivedAtIndex(wtf_size_t) const override; ++ std::optional FrameTimestampAtIndex( ++ wtf_size_t) const override; ++ base::TimeDelta FrameDurationAtIndex(wtf_size_t) const override; ++ bool ImageHasBothStillAndAnimatedSubImages() const override; ++ ++ // Returns true if the data in fast_reader begins with a valid FileTypeBox ++ // (ftyp) that supports the brand 'avif' or 'avis'. ++ static bool MatchesAVIFSignature(const FastSharedBufferReader& fast_reader); ++ ++ gfx::ColorSpace GetColorSpaceForTesting() const; ++ ++ private: ++ // If the AVIF image has a clean aperture ('clap') property, what kind of ++ // clean aperture it is. Values synced with 'AVIFCleanApertureType' in ++ // src/tools/metrics/histograms/enums.xml. ++ // ++ // These values are persisted to logs. Entries should not be renumbered and ++ // numeric values should never be reused. ++ enum class AVIFCleanApertureType { ++ kInvalid = 0, // The clean aperture property is invalid. ++ kNonzeroOrigin = 1, // The origin of the clean aperture is not (0, 0). ++ kZeroOrigin = 2, // The origin of the clean aperture is (0, 0). ++ kMaxValue = kZeroOrigin, ++ }; ++ ++ struct AvifIOData { ++ AvifIOData(); ++ AvifIOData(scoped_refptr reader, ++ bool all_data_received); ++ ~AvifIOData(); ++ ++ scoped_refptr reader; ++ std::vector buffer ALLOW_DISCOURAGED_TYPE("Required by libavif"); ++ bool all_data_received = false; ++ }; ++ ++ void ParseMetadata(); ++ ++ // ImageDecoder: ++ void DecodeSize() override; ++ wtf_size_t DecodeFrameCount() override; ++ void InitializeNewFrame(wtf_size_t) override; ++ void Decode(wtf_size_t) override; ++ bool CanReusePreviousFrameBuffer(wtf_size_t) const override; ++ ++ // Implements avifIOReadFunc, the |read| function in the avifIO struct. ++ static avifResult ReadFromSegmentReader(avifIO* io, ++ uint32_t read_flags, ++ uint64_t offset, ++ size_t size, ++ avifROData* out); ++ ++ // Creates |decoder_| if not yet created and decodes the size and frame count. ++ bool UpdateDemuxer(); ++ ++ // Decodes the frame at index |index| and checks if the frame's size, bit ++ // depth, and YUV format matches those reported by the container. The decoded ++ // frame is available in decoded_image_. ++ avifResult DecodeImage(wtf_size_t index); ++ ++ // Crops |decoded_image_|. ++ void CropDecodedImage(); ++ ++ // Renders the rows [from_row, *to_row) of |image| to |buffer|. Returns ++ // whether |image| was rendered successfully. On return, the in/out argument ++ // |*to_row| may be decremented in case of subsampled chroma needing more ++ // data. ++ bool RenderImage(const avifImage* image, ++ int from_row, ++ int* to_row, ++ ImageFrame* buffer); ++ ++ // Applies color profile correction to the rows [from_row, to_row) of ++ // |buffer|, if desired. ++ void ColorCorrectImage(int from_row, int to_row, ImageFrame* buffer); ++ ++ // Returns decoder_->image or decoder_->image->gainMap->image depending on ++ // aux_image_. May be nullptr if requesting the gain map image ++ // (cc::AuxImage::kGainmap) but no gain map is present. ++ avifImage* GetDecoderImage() const; ++ ++ bool have_parsed_current_data_ = false; ++ // The image width and height (before cropping, if any) from the container. ++ // ++ // Note: container_width_, container_height_, decoder_->image->width, and ++ // decoder_->image->height are the width and height of the full image. Size() ++ // returns the size of the cropped image (the clean aperture). ++ uint32_t container_width_ = 0; ++ uint32_t container_height_ = 0; ++ // The bit depth from the container. ++ uint8_t bit_depth_ = 0; ++ bool decode_to_half_float_ = false; ++ uint8_t chroma_shift_x_ = 0; ++ uint8_t chroma_shift_y_ = 0; ++ std::optional hdr_metadata_; ++ bool progressive_ = false; ++ // Number of displayed rows for a non-progressive still image. ++ int incrementally_displayed_height_ = 0; ++ // The YUV format from the container. ++ avifPixelFormat avif_yuv_format_ = AVIF_PIXEL_FORMAT_NONE; ++ wtf_size_t decoded_frame_count_ = 0; ++ SkYUVColorSpace yuv_color_space_ = SkYUVColorSpace::kIdentity_SkYUVColorSpace; ++ // Used to call UpdateBppHistogram<"Avif">() at most once to record the ++ // bits-per-pixel value of the image when the image is successfully decoded. ++ base::OnceCallback update_bpp_histogram_callback_; ++ std::optional clap_type_; ++ // Whether the 'clap' (clean aperture) property should be ignored, e.g. ++ // because the 'clap' property is invalid or unsupported. ++ bool ignore_clap_ = false; ++ // The origin (top left corner) of the clean aperture. Used only when the ++ // image has a valid 'clap' (clean aperture) property. ++ gfx::Point clap_origin_; ++ // A copy of decoder_->image with the width, height, and plane buffers ++ // adjusted to those of the clean aperture. Used only when the image has a ++ // 'clap' (clean aperture) property. ++ std::unique_ptr cropped_image_{ ++ nullptr, avifImageDestroy}; ++ // Set by a successful DecodeImage() call to either decoder_->image or ++ // cropped_image_.get() depending on whether the image has a 'clap' (clean ++ // aperture) property. ++ raw_ptr decoded_image_ = nullptr; ++ // The declaration order of the next three fields is important. decoder_ ++ // points to avif_io_, and avif_io_ points to avif_io_data_. The destructor ++ // must destroy them in that order. ++ AvifIOData avif_io_data_; ++ avifIO avif_io_ = {}; ++ std::unique_ptr decoder_{ ++ nullptr, avifDecoderDestroy}; ++ ++ const AnimationOption animation_option_; ++ ++ // Used temporarily for incremental decoding and for some YUV to RGB color ++ // conversions. ++ Vector previous_last_decoded_row_; ++}; ++ ++} // namespace blink ++ ++#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ +diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc +new file mode 100644 +index 0000000000000..f337ad075244a +--- /dev/null ++++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc +@@ -0,0 +1,30 @@ ++// Copyright 2024 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "third_party/blink/renderer/platform/graphics/color_behavior.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h" ++#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h" ++#include "third_party/blink/renderer/platform/testing/task_environment.h" ++#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" ++#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h" ++ ++namespace blink { ++ ++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ++ static BlinkFuzzerTestSupport test_support; ++ FuzzedDataProvider fdp(data, size); ++ FuzzDecoder(DecoderType::kAvifDecoder, fdp); ++ return 0; ++} ++ ++} // namespace blink +diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc +new file mode 100644 +index 0000000000000..9ec2b420bdf7d +--- /dev/null ++++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc +@@ -0,0 +1,1758 @@ ++// Copyright 2020 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifdef UNSAFE_BUFFERS_BUILD ++// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. ++#pragma allow_unsafe_buffers ++#endif ++ ++#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "base/barrier_closure.h" ++#include "base/bit_cast.h" ++#include "base/functional/bind.h" ++#include "base/strings/stringprintf.h" ++#include "base/synchronization/waitable_event.h" ++#include "base/task/thread_pool.h" ++#include "base/test/metrics/histogram_tester.h" ++#include "base/test/scoped_feature_list.h" ++#include "base/test/task_environment.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "third_party/blink/public/common/features.h" ++#include "third_party/blink/public/platform/platform.h" ++#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h" ++#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" ++#include "ui/gfx/color_space.h" ++#include "ui/gfx/color_transform.h" ++ ++#define FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM 0 ++#define FIXME_SUPPORT_ICC_PROFILE_TRANSFORM 0 ++#define FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS 0 ++ ++namespace blink { ++ ++namespace { ++ ++std::unique_ptr CreateAVIFDecoderWithOptions( ++ ImageDecoder::AlphaOption alpha_option, ++ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_option, ++ ColorBehavior color_behavior, ++ cc::AuxImage aux_image, ++ ImageDecoder::AnimationOption animation_option) { ++ return std::make_unique( ++ alpha_option, high_bit_depth_option, color_behavior, aux_image, ++ ImageDecoder::kNoDecodedImageByteLimit, animation_option); ++} ++ ++std::unique_ptr CreateAVIFDecoder() { ++ return CreateAVIFDecoderWithOptions( ++ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth, ++ ColorBehavior::kTag, cc::AuxImage::kDefault, ++ ImageDecoder::AnimationOption::kUnspecified); ++} ++ ++std::unique_ptr CreateGainMapAVIFDecoder() { ++ return CreateAVIFDecoderWithOptions( ++ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth, ++ ColorBehavior::kTag, cc::AuxImage::kGainmap, ++ ImageDecoder::AnimationOption::kUnspecified); ++} ++ ++struct ExpectedColor { ++ gfx::Point point; ++ SkColor color; ++}; ++ ++enum class ColorType { ++ kRgb, ++ kRgbA, ++ kMono, ++ kMonoA, ++}; ++ ++struct StaticColorCheckParam { ++ const char* path; ++ int bit_depth; ++ ColorType color_type; ++ ImageDecoder::CompressionFormat compression_format; ++ ImageDecoder::AlphaOption alpha_option; ++ ColorBehavior color_behavior; ++ ImageOrientationEnum orientation = ImageOrientationEnum::kDefault; ++ int color_threshold; ++ std::vector colors; ++}; ++ ++std::ostream& operator<<(std::ostream& os, const StaticColorCheckParam& param) { ++ const char* color_type; ++ switch (param.color_type) { ++ case ColorType::kRgb: ++ color_type = "kRgb"; ++ break; ++ case ColorType::kRgbA: ++ color_type = "kRgbA"; ++ break; ++ case ColorType::kMono: ++ color_type = "kMono"; ++ break; ++ case ColorType::kMonoA: ++ color_type = "kMonoA"; ++ break; ++ } ++ const char* alpha_option = ++ (param.alpha_option == ImageDecoder::kAlphaPremultiplied ++ ? "kAlphaPremultiplied" ++ : "kAlphaNotPremultiplied"); ++ const char* color_behavior; ++ if (param.color_behavior == ColorBehavior::kIgnore) { ++ color_behavior = "Ignore"; ++ } else if (param.color_behavior == ColorBehavior::kTag) { ++ color_behavior = "Tag"; ++ } else { ++ DCHECK(param.color_behavior == ColorBehavior::kTransformToSRGB); ++ color_behavior = "TransformToSRGB"; ++ } ++ const char* orientation; ++ switch (param.orientation) { ++ case ImageOrientationEnum::kOriginTopLeft: ++ orientation = "kOriginTopLeft"; ++ break; ++ case ImageOrientationEnum::kOriginTopRight: ++ orientation = "kOriginTopRight"; ++ break; ++ case ImageOrientationEnum::kOriginBottomRight: ++ orientation = "kOriginBottomRight"; ++ break; ++ case ImageOrientationEnum::kOriginBottomLeft: ++ orientation = "kOriginBottomLeft"; ++ break; ++ case ImageOrientationEnum::kOriginLeftTop: ++ orientation = "kOriginLeftTop"; ++ break; ++ case ImageOrientationEnum::kOriginRightTop: ++ orientation = "kOriginRightTop"; ++ break; ++ case ImageOrientationEnum::kOriginRightBottom: ++ orientation = "kOriginRightBottom"; ++ break; ++ case ImageOrientationEnum::kOriginLeftBottom: ++ orientation = "kOriginLeftBottom"; ++ break; ++ } ++ return os << "\nStaticColorCheckParam {\n path: \"" << param.path ++ << "\",\n bit_depth: " << param.bit_depth ++ << ",\n color_type: " << color_type ++ << ",\n alpha_option: " << alpha_option ++ << ",\n color_behavior: " << color_behavior ++ << ",\n orientation: " << orientation << "\n}"; ++} ++ ++StaticColorCheckParam kTestParams[] = { ++ { ++ "/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLossyFormat, ++ ImageDecoder::kAlphaNotPremultiplied, // q=60(lossy) ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ {}, // we just check that this image is lossy. ++ }, ++ { ++ "/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLossyFormat, ++ ImageDecoder::kAlphaNotPremultiplied, // q=60(lossy) ++ ColorBehavior::kIgnore, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ {}, // we just check that the decoder won't crash when ++ // ColorBehavior::kIgnore is used. ++ }, ++ {"/images/resources/avif/red-with-alpha-8bpc.avif", ++ 8, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 3, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(127, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-unspecified-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/silver-full-range-srgb-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, ++ }}, ++ {"/images/resources/avif/silver-400-matrix-6.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, ++ }}, ++ {"/images/resources/avif/silver-400-matrix-0.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-limited-range-8bpc.avif", ++ 8, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-full-range-8bpc.avif", ++ 8, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++ {"/images/resources/avif/red-with-alpha-8bpc.avif", ++ 8, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 4, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(127, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM ++ {"/images/resources/avif/red-with-profile-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kIgnore, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, ++ }}, ++#endif ++#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM ++ {"/images/resources/avif/red-with-profile-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ /* ++ * "Color Spin" ICC profile, embedded in this image, ++ * changes blue to red. ++ */ ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++#endif ++ {"/images/resources/avif/red-with-alpha-10bpc.avif", ++ 10, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 2, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-with-alpha-10bpc.avif", ++ 10, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 2, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-420-10bpc.avif", ++ 10, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-limited-range-10bpc.avif", ++ 10, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-full-range-10bpc.avif", ++ 10, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM ++ {"/images/resources/avif/red-with-profile-10bpc.avif", ++ 10, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kIgnore, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, ++ }}, ++#endif ++#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM ++ {"/images/resources/avif/red-with-profile-10bpc.avif", ++ 10, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ /* ++ * "Color Spin" ICC profile, embedded in this image, ++ * changes blue to red. ++ */ ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++#endif ++ {"/images/resources/avif/red-with-alpha-12bpc.avif", ++ 12, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 2, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-with-alpha-12bpc.avif", ++ 12, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 2, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-420-12bpc.avif", ++ 12, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-limited-range-12bpc.avif", ++ 12, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++ {"/images/resources/avif/alpha-mask-full-range-12bpc.avif", ++ 12, ++ ColorType::kMono, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, ++ }}, ++#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM ++ {"/images/resources/avif/red-with-profile-12bpc.avif", ++ 12, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kIgnore, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, ++ }}, ++#endif ++#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM ++ {"/images/resources/avif/red-with-profile-12bpc.avif", ++ 12, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTransformToSRGB, ++ ImageOrientationEnum::kOriginTopLeft, ++ 1, ++ { ++ /* ++ * "Color Spin" ICC profile, embedded in this image, ++ * changes blue to red. ++ */ ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++#endif ++ {"/images/resources/avif/red-and-purple-crop.avif", ++ 8, ++ ColorType::kRgbA, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopLeft, ++ 0, ++ { ++ // The clean aperture's size is 200x50. The left half is red and the ++ // right half is purple. Alpha values in the clean aperture are 255. ++ // (Alpha values to the right of the clean aperture are 128.) ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, // red ++ {gfx::Point(99, 24), SkColorSetARGB(255, 255, 0, 0)}, // red ++ {gfx::Point(100, 25), SkColorSetARGB(255, 127, 0, 128)}, // purple ++ {gfx::Point(199, 49), SkColorSetARGB(255, 127, 0, 128)}, // purple ++ }}, ++ {"/images/resources/avif/red-full-range-angle-1-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginLeftBottom, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-mode-0-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginBottomLeft, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-mode-1-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopRight, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-angle-2-mode-0-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginTopRight, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ {"/images/resources/avif/red-full-range-angle-3-mode-1-420-8bpc.avif", ++ 8, ++ ColorType::kRgb, ++ ImageDecoder::kLosslessFormat, ++ ImageDecoder::kAlphaNotPremultiplied, ++ ColorBehavior::kTag, ++ ImageOrientationEnum::kOriginLeftTop, ++ 0, ++ { ++ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, ++ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, ++ }}, ++ // TODO(ryoh): Add other color profile images, such as BT2020CL, ++ // SMPTE 274M ++ // TODO(ryoh): Add images with different combinations of ColorPrimaries, ++ // TransferFunction and MatrixCoefficients, ++ // such as: ++ // sRGB ColorPrimaries, BT.2020 TransferFunction and ++ // BT.709 MatrixCoefficients ++ // TODO(ryoh): Add Mono + Alpha Images. ++}; ++ ++enum class ErrorPhase { kParse, kDecode }; ++ ++// If 'error_phase' is ErrorPhase::kParse, error is expected during parse ++// (SetData() call); else error is expected during decode ++// (DecodeFrameBufferAtIndex() call). ++void TestInvalidStaticImage(const char* avif_file, ErrorPhase error_phase) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ ++ scoped_refptr data = ReadFileToSharedBuffer(avif_file); ++ ASSERT_TRUE(data.get()); ++ decoder->SetData(std::move(data), true); ++ ++ if (error_phase == ErrorPhase::kParse) { ++ EXPECT_FALSE(decoder->IsSizeAvailable()); ++ EXPECT_TRUE(decoder->Failed()); ++ EXPECT_EQ(0u, decoder->FrameCount()); ++ EXPECT_FALSE(decoder->DecodeFrameBufferAtIndex(0)); ++ } else { ++ EXPECT_TRUE(decoder->IsSizeAvailable()); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_GT(decoder->FrameCount(), 0u); ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus()); ++ EXPECT_TRUE(decoder->Failed()); ++ } ++} ++ ++float HalfFloatToUnorm(uint16_t h) { ++ const uint32_t f = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1c000) << 13) | ++ ((h & 0x03ff) << 13); ++ return base::bit_cast(f); ++} ++ ++void ReadYUV(const char* file_name, ++ const gfx::Size& expected_y_size, ++ const gfx::Size& expected_uv_size, ++ SkColorType color_type, ++ int bit_depth, ++ gfx::Point3F* rgb_pixel = nullptr) { ++ scoped_refptr data = ++ ReadFileToSharedBuffer("web_tests/images/resources/avif/", file_name); ++ ASSERT_TRUE(data); ++ ++ auto decoder = CreateAVIFDecoder(); ++ decoder->SetData(std::move(data), true); ++ ++ ASSERT_TRUE(decoder->IsDecodedSizeAvailable()); ++ ASSERT_TRUE(decoder->CanDecodeToYUV()); ++ EXPECT_NE(decoder->GetYUVSubsampling(), cc::YUVSubsampling::kUnknown); ++ EXPECT_NE(decoder->GetYUVColorSpace(), ++ SkYUVColorSpace::kIdentity_SkYUVColorSpace); ++ EXPECT_EQ(decoder->GetYUVBitDepth(), bit_depth); ++ ++ gfx::Size size = decoder->DecodedSize(); ++ gfx::Size y_size = decoder->DecodedYUVSize(cc::YUVIndex::kY); ++ gfx::Size u_size = decoder->DecodedYUVSize(cc::YUVIndex::kU); ++ gfx::Size v_size = decoder->DecodedYUVSize(cc::YUVIndex::kV); ++ ++ EXPECT_EQ(size, y_size); ++ EXPECT_EQ(u_size, v_size); ++ ++ EXPECT_EQ(expected_y_size, y_size); ++ EXPECT_EQ(expected_uv_size, u_size); ++ ++ wtf_size_t row_bytes[3]; ++ row_bytes[0] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kY); ++ row_bytes[1] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kU); ++ row_bytes[2] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kV); ++ ++ size_t planes_data_size = row_bytes[0] * y_size.height() + ++ row_bytes[1] * u_size.height() + ++ row_bytes[2] * v_size.height(); ++ auto planes_data = std::make_unique(planes_data_size); ++ ++ void* planes[3]; ++ planes[0] = planes_data.get(); ++ planes[1] = static_cast(planes[0]) + row_bytes[0] * y_size.height(); ++ planes[2] = static_cast(planes[1]) + row_bytes[1] * u_size.height(); ++ ++ decoder->SetImagePlanes( ++ std::make_unique(planes, row_bytes, color_type)); ++ ++ decoder->DecodeToYUV(); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_TRUE(decoder->HasDisplayableYUVData()); ++ ++ auto metadata = decoder->MakeMetadataForDecodeAcceleration(); ++ EXPECT_EQ(cc::ImageType::kAVIF, metadata.image_type); ++ EXPECT_EQ(size, metadata.image_size); ++ if (expected_y_size == expected_uv_size) { ++ EXPECT_EQ(cc::YUVSubsampling::k444, metadata.yuv_subsampling); ++ } else if (expected_y_size.height() == expected_uv_size.height()) { ++ EXPECT_EQ(cc::YUVSubsampling::k422, metadata.yuv_subsampling); ++ } else { ++ EXPECT_EQ(cc::YUVSubsampling::k420, metadata.yuv_subsampling); ++ } ++ ++ if (!rgb_pixel) { ++ return; ++ } ++ ++ if (bit_depth > 8) { ++ rgb_pixel->set_x(reinterpret_cast(planes[0])[0]); ++ rgb_pixel->set_y(reinterpret_cast(planes[1])[0]); ++ rgb_pixel->set_z(reinterpret_cast(planes[2])[0]); ++ } else { ++ rgb_pixel->set_x(reinterpret_cast(planes[0])[0]); ++ rgb_pixel->set_y(reinterpret_cast(planes[1])[0]); ++ rgb_pixel->set_z(reinterpret_cast(planes[2])[0]); ++ } ++ ++ if (color_type == kGray_8_SkColorType) { ++ const float max_channel = (1 << bit_depth) - 1; ++ rgb_pixel->set_x(rgb_pixel->x() / max_channel); ++ rgb_pixel->set_y(rgb_pixel->y() / max_channel); ++ rgb_pixel->set_z(rgb_pixel->z() / max_channel); ++ } else if (color_type == kA16_unorm_SkColorType) { ++ constexpr float kR16MaxChannel = 65535.0f; ++ rgb_pixel->set_x(rgb_pixel->x() / kR16MaxChannel); ++ rgb_pixel->set_y(rgb_pixel->y() / kR16MaxChannel); ++ rgb_pixel->set_z(rgb_pixel->z() / kR16MaxChannel); ++ } else { ++ DCHECK_EQ(color_type, kA16_float_SkColorType); ++ rgb_pixel->set_x(HalfFloatToUnorm(rgb_pixel->x())); ++ rgb_pixel->set_y(HalfFloatToUnorm(rgb_pixel->y())); ++ rgb_pixel->set_z(HalfFloatToUnorm(rgb_pixel->z())); ++ } ++ ++ // Convert our YUV pixel to RGB to avoid an excessive amounts of test ++ // expectations. We otherwise need bit_depth * yuv_sampling * color_type. ++ gfx::ColorTransform::Options options; ++ options.src_bit_depth = bit_depth; ++ options.dst_bit_depth = bit_depth; ++ auto transform = gfx::ColorTransform::NewColorTransform( ++ reinterpret_cast(decoder.get()) ++ ->GetColorSpaceForTesting(), ++ gfx::ColorSpace(), options); ++ transform->Transform(rgb_pixel, 1); ++} ++ ++void TestYUVRed(const char* file_name, ++ const gfx::Size& expected_uv_size, ++ SkColorType color_type = kGray_8_SkColorType, ++ int bit_depth = 8) { ++ SCOPED_TRACE(base::StringPrintf("file_name=%s, color_type=%d", file_name, ++ int{color_type})); ++ ++ constexpr gfx::Size kRedYSize(3, 3); ++ ++ gfx::Point3F decoded_pixel; ++ ASSERT_NO_FATAL_FAILURE(ReadYUV(file_name, kRedYSize, expected_uv_size, ++ color_type, bit_depth, &decoded_pixel)); ++ ++ // Allow the RGB value to be off by one step. 1/max_value is the minimum ++ // amount of error possible if error exists for integer sources. ++ // ++ // For half float values we have additional error from precision limitations, ++ // which gets worse at the extents of [-0.5, 1] -- which is the case for our R ++ // channel since we're using a pure red source. ++ // ++ // https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Precision_limitations_on_decimal_values_in_[0,_1] ++ const double kMinError = 1.0 / ((1 << bit_depth) - 1); ++ const double kError = color_type == kA16_float_SkColorType ++ ? kMinError + std::pow(2, -11) ++ : kMinError; ++ EXPECT_NEAR(decoded_pixel.x(), 1, kError); // R ++ EXPECT_NEAR(decoded_pixel.y(), 0, kMinError); // G ++ EXPECT_NEAR(decoded_pixel.z(), 0, kMinError); // B ++} ++ ++void DecodeTask(const Vector* data, base::RepeatingClosure* done) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ ++ scoped_refptr data_copy = SharedBuffer::Create(); ++ data_copy->Append(*data); ++ decoder->SetData(std::move(data_copy), true); ++ ++ EXPECT_TRUE(decoder->IsSizeAvailable()); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(decoder->FrameCount(), 1u); ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); ++ EXPECT_FALSE(decoder->Failed()); ++ ++ done->Run(); ++} ++ ++void InspectImage( ++ const StaticColorCheckParam& param, ++ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_option) { ++ std::unique_ptr decoder = CreateAVIFDecoderWithOptions( ++ param.alpha_option, high_bit_depth_option, param.color_behavior, ++ cc::AuxImage::kDefault, ImageDecoder::AnimationOption::kUnspecified); ++ scoped_refptr data = ReadFileToSharedBuffer(param.path); ++ ASSERT_TRUE(data.get()); ++#if FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS ++ EXPECT_EQ(param.compression_format, ++ ImageDecoder::GetCompressionFormat(data, "image/avif")); ++#endif ++ decoder->SetData(std::move(data), true); ++ EXPECT_EQ(1u, decoder->FrameCount()); ++ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount()); ++ EXPECT_EQ(param.bit_depth > 8, decoder->ImageIsHighBitDepth()); ++ auto metadata = decoder->MakeMetadataForDecodeAcceleration(); ++ EXPECT_EQ(cc::ImageType::kAVIF, metadata.image_type); ++ // TODO(wtc): Check metadata.yuv_subsampling. ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(param.orientation, decoder->Orientation()); ++ EXPECT_EQ(param.color_type == ColorType::kRgbA || ++ param.color_type == ColorType::kMonoA, ++ frame->HasAlpha()); ++ auto get_color_channel = [](SkColorChannel channel, SkColor color) { ++ switch (channel) { ++ case SkColorChannel::kR: ++ return SkColorGetR(color); ++ case SkColorChannel::kG: ++ return SkColorGetG(color); ++ case SkColorChannel::kB: ++ return SkColorGetB(color); ++ case SkColorChannel::kA: ++ return SkColorGetA(color); ++ } ++ }; ++ auto color_difference = [get_color_channel](SkColorChannel channel, ++ SkColor color1, ++ SkColor color2) -> int { ++ return std::abs(static_cast(get_color_channel(channel, color1)) - ++ static_cast(get_color_channel(channel, color2))); ++ }; ++ for (const auto& expected : param.colors) { ++ const SkBitmap& bitmap = frame->Bitmap(); ++ SkColor frame_color = ++ bitmap.getColor(expected.point.x(), expected.point.y()); ++ ++ EXPECT_LE(color_difference(SkColorChannel::kR, frame_color, expected.color), ++ param.color_threshold); ++ EXPECT_LE(color_difference(SkColorChannel::kG, frame_color, expected.color), ++ param.color_threshold); ++ EXPECT_LE(color_difference(SkColorChannel::kB, frame_color, expected.color), ++ param.color_threshold); ++ // TODO(ryoh): Create alpha_threshold field for alpha channels. ++ EXPECT_LE(color_difference(SkColorChannel::kA, frame_color, expected.color), ++ param.color_threshold); ++ if (param.color_type == ColorType::kMono || ++ param.color_type == ColorType::kMonoA) { ++ EXPECT_EQ(SkColorGetR(frame_color), SkColorGetG(frame_color)); ++ EXPECT_EQ(SkColorGetR(frame_color), SkColorGetB(frame_color)); ++ } ++ } ++} ++ ++void TestAvifBppHistogram(const char* image_name, ++ const char* histogram_name = nullptr, ++ base::HistogramBase::Sample32 sample = 0) { ++ TestBppHistogram(CreateAVIFDecoder, "Avif", image_name, histogram_name, ++ sample); ++} ++ ++struct AVIFImageParam { ++ const char* path; ++ size_t expected_frame_count; ++ int expected_repetition_count; ++}; ++ ++constexpr AVIFImageParam kAnimatedTestParams[] = { ++ // star-animated-8bpc.avif, star-animated-10bpc.avif, and ++ // star-animated-12bpc.avif contain an EditListBox whose `flags` field is ++ // equal to 0, meaning the edit list is not repeated. Therefore their ++ // `expected_repetition_count` is 0. ++ {"/images/resources/avif/star-animated-8bpc.avif", 5u, 0}, ++ {"/images/resources/avif/star-animated-8bpc-with-alpha.avif", 5u, ++ kAnimationLoopInfinite}, ++ {"/images/resources/avif/star-animated-10bpc.avif", 5u, 0}, ++ {"/images/resources/avif/star-animated-10bpc-with-alpha.avif", 5u, ++ kAnimationLoopInfinite}, ++ {"/images/resources/avif/star-animated-12bpc.avif", 5u, 0}, ++ {"/images/resources/avif/star-animated-12bpc-with-alpha.avif", 5u, ++ kAnimationLoopInfinite}, ++ {"/images/resources/avif/star-animated-8bpc-1-repetition.avif", 5u, 1}, ++ {"/images/resources/avif/star-animated-8bpc-10-repetition.avif", 5u, 10}, ++ {"/images/resources/avif/star-animated-8bpc-infinite-repetition.avif", 5u, ++ kAnimationLoopInfinite}, ++}; ++ ++constexpr AVIFImageParam kStaticTestParams[] = { ++ {"/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", 1, ++ kAnimationNone}, ++ {"/images/resources/avif/red-at-12-oclock-with-color-profile-8bpc.avif", 1, ++ kAnimationNone}, ++ {"/images/resources/avif/red-at-12-oclock-with-color-profile-10bpc.avif", 1, ++ kAnimationNone}, ++ {"/images/resources/avif/red-at-12-oclock-with-color-profile-12bpc.avif", 1, ++ kAnimationNone}, ++ {"/images/resources/avif/tiger_3layer_1res.avif", 1, kAnimationNone}, ++ {"/images/resources/avif/tiger_3layer_3res.avif", 1, kAnimationNone}, ++ {"/images/resources/avif/tiger_420_8b_grid1x13.avif", 1, kAnimationNone}, ++ {"/images/resources/avif/dice_444_10b_grid4x3.avif", 1, kAnimationNone}, ++ {"/images/resources/avif/gracehopper_422_12b_grid2x4.avif", 1, ++ kAnimationNone}, ++ {"/images/resources/avif/small-with-gainmap-iso.avif", 1, kAnimationNone}, ++}; ++ ++using AVIFValidImagesTest = ::testing::TestWithParam; ++ ++INSTANTIATE_TEST_SUITE_P(AnimatedAVIF, ++ AVIFValidImagesTest, ++ ::testing::ValuesIn(kAnimatedTestParams)); ++ ++INSTANTIATE_TEST_SUITE_P(StaticAVIF, ++ AVIFValidImagesTest, ++ ::testing::ValuesIn(kStaticTestParams)); ++ ++TEST_P(AVIFValidImagesTest, ByteByByteDecode) { ++ TestByteByByteDecode(&CreateAVIFDecoder, GetParam().path, ++ GetParam().expected_frame_count, ++ GetParam().expected_repetition_count); ++} ++ ++TEST(AnimatedAVIFTests, HasMultipleSubImages) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/star-animated-8bpc.avif"), ++ true); ++ EXPECT_TRUE(decoder->ImageHasBothStillAndAnimatedSubImages()); ++} ++ ++TEST(StaticAVIFTests, DoesNotHaveMultipleSubImages) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/" ++ "red-at-12-oclock-with-color-profile-8bpc.avif"), ++ true); ++ EXPECT_FALSE(decoder->ImageHasBothStillAndAnimatedSubImages()); ++} ++ ++TEST(StaticAVIFTests, HasTimingInformation) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/" ++ "red-at-12-oclock-with-color-profile-8bpc.avif"), ++ true); ++ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(0)); ++ ++ // libavif has placeholder values for timestamp and duration on still images, ++ // so any duration value is valid, but the timestamp should be zero. ++ EXPECT_EQ(base::TimeDelta(), decoder->FrameTimestampAtIndex(0)); ++} ++ ++TEST(AnimatedAVIFTests, HasTimingInformation) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/star-animated-8bpc.avif"), ++ true); ++ ++ constexpr auto kDuration = base::Milliseconds(100); ++ ++ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(0)); ++ EXPECT_EQ(base::TimeDelta(), decoder->FrameTimestampAtIndex(0)); ++ EXPECT_EQ(kDuration, decoder->FrameDurationAtIndex(0)); ++ ++ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(1)); ++ EXPECT_EQ(kDuration, decoder->FrameTimestampAtIndex(1)); ++ EXPECT_EQ(kDuration, decoder->FrameDurationAtIndex(1)); ++} ++ ++TEST(StaticAVIFTests, NoCrashWhenCheckingForMultipleSubImages) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ constexpr char kHeader[] = {0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70}; ++ auto buffer = SharedBuffer::Create(); ++ buffer->Append(kHeader); ++ decoder->SetData(std::move(buffer), false); ++ EXPECT_FALSE(decoder->ImageHasBothStillAndAnimatedSubImages()); ++} ++ ++// TODO(ryoh): Add corrupted video tests. ++ ++TEST(StaticAVIFTests, invalidImages) { ++ // Image data is truncated. ++ TestInvalidStaticImage( ++ "/images/resources/avif/" ++ "red-at-12-oclock-with-color-profile-truncated.avif", ++ ErrorPhase::kParse); ++ // Chunk size in AV1 frame header doesn't match the file size. ++ TestInvalidStaticImage( ++ "/images/resources/avif/" ++ "red-at-12-oclock-with-color-profile-with-wrong-frame-header.avif", ++ ErrorPhase::kDecode); ++} ++ ++TEST(StaticAVIFTests, GetIsoGainmapInfoAndData) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ scoped_refptr data = ReadFileToSharedBuffer( ++ "/images/resources/avif/small-with-gainmap-iso.avif"); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(decoder->Size(), gfx::Size(134, 100)); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_TRUE(has_gainmap); ++ ++ // Check gainmap metadata. ++ constexpr double kEpsilon = 0.00001; ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[0], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[1], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[2], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[0], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[1], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[2], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[3], 1., kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[0], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[1], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[2], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[0], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[1], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[2], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[0], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[1], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[2], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fDisplayRatioSdr, 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fDisplayRatioHdr, std::exp2(1.4427), kEpsilon); ++ ++ EXPECT_EQ(gainmap_info.fBaseImageType, SkGainmapInfo::BaseImageType::kSDR); ++ ++ EXPECT_EQ(gainmap_info.fGainmapMathColorSpace, nullptr); ++ ++ // Check that the gainmap can be decoded. ++ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); ++ gainmap_decoder->SetData(gainmap_data, true); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(gainmap_decoder->Size(), gfx::Size(33, 25)); ++ ImageFrame* gainmap_frame = gainmap_decoder->DecodeFrameBufferAtIndex(0); ++ EXPECT_TRUE(gainmap_frame); ++} ++ ++TEST(StaticAVIFTests, GetIsoGainmapInfoAndDataHdrToSdr) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ scoped_refptr data = ReadFileToSharedBuffer( ++ "/images/resources/avif/small-with-gainmap-iso-hdrbase.avif"); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(decoder->Size(), gfx::Size(134, 100)); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_TRUE(has_gainmap); ++ ++ // Check gainmap metadata. ++ constexpr double kEpsilon = 0.00001; ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[0], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[1], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[2], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[0], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[1], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[2], std::exp2(1.4427), kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[0], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[1], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[2], 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fGainmapGamma[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[0], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[1], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[2], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonSdr[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[0], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[1], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[2], 0.015625, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fEpsilonHdr[3], 1.0, kEpsilon); ++ ++ EXPECT_NEAR(gainmap_info.fDisplayRatioSdr, 1.0, kEpsilon); ++ EXPECT_NEAR(gainmap_info.fDisplayRatioHdr, std::exp2(1.4427), kEpsilon); ++ ++ EXPECT_EQ(gainmap_info.fBaseImageType, SkGainmapInfo::BaseImageType::kHDR); ++ ++ // Check that the gainmap can be decoded. ++ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); ++ gainmap_decoder->SetData(gainmap_data, true); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(gainmap_decoder->Size(), gfx::Size(33, 25)); ++ ImageFrame* gainmap_frame = gainmap_decoder->DecodeFrameBufferAtIndex(0); ++ EXPECT_TRUE(gainmap_frame); ++} ++ ++TEST(StaticAVIFTests, GetIsoGainmapColorSpaceSameICC) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ // The image has use_base_color_space set to false (i.e. use the alternate ++ // image's color space), and the base and alternate image ICC profiles are the ++ // same, so the alternate image color space should be ignored. ++ scoped_refptr data = ReadFileToSharedBuffer( ++ "/images/resources/avif/small-with-gainmap-iso-usealtcolorspace.avif"); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_TRUE(has_gainmap); ++ ++ EXPECT_EQ(gainmap_info.fGainmapMathColorSpace, nullptr); ++} ++ ++void ExpectMatrixNear(const skcms_Matrix3x3& lhs, ++ const skcms_Matrix3x3& rhs, ++ float epsilon) { ++ for (int r = 0; r < 3; r++) { ++ for (int c = 0; c < 3; c++) { ++ EXPECT_NEAR(lhs.vals[r][c], rhs.vals[r][c], epsilon); ++ } ++ } ++} ++ ++TEST(StaticAVIFTests, GetIsoGainmapColorSpaceDifferentICC) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ // The image has use_base_color_space set to false (i.e. use the alternate ++ // image's color space), and the base and alternate image ICC profiles are ++ // different, so the alternate ICC profile should be set as ++ // fGainmapMathColorSpace. ++ // Base is sRGB, alternate is P3. ++ scoped_refptr data = ReadFileToSharedBuffer( ++ "/images/resources/avif/" ++ "small-with-gainmap-iso-usealtcolorspace-differenticc.avif"); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_TRUE(has_gainmap); ++ ++ // Check that the gain map color space is specified. ++ EXPECT_NE(gainmap_info.fGainmapMathColorSpace, nullptr); ++ // Only compare the color primaries, the transfer function is irrelevant. ++ skcms_Matrix3x3 matrix; ++ ASSERT_TRUE(gainmap_info.fGainmapMathColorSpace->toXYZD50(&matrix)); ++ ExpectMatrixNear(matrix, SkNamedGamut::kDisplayP3, 0.001); ++} ++ ++TEST(StaticAVIFTests, GetIsoGainmapColorSpaceDifferentCICP) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ // The image has use_base_color_space set to false (i.e. use the alternate ++ // image's color space), and the base and alternate images don't have ICC ++ // but CICP values instead. The alternate image's CICP values should be used. ++ // Base is sRGB, alternate is Rec 2020. ++ scoped_refptr data = ReadFileToSharedBuffer( ++ "/images/resources/avif/gainmap-sdr-srgb-to-hdr-wcg-rec2020.avif"); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_TRUE(has_gainmap); ++ ++ // Check that the gain map color space is specified. ++ EXPECT_NE(gainmap_info.fGainmapMathColorSpace, nullptr); ++ // Only compare the color primaries, the transfer function is irrelevant. ++ skcms_Matrix3x3 matrix; ++ ASSERT_TRUE(gainmap_info.fGainmapMathColorSpace->toXYZD50(&matrix)); ++ ExpectMatrixNear(matrix, SkNamedGamut::kRec2020, 0.0001); ++} ++ ++TEST(StaticAVIFTests, GetGainmapInfoAndDataWithFeatureDisabled) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{}, ++ /*disabled_features=*/{features::kAvifGainmapHdrImages}); ++ ++ const std::string image = "small-with-gainmap-iso.avif"; ++ scoped_refptr data = ++ ReadFileToSharedBuffer("web_tests/images/resources/avif", image.c_str()); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_FALSE(has_gainmap); ++ ++ // Check that we get an error if we try decoding the gain map. ++ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); ++ gainmap_decoder->SetData(data, true); ++ EXPECT_FALSE(gainmap_decoder->IsSizeAvailable()); ++ EXPECT_TRUE(gainmap_decoder->Failed()); ++ EXPECT_EQ(gainmap_decoder->FrameCount(), 0u); ++ EXPECT_FALSE(gainmap_decoder->DecodeFrameBufferAtIndex(0)); ++} ++ ++TEST(StaticAVIFTests, GetGainmapInfoAndDataWithTruncatedData) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ const std::string image = "small-with-gainmap-iso.avif"; ++ const Vector data_vector = ++ ReadFile("web_tests/images/resources/avif", image.c_str()); ++ scoped_refptr half_data = SharedBuffer::Create( ++ base::span(data_vector).first(data_vector.size() / 2)); ++ ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(half_data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_FALSE(has_gainmap); ++} ++ ++TEST(StaticAVIFTests, GetGainmapWithGammaZero) { ++ base::test::ScopedFeatureList scoped_feature_list; ++ scoped_feature_list.InitWithFeatures( ++ /*enabled_features=*/{features::kAvifGainmapHdrImages}, ++ /*disabled_features=*/{}); ++ ++ const std::string image = "small-with-gainmap-iso-gammazero.avif"; ++ scoped_refptr data = ++ ReadFileToSharedBuffer("web_tests/images/resources/avif", image.c_str()); ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(data, true); ++ SkGainmapInfo gainmap_info; ++ scoped_refptr gainmap_data; ++ const bool has_gainmap = ++ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); ++ ASSERT_FALSE(has_gainmap); ++} ++ ++TEST(StaticAVIFTests, YUV) { ++ // 3x3, YUV 4:2:0 ++ constexpr gfx::Size kUVSize420(2, 2); ++ TestYUVRed("red-limited-range-420-8bpc.avif", kUVSize420); ++ TestYUVRed("red-full-range-420-8bpc.avif", kUVSize420); ++ ++ // 3x3, YUV 4:2:2 ++ constexpr gfx::Size kUVSize422(2, 3); ++ TestYUVRed("red-limited-range-422-8bpc.avif", kUVSize422); ++ ++ // 3x3, YUV 4:4:4 ++ constexpr gfx::Size kUVSize444(3, 3); ++ TestYUVRed("red-limited-range-444-8bpc.avif", kUVSize444); ++ ++ // Full range BT709 color space is uncommon, but should be supported. ++ TestYUVRed("red-full-range-bt709-444-8bpc.avif", kUVSize444); ++ ++ for (const auto ct : {kA16_unorm_SkColorType, kA16_float_SkColorType}) { ++ // 3x3, YUV 4:2:0, 10bpc ++ TestYUVRed("red-limited-range-420-10bpc.avif", kUVSize420, ct, 10); ++ ++ // 3x3, YUV 4:2:2, 10bpc ++ TestYUVRed("red-limited-range-422-10bpc.avif", kUVSize422, ct, 10); ++ ++ // 3x3, YUV 4:4:4, 10bpc ++ TestYUVRed("red-limited-range-444-10bpc.avif", kUVSize444, ct, 10); ++ ++ // 3x3, YUV 4:2:0, 12bpc ++ TestYUVRed("red-limited-range-420-12bpc.avif", kUVSize420, ct, 12); ++ ++ // 3x3, YUV 4:2:2, 12bpc ++ TestYUVRed("red-limited-range-422-12bpc.avif", kUVSize422, ct, 12); ++ ++ // 3x3, YUV 4:4:4, 12bpc ++ TestYUVRed("red-limited-range-444-12bpc.avif", kUVSize444, ct, 12); ++ ++ // Various common color spaces should be supported. ++ TestYUVRed("red-full-range-bt2020-pq-444-10bpc.avif", kUVSize444, ct, 10); ++ TestYUVRed("red-full-range-bt2020-pq-444-12bpc.avif", kUVSize444, ct, 12); ++ TestYUVRed("red-full-range-bt2020-hlg-444-10bpc.avif", kUVSize444, ct, 10); ++ TestYUVRed("red-full-range-bt2020-hlg-444-12bpc.avif", kUVSize444, ct, 12); ++ } ++} ++ ++TEST(StaticAVIFTests, SizeAvailableBeforeAllDataReceived) { ++ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); ++ scoped_refptr segment_reader = ++ SegmentReader::CreateFromSharedBuffer(stream_buffer); ++ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( ++ "image/avif", segment_reader, /*data_complete=*/false, ++ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, ++ ColorBehavior::kTag, cc::AuxImage::kDefault, ++ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), ++ ImageDecoder::AnimationOption::kUnspecified); ++ EXPECT_FALSE(decoder->IsSizeAvailable()); ++ ++ Vector data = ++ ReadFile("/images/resources/avif/red-limited-range-420-8bpc.avif"); ++ stream_buffer->Append(data); ++ EXPECT_EQ(stream_buffer->size(), 318u); ++ decoder->SetData(stream_buffer, /*all_data_received=*/false); ++ // All bytes are appended so we should have size, even though we pass ++ // all_data_received=false. ++ EXPECT_TRUE(decoder->IsSizeAvailable()); ++ ++ decoder->SetData(stream_buffer, /*all_data_received=*/true); ++ EXPECT_TRUE(decoder->IsSizeAvailable()); ++} ++ ++TEST(StaticAVIFTests, ProgressiveDecoding) { ++ base::HistogramTester histogram_tester; ++ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); ++ scoped_refptr segment_reader = ++ SegmentReader::CreateFromSharedBuffer(stream_buffer); ++ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( ++ "image/avif", segment_reader, /*data_complete=*/false, ++ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, ++ ColorBehavior::kTag, cc::AuxImage::kDefault, ++ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), ++ ImageDecoder::AnimationOption::kUnspecified); ++ ++ Vector data = ReadFile("/images/resources/avif/tiger_3layer_1res.avif"); ++ ASSERT_EQ(data.size(), 70944u); ++ ++ // This image has three layers. The first layer is 8299 bytes. Because of ++ // image headers and other overhead, if we pass exactly 8299 bytes to the ++ // decoder, the decoder does not have enough data to decode the first layer. ++ stream_buffer->Append(base::span(data).first(8299u)); ++ decoder->SetData(stream_buffer, /*all_data_received=*/false); ++ EXPECT_TRUE(decoder->IsSizeAvailable()); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(decoder->FrameCount(), 1u); ++ histogram_tester.ExpectTotalCount("Blink.DecodedImage.AvifDensity.Count.02MP", ++ 0); ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameEmpty); ++ EXPECT_FALSE(decoder->Failed()); ++ ++ // An additional 301 bytes are enough data for the decoder to decode the first ++ // layer. With progressive decoding, the frame buffer status will transition ++ // to ImageFrame::kFramePartial. ++ stream_buffer->Append(base::span(data).subspan(8299u, 301u)); ++ decoder->SetData(stream_buffer, /*all_data_received=*/false); ++ EXPECT_FALSE(decoder->Failed()); ++ frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFramePartial); ++ EXPECT_FALSE(decoder->Failed()); ++ ++ base::HistogramTester::CountsMap expected_counts; ++ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( ++ "Blink.DecodedImage.AvifDensity.Count."), ++ testing::ContainerEq(expected_counts)); ++ ++ // Now send the rest of the data. ++ stream_buffer->Append(base::span(data).subspan(8299u + 301u, 62344u)); ++ decoder->SetData(stream_buffer, /*all_data_received=*/true); ++ EXPECT_FALSE(decoder->Failed()); ++ frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete); ++ EXPECT_FALSE(decoder->Failed()); ++ ++ constexpr int kImageArea = 1216 * 832; // = 1011712 ++ constexpr int kFileSize = 70944; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 56 ++ histogram_tester.ExpectUniqueSample( ++ "Blink.DecodedImage.AvifDensity.Count.02MP", kSample, 1); ++ expected_counts["Blink.DecodedImage.AvifDensity.Count.02MP"] = 1; ++ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( ++ "Blink.DecodedImage.AvifDensity.Count."), ++ testing::ContainerEq(expected_counts)); ++} ++ ++TEST(StaticAVIFTests, IncrementalDecoding) { ++ base::HistogramTester histogram_tester; ++ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); ++ scoped_refptr segment_reader = ++ SegmentReader::CreateFromSharedBuffer(stream_buffer); ++ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( ++ "image/avif", segment_reader, /*data_complete=*/false, ++ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, ++ ColorBehavior::kTag, cc::AuxImage::kDefault, ++ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), ++ ImageDecoder::AnimationOption::kUnspecified); ++ ++ Vector data = ++ ReadFile("/images/resources/avif/tiger_420_8b_grid1x13.avif"); ++ ++ constexpr int kImageArea = 1216 * 832; // = 1011712 ++ constexpr int kFileSize = 72257; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 57 ++ ++ struct Step { ++ size_t size; // In bytes. ++ ImageFrame::Status status; ++ int num_decoded_rows; // In pixels. ++ }; ++ // There are 13 tiles. Tiles are as wide as the image and 64 pixels tall. ++ // |num_decoded_rows| may be odd due to an output pixel row missing the ++ // following upsampled decoded chroma row (belonging to the next tile). ++ const Step steps[] = { ++ {2000, ImageFrame::kFrameEmpty, 0}, ++ // Decoding half of the bytes gives 6 tile rows. ++ {data.size() / 2, ImageFrame::kFramePartial, 6 * 64 - 1}, ++ // Decoding all bytes but one gives 12 tile rows. ++ {data.size() - 1, ImageFrame::kFramePartial, 12 * 64 - 1}, ++ // Decoding all bytes gives all 13 tile rows. ++ {data.size(), ImageFrame::kFrameComplete, 13 * 64}}; ++ size_t previous_size = 0; ++ auto data_span = base::span(data); ++ for (const Step& step : steps) { ++ stream_buffer->Append( ++ data_span.subspan(previous_size, step.size - previous_size)); ++ decoder->SetData(stream_buffer, step.status == ImageFrame::kFrameComplete); ++ ++ EXPECT_EQ(decoder->FrameCount(), 1u); ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ ASSERT_FALSE(decoder->Failed()); ++ EXPECT_EQ(frame->GetStatus(), step.status); ++ ++ const SkBitmap& bitmap = frame->Bitmap(); ++ for (int y = 0; y < bitmap.height(); ++y) { ++ const uint32_t* row = bitmap.getAddr32(0, y); ++ const bool is_row_decoded = y < step.num_decoded_rows; ++ for (int x = 0; x < bitmap.width(); ++x) { ++ // The input image is opaque. Pixels outside the decoded area are fully ++ // transparent black pixels, with each channel value being 0. ++ const bool is_pixel_decoded = row[x] != 0x00000000u; ++ ASSERT_EQ(is_pixel_decoded, is_row_decoded); ++ } ++ } ++ previous_size = step.size; ++ ++ base::HistogramTester::CountsMap expected_counts; ++ if (step.status == ImageFrame::kFrameComplete) { ++ histogram_tester.ExpectUniqueSample( ++ "Blink.DecodedImage.AvifDensity.Count.02MP", kSample, 1); ++ expected_counts["Blink.DecodedImage.AvifDensity.Count.02MP"] = 1; ++ } ++ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( ++ "Blink.DecodedImage.AvifDensity.Count."), ++ testing::ContainerEq(expected_counts)); ++ } ++} ++ ++// Reproduces crbug.com/1402841. Decodes a large AVIF image 104 times in ++// parallel from base::ThreadPool. Should not cause temporary deadlock of ++// base::ThreadPool. ++TEST(StaticAVIFTests, ParallelDecoding) { ++ // The base::test::TaskEnvironment constructor creates a base::ThreadPool ++ // instance with 4 foreground threads. The number 4 comes from the ++ // test::TaskEnvironment::kNumForegroundThreadPoolThreads constant. ++ base::test::TaskEnvironment task_environment; ++ ++ // This test image is fast to decode (all neutral gray pixels) and its ++ // allocation size is large enough to cause ++ // media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels() to pick ++ // n_tasks > 1 if AVIFImageDecoder did not pass disable_threading=true to it. ++ Vector data = ReadFile("/images/resources/avif/gray1024x704.avif"); ++ ++ // Task timeout in tests is 30 seconds (see https://crrev.com/c/1949028). ++ // Four blocking tasks cause a temporary deadlock (1.2 seconds) of ++ // base::ThreadPool, so we need at least 30 / 1.2 * 4 = 100 decodes for the ++ // test to time out without the bug fix. We add a margin of 4 decodes, i.e., ++ // (30 / 1.2 + 1) * 4 = 104. ++ const size_t n_decodes = 104; ++ base::WaitableEvent event; ++ base::RepeatingClosure barrier = base::BarrierClosure( ++ n_decodes, ++ base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event))); ++ ++ for (size_t i = 0; i < n_decodes; ++i) { ++ base::ThreadPool::PostTask( ++ FROM_HERE, ++ base::BindOnce(DecodeTask, base::Unretained(&data), &barrier)); ++ } ++ ++ event.Wait(); ++} ++ ++TEST(StaticAVIFTests, AlphaHasNoIspeProperty) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/green-no-alpha-ispe.avif"), ++ true); ++ EXPECT_FALSE(decoder->IsSizeAvailable()); ++ EXPECT_TRUE(decoder->Failed()); ++} ++ ++TEST(StaticAVIFTests, UnsupportedTransferFunctionInColrProperty) { ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData(ReadFileToSharedBuffer( ++ "/images/resources/avif/red-unsupported-transfer.avif"), ++ true); ++ EXPECT_FALSE(decoder->IsSizeAvailable()); ++ EXPECT_TRUE(decoder->Failed()); ++} ++ ++TEST(StaticAVIFTests, ClapPropertyZeroOrigin) { ++ constexpr int kClapWidth = 200; ++ constexpr int kClapHeight = 50; ++ std::unique_ptr decoder1 = CreateAVIFDecoder(); ++ decoder1->SetData( ++ ReadFileToSharedBuffer("/images/resources/avif/red-and-purple-crop.avif"), ++ true); ++ ASSERT_TRUE(decoder1->IsSizeAvailable()); ++ gfx::Size size1 = decoder1->Size(); ++ ASSERT_EQ(size1.width(), kClapWidth); ++ ASSERT_EQ(size1.height(), kClapHeight); ++ ImageFrame* frame1 = decoder1->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame1); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame1->GetStatus()); ++ EXPECT_FALSE(decoder1->Failed()); ++ const SkBitmap& bitmap1 = frame1->Bitmap(); ++ ++ // The second image is the uncropped version of the first image. ++ std::unique_ptr decoder2 = CreateAVIFDecoder(); ++ decoder2->SetData(ReadFileToSharedBuffer( ++ "/images/resources/avif/red-and-purple-and-blue.avif"), ++ true); ++ ASSERT_TRUE(decoder2->IsSizeAvailable()); ++ gfx::Size size2 = decoder2->Size(); ++ ASSERT_EQ(size2.width(), 300); ++ ASSERT_EQ(size2.height(), 100); ++ ImageFrame* frame2 = decoder2->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame2); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame2->GetStatus()); ++ EXPECT_FALSE(decoder2->Failed()); ++ const SkBitmap& bitmap2 = frame2->Bitmap(); ++ ++ // Compare pixel data. ++ for (int row = 0; row < kClapHeight; ++row) { ++ for (int col = 0; col < kClapWidth; ++col) { ++ EXPECT_EQ(bitmap1.getColor(/*x=*/col, /*y=*/row), ++ bitmap2.getColor(/*x=*/col, /*y=*/row)); ++ } ++ } ++} ++ ++// Verifies that an invalid 'clap' (clean aperture) image property is handled by ++// ignoring the 'clap' property and showing the full image. ++TEST(StaticAVIFTests, InvalidClapPropertyHandling) { ++ // The first image has a valid 'clap' property. The full image has size ++ // 320x280. The clean aperture has size 180x100, located at (40, 80) of the ++ // full image. ++ // ++ // Since the origin of the clean aperture is not located at (0, 0), we treat ++ // the 'clap' property as invalid. So the full image is shown. ++ std::unique_ptr decoder1 = CreateAVIFDecoder(); ++ decoder1->SetData(ReadFileToSharedBuffer( ++ "/images/resources/avif/blue-and-magenta-crop.avif"), ++ true); ++ ASSERT_TRUE(decoder1->IsSizeAvailable()); ++ gfx::Size size1 = decoder1->Size(); ++ ASSERT_EQ(size1.width(), 320); ++ ASSERT_EQ(size1.height(), 280); ++ ImageFrame* frame1 = decoder1->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame1); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame1->GetStatus()); ++ EXPECT_FALSE(decoder1->Failed()); ++ const SkBitmap& bitmap1 = frame1->Bitmap(); ++ ++ // The second image is the same as the first image except that the 'clap' ++ // property is invalid. In this case the full image is shown. ++ std::unique_ptr decoder2 = CreateAVIFDecoder(); ++ decoder2->SetData( ++ ReadFileToSharedBuffer( ++ "/images/resources/avif/blue-and-magenta-crop-invalid.avif"), ++ true); ++ ASSERT_TRUE(decoder2->IsSizeAvailable()); ++ gfx::Size size2 = decoder2->Size(); ++ ASSERT_EQ(size2.width(), 320); ++ ASSERT_EQ(size2.height(), 280); ++ ImageFrame* frame2 = decoder2->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame2); ++ EXPECT_EQ(ImageFrame::kFrameComplete, frame2->GetStatus()); ++ EXPECT_FALSE(decoder2->Failed()); ++ const SkBitmap& bitmap2 = frame2->Bitmap(); ++ ++ // Compare pixel data. ++ for (int row = 0; row < size1.height(); ++row) { ++ for (int col = 0; col < size1.width(); ++col) { ++ EXPECT_EQ(bitmap1.getColor(/*x=*/col, /*y=*/row), ++ bitmap2.getColor(/*x=*/col, /*y=*/row)); ++ } ++ } ++} ++ ++TEST(StaticAVIFTests, BppHistogramSmall) { ++ constexpr int kImageArea = 768 * 512; // = 393216 ++ constexpr int kFileSize = 25724; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 52 ++ TestAvifBppHistogram("/images/resources/avif/kodim03.avif", ++ "Blink.DecodedImage.AvifDensity.Count.0.4MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramSmall3x3) { ++ // The centi bpp = 318 * 100 * 8 / (3 * 3) ~= 28267, which is greater than the ++ // histogram's max value (1000), so this sample goes into the overflow bucket. ++ constexpr int kSample = 1000; ++ TestAvifBppHistogram("/images/resources/avif/red-full-range-420-8bpc.avif", ++ "Blink.DecodedImage.AvifDensity.Count.0.1MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramSmall900000) { ++ constexpr int kImageArea = 1200 * 750; // = 900000 ++ constexpr int kFileSize = 8144; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 7 ++ TestAvifBppHistogram("/images/resources/avif/peach_900000.avif", ++ "Blink.DecodedImage.AvifDensity.Count.0.9MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramBig) { ++ constexpr int kImageArea = 4032 * 3024; // = 12192768 ++ constexpr int kFileSize = 88692; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 6 ++ TestAvifBppHistogram("/images/resources/avif/bee.avif", ++ "Blink.DecodedImage.AvifDensity.Count.13MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramBig13000000) { ++ constexpr int kImageArea = 4000 * 3250; // = 13000000 ++ constexpr int kFileSize = 16725; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 ++ TestAvifBppHistogram("/images/resources/avif/peach_13000000.avif", ++ "Blink.DecodedImage.AvifDensity.Count.13MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramHuge) { ++ constexpr int kImageArea = 4624 * 3472; // = 16054528 ++ constexpr int kFileSize = 20095; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 ++ TestAvifBppHistogram("/images/resources/avif/peach.avif", ++ "Blink.DecodedImage.AvifDensity.Count.14+MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramHuge13000002) { ++ constexpr int kImageArea = 3961 * 3282; // = 13000002 ++ constexpr int kFileSize = 16379; ++ constexpr int kSample = ++ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 ++ TestAvifBppHistogram("/images/resources/avif/peach_13000002.avif", ++ "Blink.DecodedImage.AvifDensity.Count.14+MP", kSample); ++} ++ ++TEST(StaticAVIFTests, BppHistogramInvalid) { ++ base::HistogramTester histogram_tester; ++ std::unique_ptr decoder = CreateAVIFDecoder(); ++ decoder->SetData( ++ ReadFileToSharedBuffer( ++ "/images/resources/avif/" ++ "red-at-12-oclock-with-color-profile-with-wrong-frame-header.avif"), ++ true); ++ ASSERT_TRUE(decoder->IsSizeAvailable()); ++ EXPECT_FALSE(decoder->Failed()); ++ EXPECT_EQ(decoder->FrameCount(), 1u); ++ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); ++ ASSERT_TRUE(frame); ++ EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus()); ++ EXPECT_TRUE(decoder->Failed()); ++ const base::HistogramTester::CountsMap empty_counts; ++ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( ++ "Blink.DecodedImage.AvifDensity.Count."), ++ testing::ContainerEq(empty_counts)); ++} ++ ++TEST(StaticAVIFTests, BppHistogram10bit) { ++ TestAvifBppHistogram("/images/resources/avif/red-full-range-420-10bpc.avif"); ++} ++ ++TEST(StaticAVIFTests, BppHistogramMonochrome) { ++ TestAvifBppHistogram("/images/resources/avif/silver-400-matrix-6.avif"); ++} ++ ++TEST(StaticAVIFTests, BppHistogramAlpha) { ++ TestAvifBppHistogram("/images/resources/avif/red-with-alpha-8bpc.avif"); ++} ++ ++TEST(StaticAVIFTests, BppHistogramAnimated) { ++ TestAvifBppHistogram("/images/resources/avif/star-animated-8bpc.avif"); ++} ++ ++using StaticAVIFColorTests = ::testing::TestWithParam; ++ ++INSTANTIATE_TEST_SUITE_P(Parameterized, ++ StaticAVIFColorTests, ++ ::testing::ValuesIn(kTestParams)); ++ ++TEST_P(StaticAVIFColorTests, InspectImage) { ++ InspectImage(GetParam(), ImageDecoder::kDefaultBitDepth); ++} ++ ++TEST_P(StaticAVIFColorTests, InspectImageHalfFloat) { ++ InspectImage(GetParam(), ImageDecoder::kHighBitDepthToHalfFloat); ++} ++ ++} // namespace ++ ++} // namespace blink +diff --git a/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py b/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py +new file mode 100644 +index 0000000000000..82f1c0c3df73a +--- /dev/null ++++ b/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py +@@ -0,0 +1,164 @@ ++# Copyright 2024 The Chromium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++"""Script to generate crabbyavif wrapper using the libavif wrapper as the base. ++ ++When the libavif wrapper files (any of avif_image_decoder*) are changed, this ++script must be run to update the crabbyavif wrappers ++(crabbyavif_image_decoder*). ++""" ++ ++import os ++import re ++ ++ ++def _read_file(filename): ++ with open(filename) as file: ++ return file.read() ++ ++ ++def _write_file(filename, contents): ++ with open(filename, "w") as file: ++ file.write(contents) ++ ++ ++def _apply_replacements(contents, replacements): ++ for find, replace in replacements: ++ contents = re.sub(find, replace, contents) ++ return contents ++ ++ ++_COMMON_REPLACEMENTS = ( ++ (r"Copyright 2020", "Copyright 2024"), ++ (r"third_party/libavif/src", "third_party/crabbyavif/src"), ++ (r"avif_image_decoder.h", "crabbyavif_image_decoder.h"), ++ (r"AVIFImageDecoder", "CrabbyAVIFImageDecoder"), ++ (r"AVIF_TRUE", "CRABBY_AVIF_TRUE"), ++ (r"AVIF_FALSE", "CRABBY_AVIF_FALSE"), ++ (r"AVIF_REPETITION_COUNT_", "CRABBY_AVIF_REPETITION_COUNT_"), ++) ++ ++_NOTICE = """// WARNING: Auto-generated by gen_crabbyavif_wrapper.py. ++// Do not modify manually. ++""" ++ ++ ++def _generate_crabbyavif_file(source_file, replacements): ++ contents = _read_file(source_file) ++ contents = _apply_replacements(contents, _COMMON_REPLACEMENTS) ++ contents = _apply_replacements(contents, replacements) ++ contents = contents.split("\n") ++ # Copyright notice is 3 lines, insert the notice as the 4th line. ++ contents.insert(3, _NOTICE) ++ contents = "\n".join(contents) ++ crabby_source_file = "crabby%(source_file)s" % locals() ++ _write_file(crabby_source_file, contents) ++ os.system("clang-format -style chromium -i %(crabby_source_file)s" % ++ locals()) ++ ++ ++_HEADER_REPLACEMENTS = ( ++ (r"AVIF_AVIF_IMAGE_DECODER_H_", "AVIF_CRABBYAVIF_IMAGE_DECODER_H_"), ++ (r"AVIF_PIXEL_FORMAT_NONE", "crabbyavif::AVIF_PIXEL_FORMAT_NONE"), ++ # Functions ++ (r"avifDecoderDestroy", "crabbyavif::crabby_avifDecoderDestroy"), ++ (r"avifImageDestroy", "crabbyavif::crabby_avifImageDestroy"), ++ # Types ++ (r"avifIO", "crabbyavif::avifIO"), ++ (r"avifPixelFormat", "crabbyavif::avifPixelFormat"), ++ (r"avifROData", "crabbyavif::avifROData"), ++ (r"avifResult", "crabbyavif::avifResult"), ++ (r"\bavifDecoder\b", "crabbyavif::avifDecoder"), ++ (r"\bavifImage\b", "crabbyavif::avifImage"), ++) ++ ++_CC_REPLACEMENTS = ( ++ # Functions (to be namespaced and prefixed with "crabby_") ++ ( ++ r"\bavifCropRectConvertCleanApertureBox\b", ++ "crabbyavif::crabby_avifCropRectConvertCleanApertureBox", ++ ), ++ (r"\bavifDecoderCreate\b", "crabbyavif::crabby_avifDecoderCreate"), ++ ( ++ r"\bavifDecoderDecodedRowCount\b", ++ "crabbyavif::crabby_avifDecoderDecodedRowCount", ++ ), ++ (r"\bavifDecoderDestroy\b", "crabbyavif::crabby_avifDecoderDestroy"), ++ (r"\bavifDecoderNthImage\b", "crabbyavif::crabby_avifDecoderNthImage"), ++ ( ++ r"\bavifDecoderNthImageMaxExtent\b", ++ "crabbyavif::crabby_avifDecoderNthImageMaxExtent", ++ ), ++ ( ++ r"\bavifDecoderNthImageMaxExtent\b", ++ "crabbyavif::crabby_avifDecoderNthImageMaxExtent", ++ ), ++ ( ++ r"\bavifDecoderNthImageTiming\b", ++ "crabbyavif::crabby_avifDecoderNthImageTiming", ++ ), ++ (r"\bavifDecoderParse\b", "crabbyavif::crabby_avifDecoderParse"), ++ (r"\bavifDecoderSetIO\b", "crabbyavif::crabby_avifDecoderSetIO"), ++ (r"\bavifDecoderSetSource\b", "crabbyavif::crabby_avifDecoderSetSource"), ++ ( ++ r"\bavifGetPixelFormatInfo\b", ++ "crabbyavif::crabby_avifGetPixelFormatInfo", ++ ), ++ (r"\bavifImageCreateEmpty\b", "crabbyavif::crabby_avifImageCreateEmpty"), ++ (r"\bavifImageDestroy\b", "crabbyavif::crabby_avifImageDestroy"), ++ (r"\bavifImageSetViewRect\b", "crabbyavif::crabby_avifImageSetViewRect"), ++ (r"\bavifImageYUVToRGB\b", "crabbyavif::crabby_avifImageYUVToRGB"), ++ ( ++ r"\bavifPeekCompatibleFileType\b", ++ "crabbyavif::crabby_avifPeekCompatibleFileType", ++ ), ++ ( ++ r"\bavifRGBImageSetDefaults\b", ++ "crabbyavif::crabby_avifRGBImageSetDefaults", ++ ), ++ (r"\bavifResultToString\b", "crabbyavif::crabby_avifResultToString"), ++ # Symbols (to be namespaced). ++ (r"avifBool\b", "crabbyavif::avifBool"), ++ (r"avifColorPrimaries\b", "crabbyavif::avifColorPrimaries"), ++ (r"avifCropRect\b", "crabbyavif::avifCropRect"), ++ (r"avifDecoder\b", "crabbyavif::avifDecoder"), ++ (r"avifDiagnostics\b", "crabbyavif::avifDiagnostics"), ++ (r"avifExtent\b", "crabbyavif::avifExtent"), ++ (r"avifGainMap\b", "crabbyavif::avifGainMap"), ++ (r"avifGainMapMetadata\b", "crabbyavif::avifGainMapMetadata"), ++ (r"avifIO\b", "crabbyavif::avifIO"), ++ (r"avifImage\b", "crabbyavif::avifImage"), ++ (r"avifImageTiming\b", "crabbyavif::avifImageTiming"), ++ (r"avifMatrixCoefficients\b", "crabbyavif::avifMatrixCoefficients"), ++ (r"avifPixelFormatInfo\b", "crabbyavif::avifPixelFormatInfo"), ++ (r"avifRGBImage\b", "crabbyavif::avifRGBImage"), ++ (r"avifROData\b", "crabbyavif::avifROData"), ++ (r"avifRange\b", "crabbyavif::avifRange"), ++ (r"avifResult\b", "crabbyavif::avifResult"), ++ ( ++ r"avifTransferCharacteristics\b", ++ "crabbyavif::avifTransferCharacteristics", ++ ), ++ (r"\bAVIF_", "crabbyavif::AVIF_"), ++ (r"\bCRABBY_AVIF_", "crabbyavif::CRABBY_AVIF_"), ++) ++ ++_TEST_REPLACEMENTS = ( ++ (r"\bAVIFValidImagesTest\b", "CrabbyAVIFValidImagesTest"), ++ (r"\bAnimatedAVIFTests\b", "CrabbyAnimatedAVIFTests"), ++ (r"\bStaticAVIFColorTests\b", "CrabbyStaticAVIFColorTests"), ++ (r"\bStaticAVIFTests\b", "CrabbyStaticAVIFTests"), ++) ++ ++_FUZZER_REPLACEMENTS = ((r"kAvifDecoder", "kCrabbyAvifDecoder"), ) ++ ++def main(): ++ _generate_crabbyavif_file("avif_image_decoder.h", _HEADER_REPLACEMENTS) ++ _generate_crabbyavif_file("avif_image_decoder.cc", _CC_REPLACEMENTS) ++ _generate_crabbyavif_file("avif_image_decoder_test.cc", _TEST_REPLACEMENTS) ++ _generate_crabbyavif_file("avif_image_decoder_fuzzer.cc", ++ _FUZZER_REPLACEMENTS) ++ ++ ++if __name__ == "__main__": ++ main() +diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc +index 0cc5dfa276964..0523ccbd18509 100644 +--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc ++++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc +@@ -52,6 +52,7 @@ + #include "ui/gfx/geometry/size_conversions.h" + + #if BUILDFLAG(ENABLE_AV1_DECODER) ++#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" + #include "third_party/blink/renderer/platform/image-decoders/avif/crabbyavif_image_decoder.h" + #endif + +@@ -197,7 +198,9 @@ String SniffMimeTypeInternal(scoped_refptr reader) { + return "image/bmp"; + } + #if BUILDFLAG(ENABLE_AV1_DECODER) +- if (CrabbyAVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { ++ if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif) ++ ? CrabbyAVIFImageDecoder::MatchesAVIFSignature(fast_reader) ++ : AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { + return "image/avif"; + } + #endif +@@ -307,9 +310,15 @@ std::unique_ptr ImageDecoder::CreateByMimeType( + max_decoded_bytes); + #if BUILDFLAG(ENABLE_AV1_DECODER) + } else if (mime_type == "image/avif") { +- decoder = std::make_unique( +- alpha_option, high_bit_depth_decoding_option, color_behavior, aux_image, +- max_decoded_bytes, animation_option); ++ if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif)) { ++ decoder = std::make_unique( ++ alpha_option, high_bit_depth_decoding_option, color_behavior, ++ aux_image, max_decoded_bytes, animation_option); ++ } else { ++ decoder = std::make_unique( ++ alpha_option, high_bit_depth_decoding_option, color_behavior, ++ aux_image, max_decoded_bytes, animation_option); ++ } + #endif + } + +diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc +index 93805eafce9fd..d07cbcd599f15 100644 +--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc ++++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc +@@ -5,6 +5,7 @@ + #include "third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h" + + #include "third_party/blink/renderer/platform/graphics/color_behavior.h" ++#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" + #include "third_party/blink/renderer/platform/image-decoders/avif/crabbyavif_image_decoder.h" + #include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h" + #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" +@@ -72,6 +73,13 @@ std::unique_ptr CreateImageDecoder(DecoderType decoder_type, + /*max_decoded_bytes=*/fdp.ConsumeIntegral(), + /*offset=*/fdp.ConsumeIntegral()); + } ++ case DecoderType::kAvifDecoder: { ++ return std::make_unique( ++ GetAlphaOption(fdp), GetHbdOption(fdp), GetColorBehavior(fdp), ++ GetAuxImageType(fdp), ++ /*max_decoded_bytes=*/fdp.ConsumeIntegral(), ++ GetAnimationOption(fdp)); ++ } + case DecoderType::kCrabbyAvifDecoder: { + return std::make_unique( + GetAlphaOption(fdp), GetHbdOption(fdp), GetColorBehavior(fdp), +diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h +index f6ad7fe6b4c52..d1227387a9145 100644 +--- a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h ++++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h +@@ -15,6 +15,7 @@ enum class DecoderType { + kBmpDecoder, + kJpegDecoder, + kPngDecoder, ++ kAvifDecoder, + kCrabbyAvifDecoder, + }; + diff --git a/meta-chromium/recipes-browser/chromium/files/0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch b/meta-chromium/recipes-browser/chromium/files/0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch deleted file mode 100644 index 637bbad0e..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch +++ /dev/null @@ -1,488 +0,0 @@ -From a8d282ffd65a8de353a446485d730b27ccd5cd67 Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Fri, 1 Nov 2024 08:48:02 -0300 -Subject: [PATCH] Revert "Connect the Rust log crate to the //base logging - implementation" - -This reverts chromium commit 7ea0a5e831dd5 ("Connect the Rust log crate -to the //base logging implementation". - -The above commit enabled bindgen for the Rust logger. Similar to patch -0011-Disable-crabbyavif-to-fix-build-errors.patch added in commit [0], -this fixes a build error that's most likely because the way upstream -configures bindgen makes it incompatible with our cross-architecture -builds. - -``` -| FAILED: gen/base/logging_rust_log_integration_bindgen_generator/bindings.rs -| [...] -| panicked at bindgen-cli/main.rs:52:36: -| Unable to generate bindings: ClangDiagnostic("error: unsupported option '-mbranch-protection=' for target 'x86_64-unknown-linux-gnu'\n") -``` - -[0] meta-browser commit fd347dc ("chromium: Update to 127.0.6533.99") - -Upstream-Status: Inappropriate [specific to our build setup] -Signed-off-by: Ariel D'Alessandro ---- - base/BUILD.gn | 42 ---------- - base/logging.cc | 9 --- - base/logging/rust_log_integration.cc | 24 ------ - base/logging/rust_log_integration.h | 29 ------- - base/logging/rust_log_integration_unittest.cc | 76 ------------------- - base/logging/rust_logger.rs | 74 ------------------ - base/test/BUILD.gn | 13 ---- - .../test/logging/test_rust_logger_consumer.rs | 41 ---------- - .../rust/chromium_crates_io/gnrt_config.toml | 1 + - third_party/rust/log/v0_4/BUILD.gn | 2 + - 10 files changed, 3 insertions(+), 308 deletions(-) - delete mode 100644 base/logging/rust_log_integration.cc - delete mode 100644 base/logging/rust_log_integration.h - delete mode 100644 base/logging/rust_log_integration_unittest.cc - delete mode 100644 base/logging/rust_logger.rs - delete mode 100644 base/test/logging/test_rust_logger_consumer.rs - -diff --git a/base/BUILD.gn b/base/BUILD.gn -index bc3e57164a010..ba0a6cad17477 100644 ---- a/base/BUILD.gn -+++ b/base/BUILD.gn -@@ -38,8 +38,6 @@ import("//build/config/sanitizers/sanitizers.gni") - import("//build/config/sysroot.gni") - import("//build/config/ui.gni") - import("//build/nocompile.gni") --import("//build/rust/rust_bindgen.gni") --import("//build/rust/rust_static_library.gni") - import("//build/timestamp.gni") - import("//build/util/process_version.gni") - import("//build_overrides/build.gni") -@@ -1022,9 +1020,7 @@ component("base") { - # Used by metrics/crc32, except on NaCl builds. - deps += [ "//third_party/zlib" ] - -- # NaCl does not support Rust. - deps += [ -- ":rust_logger", - "//third_party/rust/serde_json_lenient/v0_2/wrapper", - ] - } -@@ -1545,8 +1541,6 @@ component("base") { - "files/scoped_temp_file.h", - "json/json_file_value_serializer.cc", - "json/json_file_value_serializer.h", -- "logging/rust_log_integration.cc", -- "logging/rust_log_integration.h", - "memory/discardable_memory.cc", - "memory/discardable_memory.h", - "memory/discardable_memory_allocator.cc", -@@ -2449,41 +2443,6 @@ component("base") { - } - } - --rust_bindgen("logging_log_severity_bindgen") { -- # TODO(danakj): Maybe combine all base bindgen targets, or all base/logging -- # ones even) into a single GN target? But the GN rule needs to handle multiple -- # headers then. -- header = "logging/log_severity.h" -- cpp = true -- visibility = [ ":*" ] -- -- # Transitive generated header dependency. -- deps = [ ":debugging_buildflags" ] --} -- --rust_bindgen("logging_rust_log_integration_bindgen") { -- header = "logging/rust_log_integration.h" -- cpp = true -- visibility = [ ":*" ] -- -- # Transitive generated header dependency. -- deps = [ ":debugging_buildflags" ] --} -- --rust_static_library("rust_logger") { -- allow_unsafe = true # Unsafe needed for FFI. -- deps = [ -- ":logging_log_severity_bindgen", -- ":logging_rust_log_integration_bindgen", -- "//third_party/rust/log/v0_4:lib", -- ] -- visibility = [ ":base" ] -- sources = [ "logging/rust_logger.rs" ] -- crate_root = "logging/rust_logger.rs" -- -- cxx_bindings = [ "logging/rust_logger.rs" ] --} -- - if (is_linux || is_chromeos) { - # Split out as a separate target for two reasons: - # - the line number reader is 2x slower in debug builds if not optimized, -@@ -3287,7 +3246,6 @@ test("base_unittests") { - "json/string_escape_unittest.cc", - "json/values_util_unittest.cc", - "lazy_instance_unittest.cc", -- "logging/rust_log_integration_unittest.cc", - "logging_unittest.cc", - "memory/aligned_memory_unittest.cc", - "memory/discardable_memory_backing_field_trial_unittest.cc", -diff --git a/base/logging.cc b/base/logging.cc -index 508ce135131aa..d296b81fa6d36 100644 ---- a/base/logging.cc -+++ b/base/logging.cc -@@ -126,10 +126,6 @@ typedef FILE* FileHandle; - #include "base/fuchsia/scoped_fx_logger.h" - #endif - --#if !BUILDFLAG(IS_NACL) --#include "base/logging/rust_logger.rs.h" --#endif -- - namespace logging { - - namespace { -@@ -526,11 +522,6 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) { - } - #endif - --#if !BUILDFLAG(IS_NACL) -- // Connects Rust logging with the //base logging functionality. -- internal::init_rust_log_crate(); --#endif -- - // Ignore file options unless logging to file is set. - if ((g_logging_destination & LOG_TO_FILE) == 0) - return true; -diff --git a/base/logging/rust_log_integration.cc b/base/logging/rust_log_integration.cc -deleted file mode 100644 -index 1dba55d18a64a..0000000000000 ---- a/base/logging/rust_log_integration.cc -+++ /dev/null -@@ -1,24 +0,0 @@ --// Copyright 2024 The Chromium Authors --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "base/logging/rust_log_integration.h" -- --#include "base/logging.h" --#include "base/logging/log_severity.h" -- --namespace logging { --namespace internal { -- --BASE_EXPORT void print_rust_log(const char* msg, -- const char* file, -- int line, -- LogSeverity severity, -- bool verbose) { -- // TODO(danakj): If `verbose` make the log equivalent to VLOG instead of LOG. -- logging::LogMessage log_message(file, line, severity); -- log_message.stream() << msg; --} -- --} // namespace internal --} // namespace logging -diff --git a/base/logging/rust_log_integration.h b/base/logging/rust_log_integration.h -deleted file mode 100644 -index 1795950e18557..0000000000000 ---- a/base/logging/rust_log_integration.h -+++ /dev/null -@@ -1,29 +0,0 @@ --// Copyright 2024 The Chromium Authors --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef BASE_LOGGING_RUST_LOG_INTEGRATION_H_ --#define BASE_LOGGING_RUST_LOG_INTEGRATION_H_ -- --#include "base/base_export.h" --#include "base/logging/log_severity.h" -- --namespace logging { --namespace internal { -- --// Receives a log line from Rust and forwards it to base logging, because --// logging::LogMessage is not accessible from Rust yet with our current interop --// tools. --// --// TODO(danakj): Should this helper function be replaced with C-like apis next --// to logging::LogMessage that Rust uses more directly? --void BASE_EXPORT print_rust_log(const char* msg, -- const char* file, -- int line, -- LogSeverity severity, -- bool verbose); -- --} // namespace internal --} // namespace logging -- --#endif // BASE_LOGGING_RUST_LOG_INTEGRATION_H_ -diff --git a/base/logging/rust_log_integration_unittest.cc b/base/logging/rust_log_integration_unittest.cc -deleted file mode 100644 -index 8fc0fec7e1350..0000000000000 ---- a/base/logging/rust_log_integration_unittest.cc -+++ /dev/null -@@ -1,76 +0,0 @@ --// Copyright 2024 The Chromium Authors --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "base/logging.h" --#include "base/test/logging/test_rust_logger_consumer.rs.h" --#include "base/test/mock_log.h" -- --using testing::_; -- --namespace logging { --namespace { -- --class RustLogIntegrationTest : public testing::Test { -- public: -- void SetUp() override { log_.StartCapturingLogs(); } -- -- void TearDown() override { log_.StopCapturingLogs(); } -- -- base::test::MockLog log_; --}; -- --// TODO(crbug.com/374023535): Logging does not work in component builds. --#if defined(COMPONENT_BUILD) --#define MAYBE_CheckAllSeverity DISABLED_CheckAllSeverity --#else --#define MAYBE_CheckAllSeverity CheckAllSeverity --#endif --TEST_F(RustLogIntegrationTest, MAYBE_CheckAllSeverity) { --#if DCHECK_IS_ON() -- // Debug and Trace logs from Rust are discarded when DCHECK_IS_ON() is false; -- // otherwise, they are logged as info. -- EXPECT_CALL(log_, -- Log(LOGGING_INFO, _, _, _, testing::HasSubstr("test trace log"))) -- .WillOnce(testing::Return(true)); -- -- EXPECT_CALL(log_, -- Log(LOGGING_INFO, _, _, _, testing::HasSubstr("test debug log"))) -- .WillOnce(testing::Return(true)); --#endif -- -- EXPECT_CALL(log_, -- Log(LOGGING_INFO, _, _, _, testing::HasSubstr("test info log"))) -- .WillOnce(testing::Return(true)); -- -- EXPECT_CALL(log_, Log(LOGGING_WARNING, _, _, _, -- testing::HasSubstr("test warning log"))) -- .WillOnce(testing::Return(true)); -- -- EXPECT_CALL(log_, -- Log(LOGGING_ERROR, _, _, _, testing::HasSubstr("test error log"))) -- .WillOnce(testing::Return(true)); -- -- base::test::print_test_trace_log(); -- base::test::print_test_debug_log(); -- base::test::print_test_info_log(); -- base::test::print_test_warning_log(); -- base::test::print_test_error_log(); --} -- --// TODO(crbug.com/374023535): Logging does not work in component builds. --#if defined(COMPONENT_BUILD) --#define MAYBE_Placeholders DISABLED_Placeholders --#else --#define MAYBE_Placeholders Placeholders --#endif --TEST_F(RustLogIntegrationTest, MAYBE_Placeholders) { -- EXPECT_CALL(log_, Log(LOGGING_ERROR, _, _, _, -- testing::HasSubstr("test log with placeholder 2"))) -- .WillOnce(testing::Return(true)); -- -- base::test::print_test_error_log_with_placeholder(2); --} -- --} // namespace --} // namespace logging -diff --git a/base/logging/rust_logger.rs b/base/logging/rust_logger.rs -deleted file mode 100644 -index 918c1ce0f58bd..0000000000000 ---- a/base/logging/rust_logger.rs -+++ /dev/null -@@ -1,74 +0,0 @@ --// Copyright 2024 The Chromium Authors --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --chromium::import! { -- "//base:logging_log_severity_bindgen" as log_severity; -- "//base:logging_rust_log_integration_bindgen" as rust_log_integration; --} -- --use log::Level::{Debug, Error, Info, Trace, Warn}; --use log::{LevelFilter, Metadata, Record}; --use log_severity::logging::{LOGGING_ERROR, LOGGING_INFO, LOGGING_WARNING}; --use rust_log_integration::logging::internal::print_rust_log; --use std::ffi::CString; -- --struct RustLogger; -- --impl log::Log for RustLogger { -- fn enabled(&self, _metadata: &Metadata) -> bool { -- // Always enabled, as it's controlled by command line flags managed by the C++ -- // implementation. -- true -- } -- -- fn log(&self, record: &Record) { -- // TODO(thiruak1024@gmail.com): Rather than using heap allocation to pass |msg| -- // and |file|, we should return a pointer and size object to leverage the -- // string_view object in C++. https://crbug.com/371112531 -- let msg = match record.args().as_str() { -- Some(s) => CString::new(s), -- None => CString::new(&*record.args().to_string()), -- } -- .expect("CString::new failed to create the log message!"); -- let file = CString::new(record.file().unwrap()) -- .expect("CString::new failed to create the log file name!"); -- unsafe { -- print_rust_log( -- msg.as_ptr(), -- file.as_ptr(), -- record.line().unwrap() as i32, -- match record.metadata().level() { -- Error => LOGGING_ERROR, -- Warn => LOGGING_WARNING, -- Info => LOGGING_INFO, -- // Note that Debug and Trace level logs are dropped at -- // compile time at the macro call-site when DCHECK_IS_ON() -- // is false. This is done through a Cargo feature. -- Debug | Trace => LOGGING_INFO, -- }, -- record.metadata().level() == Trace, -- ) -- } -- } -- fn flush(&self) {} --} -- --static RUST_LOGGER: RustLogger = RustLogger; -- --#[cxx::bridge(namespace = "logging::internal")] --mod ffi { -- extern "Rust" { -- fn init_rust_log_crate(); -- } --} -- --pub fn init_rust_log_crate() { -- // An error may occur if set_logger has already been called, which can happen -- // during unit tests. In that case, return from the method without executing the -- // subsequent code. -- if let Err(_) = log::set_logger(&RUST_LOGGER) { -- return; -- }; -- log::set_max_level(LevelFilter::Trace); --} -diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn -index ddf58b1d496e0..77b571c31a31c 100644 ---- a/base/test/BUILD.gn -+++ b/base/test/BUILD.gn -@@ -7,7 +7,6 @@ import("//build/config/chromeos/ui_mode.gni") - import("//build/config/features.gni") - import("//build/config/nacl/config.gni") - import("//build/config/ui.gni") --import("//build/rust/rust_static_library.gni") - import("//build_overrides/build.gni") - import("//third_party/protobuf/proto_library.gni") - -@@ -39,17 +38,6 @@ static_library("test_config") { - ] - } - --rust_static_library("test_rust_logger_consumer") { -- allow_unsafe = true # Unsafe needed for FFI -- testonly = true -- deps = [ "//third_party/rust/log/v0_4:lib" ] -- sources = [ "logging/test_rust_logger_consumer.rs" ] -- -- crate_root = "logging/test_rust_logger_consumer.rs" -- -- cxx_bindings = [ "logging/test_rust_logger_consumer.rs" ] --} -- - static_library("test_support") { - testonly = true - sources = [ -@@ -189,7 +177,6 @@ static_library("test_support") { - - public_deps = [ - ":test_config", -- ":test_rust_logger_consumer", - "//base", - "//base:base_static", - "//base:i18n", -diff --git a/base/test/logging/test_rust_logger_consumer.rs b/base/test/logging/test_rust_logger_consumer.rs -deleted file mode 100644 -index dc8adc6b85f22..0000000000000 ---- a/base/test/logging/test_rust_logger_consumer.rs -+++ /dev/null -@@ -1,41 +0,0 @@ --// Copyright 2024 The Chromium Authors --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --use log::{debug, error, info, trace, warn}; -- --#[cxx::bridge(namespace = "base::test")] --mod ffi { -- extern "Rust" { -- fn print_test_info_log(); -- fn print_test_warning_log(); -- fn print_test_error_log(); -- fn print_test_debug_log(); -- fn print_test_trace_log(); -- fn print_test_error_log_with_placeholder(i: i32); -- } --} -- --pub fn print_test_info_log() { -- info!("test info log"); --} -- --pub fn print_test_warning_log() { -- warn!("test warning log"); --} -- --pub fn print_test_error_log() { -- error!("test error log"); --} -- --pub fn print_test_debug_log() { -- debug!("test debug log"); --} -- --pub fn print_test_trace_log() { -- trace!("test trace log"); --} -- --fn print_test_error_log_with_placeholder(i: i32) { -- error!("test log with placeholder {}", i); --} -diff --git a/third_party/rust/chromium_crates_io/gnrt_config.toml b/third_party/rust/chromium_crates_io/gnrt_config.toml -index 9f36dd0efc3d3..828786f06ff8c 100644 ---- a/third_party/rust/chromium_crates_io/gnrt_config.toml -+++ b/third_party/rust/chromium_crates_io/gnrt_config.toml -@@ -163,6 +163,7 @@ ban_features = [ - ] - - [crate.log] -+group = 'test' - - [crate.nom] - remove_deps = ['minimal-lexical'] -diff --git a/third_party/rust/log/v0_4/BUILD.gn b/third_party/rust/log/v0_4/BUILD.gn -index 6db643f2c446b..6e871e1b71808 100644 ---- a/third_party/rust/log/v0_4/BUILD.gn -+++ b/third_party/rust/log/v0_4/BUILD.gn -@@ -43,4 +43,6 @@ cargo_crate("lib") { - "release_max_level_info", - "std", - ] -+ -+ testonly = true - } diff --git a/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch b/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch new file mode 100644 index 000000000..f0991eb71 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch @@ -0,0 +1,333 @@ +From d71a0fc5b00c20492fbfd15a561548c2e40ae2d4 Mon Sep 17 00:00:00 2001 +From: Ariel D'Alessandro +Date: Thu, 17 Jul 2025 13:11:35 -0300 +Subject: [PATCH] Revert "Remove third_party/libavif" + +This reverts commit 16708cb7341629a2793a6a06a370e2b846313e56. + +In order to disable crabbyavif to fix build errors, re-enable libavif so +it can be used in replacement. + +Upstream-Status: Inappropriate [upstream ticket https://crbug.com/357017325] +Signed-off-by: Ariel D'Alessandro +--- + DEPS | 7 + + third_party/libavif/BUILD.gn | 172 +++++++++++++++++++ + third_party/libavif/DIR_METADATA | 6 + + third_party/libavif/OWNERS | 2 + + third_party/libavif/README.chromium | 13 ++ + third_party/libavif/avif_apps_shared_stubs.c | 50 ++++++ + third_party/libavif/src | 1 + + 7 files changed, 251 insertions(+) + create mode 100644 third_party/libavif/BUILD.gn + create mode 100644 third_party/libavif/DIR_METADATA + create mode 100644 third_party/libavif/OWNERS + create mode 100644 third_party/libavif/README.chromium + create mode 100644 third_party/libavif/avif_apps_shared_stubs.c + create mode 160000 third_party/libavif/src + +diff --git a/DEPS b/DEPS +index f527d6b0d4b78..3b31fee5d5d3a 100644 +--- a/DEPS ++++ b/DEPS +@@ -445,6 +445,10 @@ vars = { + # and whatever else without interference from each other. + 'wuffs_revision': 'e3f919ccfe3ef542cfc983a82146070258fb57f8', + # Three lines of non-changing comments so that ++ # the commit queue can handle CLs rolling libavif ++ # and whatever else without interference from each other. ++ 'libavif_revision': 'e7b34a1f5e9f7024d08311c7bae156061b889882', ++ # Three lines of non-changing comments so that + # the commit queue can handle CLs rolling crabbyavif + # and whatever else without interference from each other. + 'crabbyavif_revision': '02d0fad2c512380b7270d6e704c86521075d7d54', +@@ -2268,6 +2272,9 @@ deps = { + 'src/third_party/libaom/source/libaom': + Var('aomedia_git') + '/aom.git' + '@' + '9680f2b1781fb33b9eeb52409b75c679c8a954be', + ++ 'src/third_party/libavif/src': ++ Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'), ++ + 'src/third_party/crabbyavif/src': + Var('chromium_git') + '/external/github.com/webmproject/CrabbyAvif.git' + '@' + Var('crabbyavif_revision'), + +diff --git a/third_party/libavif/BUILD.gn b/third_party/libavif/BUILD.gn +new file mode 100644 +index 0000000000000..c2cc9e2d9fd87 +--- /dev/null ++++ b/third_party/libavif/BUILD.gn +@@ -0,0 +1,172 @@ ++# Copyright 2020 The Chromium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++import("//media/media_options.gni") ++import("//testing/libfuzzer/fuzzer_test.gni") ++ ++# Public configuration exported to users of the libavif target. ++config("avif_public_config") { ++ if (is_component_build) { ++ defines = [ "AVIF_DLL" ] ++ } ++} ++ ++# Private configuration used in building libavif. ++config("avif_config") { ++ include_dirs = [ "src/include/" ] ++ defines = [ "AVIF_LIBYUV_ENABLED" ] ++ if (is_component_build) { ++ defines += [ "AVIF_BUILDING_SHARED_LIBS" ] ++ } ++ ++ if (enable_dav1d_decoder) { ++ include_dirs += [ "../dav1d/libdav1d/include/" ] ++ defines += [ "AVIF_CODEC_DAV1D" ] ++ } ++} ++ ++libavif_decoder_sources = [ ++ "src/include/avif/internal.h", ++ "src/src/alpha.c", ++ "src/src/avif.c", ++ "src/src/colr.c", ++ "src/src/colrconvert.c", ++ "src/src/diag.c", ++ "src/src/exif.c", ++ "src/src/gainmap.c", ++ "src/src/io.c", ++ "src/src/mem.c", ++ "src/src/obu.c", ++ "src/src/properties.c", ++ "src/src/rawdata.c", ++ "src/src/read.c", ++ "src/src/reformat.c", ++ "src/src/reformat_libsharpyuv.c", ++ "src/src/reformat_libyuv.c", ++ "src/src/scale.c", ++ "src/src/stream.c", ++ "src/src/utils.c", ++] ++ ++component("libavif") { ++ public = [ "src/include/avif/avif.h" ] ++ public_configs = [ ":avif_public_config" ] ++ ++ sources = libavif_decoder_sources ++ ++ configs += [ ":avif_config" ] ++ ++ deps = [ "//third_party/libyuv" ] ++ ++ if (enable_dav1d_decoder) { ++ sources += [ "src/src/codec_dav1d.c" ] ++ deps += [ "//third_party/dav1d" ] ++ } ++} ++ ++# Note only the decoder fuzzers are enabled as only the decoder is being used ++# in Chrome. avif_fuzztest_read_image is not enabled due to libpng not having ++# PNG_READ_iTXt_SUPPORTED enabled. ++config("avif_fuzztest_config") { ++ include_dirs = [ ++ "src/include", ++ "src/apps/shared", ++ ] ++} ++ ++# This is used to satisfy dependencies in avif_fuzztest_helpers. The encoder ++# functions are not used. ++component("libavif_enc") { ++ public = [ "src/include/avif/avif.h" ] ++ public_configs = [ ":avif_public_config" ] ++ ++ sources = libavif_decoder_sources + [ ++ "src/src/write.c", ++ ] ++ testonly = true ++ ++ configs += [ ":avif_config" ] ++ ++ deps = [ ++ "//third_party/libwebp:libwebp_sharpyuv", ++ "//third_party/libyuv", ++ ] ++ defines = [ "AVIF_LIBSHARPYUV_ENABLED" ] ++ ++ if (enable_dav1d_decoder) { ++ sources += [ "src/src/codec_dav1d.c" ] ++ deps += [ "//third_party/dav1d" ] ++ } ++} ++ ++source_set("avif_apps_shared") { ++ sources = [ ++ "avif_apps_shared_stubs.c", ++ "src/apps/shared/avifexif.c", ++ "src/apps/shared/avifexif.h", ++ "src/apps/shared/avifjpeg.h", ++ "src/apps/shared/avifpng.h", ++ "src/apps/shared/avifutil.c", ++ "src/apps/shared/avifutil.h", ++ "src/apps/shared/y4m.c", ++ "src/apps/shared/y4m.h", ++ ] ++ testonly = true ++ configs += [ ":avif_fuzztest_config" ] ++ deps = [ ":libavif_enc" ] ++} ++ ++source_set("avif_fuzztest_helpers") { ++ sources = [ ++ "src/tests/gtest/avif_fuzztest_helpers.cc", ++ "src/tests/gtest/avifincrtest_helpers.cc", ++ "src/tests/gtest/aviftest_helpers.cc", ++ ] ++ testonly = true ++ configs += [ ":avif_fuzztest_config" ] ++ deps = [ ++ ":avif_apps_shared", ++ ":libavif_enc", ++ "//testing/gtest", ++ "//third_party/fuzztest:fuzztest", ++ ] ++} ++ ++# TODO: b/308013905 - These tests require seeds from ++# third_party/libavif/src/tests/data which ++# aren't available in the fuzzing environment. These targets can be enabled if ++# they are made hermetic. ++# ++# test("avif_fuzztest_dec") { ++# sources = [ "src/tests/gtest/avif_fuzztest_dec.cc" ] ++# fuzztests = [ "DecodeAvifTest.Decode" ] ++# configs += [ ":avif_fuzztest_config" ] ++# deps = [ ++# ":avif_fuzztest_helpers", ++# ":libavif_enc", ++# "//third_party/fuzztest:fuzztest_gtest_main", ++# ] ++# } ++# ++# test("avif_fuzztest_dec_incr") { ++# sources = [ "src/tests/gtest/avif_fuzztest_dec_incr.cc" ] ++# fuzztests = [ "DecodeAvifFuzzTest.DecodeIncr" ] ++# configs += [ ":avif_fuzztest_config" ] ++# deps = [ ++# ":avif_fuzztest_helpers", ++# ":libavif_enc", ++# "//third_party/fuzztest:fuzztest_gtest_main", ++# ] ++# } ++ ++test("avif_fuzztest_yuvrgb") { ++ sources = [ "src/tests/gtest/avif_fuzztest_yuvrgb.cc" ] ++ fuzztests = [ "YuvRgbFuzzTest.Convert" ] ++ configs += [ ":avif_fuzztest_config" ] ++ deps = [ ++ ":avif_fuzztest_helpers", ++ ":libavif_enc", ++ "//third_party/fuzztest:fuzztest_gtest_main", ++ ] ++} +diff --git a/third_party/libavif/DIR_METADATA b/third_party/libavif/DIR_METADATA +new file mode 100644 +index 0000000000000..beadfa62dc3ad +--- /dev/null ++++ b/third_party/libavif/DIR_METADATA +@@ -0,0 +1,6 @@ ++monorail: { ++ component: "Internals>Images>Codecs" ++} ++buganizer_public: { ++ component_id: 1456316 ++} +diff --git a/third_party/libavif/OWNERS b/third_party/libavif/OWNERS +new file mode 100644 +index 0000000000000..6946c0b7788b5 +--- /dev/null ++++ b/third_party/libavif/OWNERS +@@ -0,0 +1,2 @@ ++file://media/OWNERS ++wtc@google.com +diff --git a/third_party/libavif/README.chromium b/third_party/libavif/README.chromium +new file mode 100644 +index 0000000000000..81904b69c35c5 +--- /dev/null ++++ b/third_party/libavif/README.chromium +@@ -0,0 +1,13 @@ ++Name: libavif - Library for encoding and decoding .avif files ++Short Name: libavif ++URL: https://github.com/AOMediaCodec/libavif ++Version: N/A ++Revision: DEPS ++License: BSD-2-Clause, MIT ++License File: src/LICENSE ++Security Critical: yes ++Shipped: yes ++ ++Description: ++This contains the source to the AV1 image format demuxer; used for demuxing and ++decoding .avif files in Chromium. +diff --git a/third_party/libavif/avif_apps_shared_stubs.c b/third_party/libavif/avif_apps_shared_stubs.c +new file mode 100644 +index 0000000000000..18980cbc5b0b5 +--- /dev/null ++++ b/third_party/libavif/avif_apps_shared_stubs.c +@@ -0,0 +1,50 @@ ++// Copyright 2024 The Chromium Authors ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// This file provides functions necessary to link the decoder fuzz tests. The ++// functions are not used by the tests being built, but their dependencies in ++// apps/shared and avif_*_helpers.cc unconditionally reference them. ++ ++#include ++ ++#include "src/apps/shared/avifjpeg.h" ++#include "src/apps/shared/avifpng.h" ++ ++avifBool avifJPEGRead(const char * inputFilename, ++ avifImage * avif, ++ avifPixelFormat requestedFormat, ++ uint32_t requestedDepth, ++ avifChromaDownsampling chromaDownsampling, ++ avifBool ignoreColorProfile, ++ avifBool ignoreExif, ++ avifBool ignoreXMP, ++ avifBool ignoreGainMap, ++ uint32_t imageSizeLimit) { ++ fprintf(stderr, "The tests were built without JPEG support!\n"); ++ return AVIF_FALSE; ++} ++ ++avifBool avifPNGRead(const char * inputFilename, ++ avifImage * avif, ++ avifPixelFormat requestedFormat, ++ uint32_t requestedDepth, ++ avifChromaDownsampling chromaDownsampling, ++ avifBool ignoreColorProfile, ++ avifBool ignoreExif, ++ avifBool ignoreXMP, ++ avifBool allowChangingCicp, ++ uint32_t imageSizeLimit, ++ uint32_t * outPNGDepth) { ++ fprintf(stderr, "The tests were built without PNG support!\n"); ++ return AVIF_FALSE; ++} ++ ++avifBool avifPNGWrite(const char * outputFilename, ++ const avifImage * avif, ++ uint32_t requestedDepth, ++ avifChromaUpsampling chromaUpsampling, ++ int compressionLevel) { ++ fprintf(stderr, "The tests were built without PNG support!\n"); ++ return AVIF_FALSE; ++} +diff --git a/third_party/libavif/src b/third_party/libavif/src +new file mode 160000 +index 0000000000000..e7b34a1f5e9f7 +--- /dev/null ++++ b/third_party/libavif/src +@@ -0,0 +1 @@ ++Subproject commit e7b34a1f5e9f7024d08311c7bae156061b889882 diff --git a/meta-chromium/recipes-browser/chromium/files/0011-Disable-crabbyavif-to-fix-build-errors.patch b/meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch similarity index 100% rename from meta-chromium/recipes-browser/chromium/files/0011-Disable-crabbyavif-to-fix-build-errors.patch rename to meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch diff --git a/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch b/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch new file mode 100644 index 000000000..6cae0abc0 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch @@ -0,0 +1,38 @@ +From d1d686cb5cebca571cb9eafc5a3cabff6971ddb6 Mon Sep 17 00:00:00 2001 +From: Ariel D'Alessandro +Date: Sat, 26 Apr 2025 17:19:24 -0300 +Subject: [PATCH] rust: Use adler instead of adler2 + +Upstream Rust replaced adler with adler2, for older versions of Rust we still +need to tell GN that we have the older lib when it tries to copy the Rust +sysroot into the bulid directory. + +It looks that in rust-1.85.0, `adler` has not yet been replaced with `adler2`. +Therefore, the condition should likely be adjusted. + +This patch will be dropped once Rust >= 1.86.0 is available, which +replaced adler with adler2. + +Note that currently layer provides Rust version `1.85.1`. + +[0] https://git.yoctoproject.org/meta-lts-mixins/log/?h=scarthgap/rust + +Upstream-Status: Inappropriate [specific to older versions of rust] +Signed-off-by: Ariel D'Alessandro +--- + build/rust/std/BUILD.gn | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn +index 7f64198b9f..d29e3466b1 100644 +--- a/build/rust/std/BUILD.gn ++++ b/build/rust/std/BUILD.gn +@@ -89,7 +89,7 @@ if (toolchain_has_rust) { + # These are no longer present in the Windows toolchain. + stdlib_files += [ + "addr2line", +- "adler2", ++ "adler", + "gimli", + "libc", + "memchr", diff --git a/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch b/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch new file mode 100644 index 000000000..30a9206b9 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch @@ -0,0 +1,27 @@ +From 6e3d91964a64dbacfddc4c7a38c64fd221fcdd2b Mon Sep 17 00:00:00 2001 +From: Ariel D'Alessandro +Date: Wed, 16 Jul 2025 18:44:44 -0300 +Subject: [PATCH] update_node_binaries: Update nodejs version to v22.15.0 + +Update the NodeJS version to match exactly the one shipped in these +images. + +Upstream-Status: Inappropriate [specific to our build setup] +Signed-off-by: Ariel D'Alessandro +--- + third_party/node/update_node_binaries | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/third_party/node/update_node_binaries b/third_party/node/update_node_binaries +index 4aab805844..5c3714f574 100755 +--- a/third_party/node/update_node_binaries ++++ b/third_party/node/update_node_binaries +@@ -19,7 +19,7 @@ set -eu + cd "$(dirname "$0")" + + BASE_URL="https://nodejs.org/dist" +-NODE_VERSION="v22.11.0" ++NODE_VERSION="v22.15.0" + + upload=false # Default value + diff --git a/meta-chromium/recipes-browser/chromium/gn-native_132.0.6834.83.bb b/meta-chromium/recipes-browser/chromium/gn-native_136.0.7103.113.bb similarity index 100% rename from meta-chromium/recipes-browser/chromium/gn-native_132.0.6834.83.bb rename to meta-chromium/recipes-browser/chromium/gn-native_136.0.7103.113.bb From 052a95515ade812ff272fab39fbb64451e0aa069 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Nov 2023 00:21:12 +0100 Subject: [PATCH 04/15] chromium: Add v4l2 m2m stateless decode configuration options Add configuration options to enable hardware video decoding using stateless V4L2 M2M device. This allows offloading e.g. h264 video playback to Hantro VPU on i.MX8MP where this was tested. To make that work, enable 'use-v4l2' and 'proprietary-codecs' PACKAGECONFIG. This commit was implemented with much great help from Jianfeng Liu . Signed-off-by: Marek Vasut --- meta-chromium/recipes-browser/chromium/chromium-gn.inc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index 27c9047d5..38e93d7d2 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -124,7 +124,7 @@ BUILD_CC:toolchain-clang = "clang" BUILD_CXX:toolchain-clang = "clang++" BUILD_LD:toolchain-clang = "clang" -PACKAGECONFIG ??= "upower use-egl" +PACKAGECONFIG ??= "upower use-egl use-v4l2" # this makes sure the dependencies for the EGL mode are present; otherwise, the configure scripts # automatically and silently fall back to GLX @@ -158,6 +158,10 @@ PACKAGECONFIG[upower] = ",,,upower" # but remember to also use proprietary codecs so that H.264 is supported. Also note # that not all the hardware configs might be supported. PACKAGECONFIG[use-vaapi] = "use_vaapi=true use_libgav1_parser=true,use_vaapi=false,libva" +# Enable stateless V4L2 M2M video decoding support. +# This requires 'proprietary-codecs' PACKAGECONFIG +# to decode h264 streams on the V4L2 M2M device. +PACKAGECONFIG[use-v4l2] = "use_v4l2_codec=true,use_v4l2_codec=false" # Base GN arguments, mostly related to features we want to enable or disable. GN_ARGS = " \ @@ -382,6 +386,7 @@ CHROMIUM_EXTRA_ARGS ?= " \ ${@bb.utils.contains('PACKAGECONFIG', 'use-egl', '--use-angle=gles-egl', '', d)} \ ${@bb.utils.contains('PACKAGECONFIG', 'kiosk-mode', '--kiosk --no-first-run --incognito', '', d)} \ ${@bb.utils.contains('PACKAGECONFIG', 'gtk4', '--gtk-version=4', '', d)} \ + ${@bb.utils.contains('PACKAGECONFIG', 'use-v4l2', '--ozone-platform-hint=wayland --enable-features=AcceleratedVideoDecoder,AcceleratedVideoDecodeLinuxGL,AcceleratedVideoDecodeLinuxZeroCopyGL', '', d)} \ " # V8's JIT infrastructure requires binaries such as mksnapshot and From 02315296eeeaa21b5ecd83a33ee642314e9d47ea Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Thu, 24 Jul 2025 17:44:15 +0100 Subject: [PATCH 05/15] actions: Use meta-chromium-test chromium136-master branch OSSystems tested Chromium136 with following repo hashes: meta-oe: commit: "a8dfd10600035a799abae03178fc7054582ea43d" poky: commit: "243d54fd466f5f852cc0fdcce57997918ce35f32" meta-clang: commit: "7a2f83360920b10214e2659e17a4b9cba2d0435b" chromium136-master branch of meta-chromium-test uses these hashes. --- .github/workflows/chromium.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index a7e045a89..71693aaed 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -54,5 +54,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone -b walnascar https://github.com/brightsign/meta-chromium-test.git - ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} \ No newline at end of file + git clone -b chromium136-master https://github.com/brightsign/meta-chromium-test.git + ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} From bbc07c2645e71f43d5d3c093f0bb64bed207015e Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Fri, 25 Jul 2025 09:11:38 +0100 Subject: [PATCH 06/15] fix: Fix compilation error with neon enabled arm target rust triple is not specialized in yocto for neon enabled arm targets. --- .../files/0009-Adjust-the-Rust-build-to-our-needs.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch index d25447620..391c5cda7 100644 --- a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch +++ b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch @@ -68,7 +68,7 @@ index 74b3b9ced5..5affd0b445 100644 # https://github.com/rust-lang/rust/issues/44722 if (arm_use_neon) { - rust_abi_target = "thumbv7neon-unknown-linux-gnueabi" + float_suffix -+ rust_abi_target = "thumbv7neon" + vendor + "-linux-gnueabi" + float_suffix ++ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix } else { - rust_abi_target = "armv7-unknown-linux-gnueabi" + float_suffix + rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix From a00ef74eeac2d8aecd214ad5e8fcb076055f9da7 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Fri, 25 Jul 2025 13:09:06 +0100 Subject: [PATCH 07/15] chromium; Fix rust compile error with arm targets Filters out unwanted clang flags from rust compiler invocation. --- .../recipes-browser/chromium/chromium-gn.inc | 1 + ...arm-specific-flags-from-rust-compile.patch | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index 38e93d7d2..80932d2b4 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -33,6 +33,7 @@ SRC_URI += "\ file://0016-Disable-crabbyavif-to-fix-build-errors.patch \ file://0017-rust-Use-adler-instead-of-adler2.patch \ file://0018-third_party-node-update_node_binaries-Update-nodejs-.patch \ + file://0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch \ " # Missing third_party sources. diff --git a/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch b/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch new file mode 100644 index 000000000..881863306 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch @@ -0,0 +1,29 @@ +From f29addf315e55f371c7c9a973002607a628ea3ea Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Fri, 25 Jul 2025 13:00:14 +0100 +Subject: [PATCH 19/19] rust: filter out arm specific flags from rust compiler + +Upstream-Status: Inappropriate [specific to our build setup] +--- + build/rust/filter_clang_args.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/build/rust/filter_clang_args.py b/build/rust/filter_clang_args.py +index 5a1843c0df..7fb4b49516 100644 +--- a/build/rust/filter_clang_args.py ++++ b/build/rust/filter_clang_args.py +@@ -24,6 +24,11 @@ def filter_clang_args(clangargs): + pass + elif args[i].startswith('-plugin-arg'): + i += 2 ++ # Filter out ARM-specific flags that aren't supported on other architectures ++ elif args[i].startswith('-mbranch-protection'): ++ pass # Skip this argument ++ elif args[i].startswith('-mfpu='): ++ pass + else: + yield args[i] + i += 1 +-- +2.39.5 + From 97f9c85ac3a08b950667376e2b8b0100effd86a2 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Sat, 26 Jul 2025 12:51:27 +0100 Subject: [PATCH 08/15] chromium: fix v4l2 compiler warning error on arm --- .../recipes-browser/chromium/chromium-gn.inc | 1 + ...omium-fix-v4l2-compiler-error-on-arm.patch | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index 80932d2b4..70a5c92d2 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -34,6 +34,7 @@ SRC_URI += "\ file://0017-rust-Use-adler-instead-of-adler2.patch \ file://0018-third_party-node-update_node_binaries-Update-nodejs-.patch \ file://0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch \ + file://0020-chromium-fix-v4l2-compiler-error-on-arm.patch \ " # Missing third_party sources. diff --git a/meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch b/meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch new file mode 100644 index 000000000..26823502e --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch @@ -0,0 +1,31 @@ +From 568dd0026ac86878bf6f0c99ac837a964525860f Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Sat, 26 Jul 2025 12:47:57 +0100 +Subject: [PATCH 20/20] chromium: fix v4l2 compiler error on arm + +fixes: +../../media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc:450:20: +error: non-constant-expression cannot be narrowed from type '__suseconds64_t' +(aka 'long long') to 'long' in initializer list [-Wc++11-narrowing] + +Upstream-Status: Inappropriate [specific to our build setup] +--- + media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc b/media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc +index b77f2b08d7..9069b97c02 100644 +--- a/media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc ++++ b/media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc +@@ -447,7 +447,7 @@ void V4L2StatefulVideoDecoderBackend::OnOutputBufferDequeued( + const struct timeval timeval = buffer->GetTimeStamp(); + const struct timespec timespec = { + .tv_sec = timeval.tv_sec, +- .tv_nsec = timeval.tv_usec * 1000, ++ .tv_nsec = static_cast(timeval.tv_usec * 1000), + }; + + const int64_t flat_timespec = +-- +2.39.5 + From 5950511d49f079510546d9d12cf770ce2f0243bf Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 15 Jul 2025 11:38:53 +0100 Subject: [PATCH 09/15] chromium: Update to 138.0.7204.35 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chromium 138+ uses its bundled libc++ and compiler-rt. To ensure the build system uses these and does not fall back to system-provided runtime libraries, explicitly pass the target triple and disable system stdlib++. This avoids linker errors caused by missing crtbeginS.o, crtendS.o, and libgcc when using Clang with a minimal sysroot. Release Notes: https://chromereleases.googleblog.com/2025/07/stable-channel-update-for-desktop_15.html Build and patch changes: Added patches: 0011-fix-check_version-Only-compare-node.js-major-version.patch Updated patches: 0009-Adjust-the-Rust-build-to-our-needs.patch Removed patches: 0011-Disable-crabbyavif-to-fix-build-errors.patch 0012-Revert-Allow-and-use-std-hardware_destructive_interf.patch 0013-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch 0014-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch 0015-Revert-Connect-the-Rust-log-crate-to-the-base-loggin.patch License changes: Added licenses: third_party/compiler-rt/src/LICENSE.TXT third_party/libtess2/LICENSE third_party/llvm-libc/src/LICENSE.TXT third_party/net/third_party/mozilla_security_manager/LICENSE Removed licenses: third_party/accessibility-audit/LICENSE third_party/io_grpc_grpc_api/LICENSE third_party/libavifinfo/LICENSE Updated licenses: third_party/android_deps/libs/com_google_android_gms_play_services_auth/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth/LICENSE third_party/android_deps/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE third_party/android_deps/libs/com_google_android_gms_play_services_auth_base/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_base/LICENSE third_party/android_deps/libs/com_google_android_gms_play_services_auth_blockstore/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_blockstore/LICENSE third_party/android_deps/libs/com_google_android_gms_play_services_fido/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_fido/LICENSE third_party/android_deps/libs/com_google_android_libraries_identity_googleid_googleid/LICENSE → third_party/android_deps/autorolled/committed/libs/com_google_android_libraries_identity_googleid_googleid/LICENSE third_party/expat/src/expat/COPYING third_party/libjpeg_turbo/LICENSE Test-built chromium-ozone-wayland master, clang, MACHINE=qemux86-64 chromium-x11 master, clang, MACHINE=qemux86-64, qemuarm, qemuarm64 Repo hashes (from meta-chromium-test) meta-oe: commit: "a8dfd10600035a799abae03178fc7054582ea43d" poky: commit: "243d54fd466f5f852cc0fdcce57997918ce35f32" meta-clang: commit: "7a2f83360920b10214e2659e17a4b9cba2d0435b" --- .github/workflows/chromium.yml | 2 +- meta-chromium/conf/layer.conf | 2 +- .../recipes-browser/chromium/chromium-gn.inc | 79 +- ... chromium-ozone-wayland_138.0.7204.157.bb} | 0 ....113.bb => chromium-x11_138.0.7204.157.bb} | 0 .../recipes-browser/chromium/chromium.inc | 87 +- ...ompiler-settings-conflicting-with-OE.patch | 29 +- .../chromium/files/0002-v8-qemu-wrapper.patch | 26 +- .../files/0003-wrapper-extra-flags.patch | 10 +- ...options-not-available-in-release-ver.patch | 26 +- ...ink-latomic-failure-on-CentOS-8-host.patch | 14 +- ...0006-Don-t-pass-unknown-LLVM-options.patch | 13 +- ...riable-must-be-initialized-by-a-cons.patch | 11 +- ...rrect-path-to-libclang_rt.builtins.a.patch | 18 +- ...9-Adjust-the-Rust-build-to-our-needs.patch | 110 +- ...Don-t-require-profiler_builtins.rlib.patch | 11 +- ...-use-std-hardware_destructive_interf.patch | 116 - ...n-Only-compare-node.js-major-version.patch | 42 + ...symbol-visibility-to-hidden-when-C-s.patch | 73 - ...mium-fix-v4l2-compiler-error-on-arm.patch} | 4 +- ...ng-PDFiumAPIStringBufferAdapter-temp.patch | 50 - ...rt-Remove-libavif-based-AVIF-decoder.patch | 3744 ----------------- ...15-Revert-Remove-third_party-libavif.patch | 333 -- ...sable-crabbyavif-to-fix-build-errors.patch | 97 - ...017-rust-Use-adler-instead-of-adler2.patch | 38 - ...-update_node_binaries-Update-nodejs-.patch | 27 - ...arm-specific-flags-from-rust-compile.patch | 29 - ...103.113.bb => gn-native_138.0.7204.157.bb} | 2 +- 28 files changed, 299 insertions(+), 4694 deletions(-) rename meta-chromium/recipes-browser/chromium/{chromium-ozone-wayland_136.0.7103.113.bb => chromium-ozone-wayland_138.0.7204.157.bb} (100%) rename meta-chromium/recipes-browser/chromium/{chromium-x11_136.0.7103.113.bb => chromium-x11_138.0.7204.157.bb} (100%) delete mode 100644 meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/0011-fix-check_version-Only-compare-node.js-major-version.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch rename meta-chromium/recipes-browser/chromium/files/{0020-chromium-fix-v4l2-compiler-error-on-arm.patch => 0012-chromium-fix-v4l2-compiler-error-on-arm.patch} (90%) delete mode 100644 meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch delete mode 100644 meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch rename meta-chromium/recipes-browser/chromium/{gn-native_136.0.7103.113.bb => gn-native_138.0.7204.157.bb} (97%) diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 71693aaed..2add96351 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -54,5 +54,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone -b chromium136-master https://github.com/brightsign/meta-chromium-test.git + git clone -b main https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} diff --git a/meta-chromium/conf/layer.conf b/meta-chromium/conf/layer.conf index d2de2af72..1ddabff3b 100644 --- a/meta-chromium/conf/layer.conf +++ b/meta-chromium/conf/layer.conf @@ -9,6 +9,6 @@ BBFILE_PATTERN_chromium-browser-layer := "^${LAYERDIR}/" BBFILE_PRIORITY_chromium-browser-layer = "7" LAYERVERSION_chromium-browser-layer = "1" -LAYERSERIES_COMPAT_chromium-browser-layer = "scarthgap styhead walnascar" +LAYERSERIES_COMPAT_chromium-browser-layer = "scarthgap styhead walnascar whinlatter" LAYERDEPENDS_chromium-browser-layer = "clang-layer core openembedded-layer" diff --git a/meta-chromium/recipes-browser/chromium/chromium-gn.inc b/meta-chromium/recipes-browser/chromium/chromium-gn.inc index 70a5c92d2..f6ab2a839 100644 --- a/meta-chromium/recipes-browser/chromium/chromium-gn.inc +++ b/meta-chromium/recipes-browser/chromium/chromium-gn.inc @@ -25,26 +25,10 @@ SRC_URI += "\ file://0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch \ file://0009-Adjust-the-Rust-build-to-our-needs.patch \ file://0010-Don-t-require-profiler_builtins.rlib.patch \ - file://0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch \ - file://0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch \ - file://0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch \ - file://0014-Revert-Remove-libavif-based-AVIF-decoder.patch \ - file://0015-Revert-Remove-third_party-libavif.patch \ - file://0016-Disable-crabbyavif-to-fix-build-errors.patch \ - file://0017-rust-Use-adler-instead-of-adler2.patch \ - file://0018-third_party-node-update_node_binaries-Update-nodejs-.patch \ - file://0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch \ - file://0020-chromium-fix-v4l2-compiler-error-on-arm.patch \ + file://0011-fix-check_version-Only-compare-node.js-major-version.patch \ + file://0012-chromium-fix-v4l2-compiler-error-on-arm.patch \ " -# Missing third_party sources. -SRC_URI += "\ - git://chromium.googlesource.com/external/github.com/AOMediaCodec/libavif;protocol=https;branch=main;name=libavif;destsuffix=third_party/libavif/src \ -" - -SRCREV_FORMAT .= "_libavif" -SRCREV_libavif = "e7b34a1f5e9f7024d08311c7bae156061b889882" - # ARM/AArch64-specific patches. SRC_URI:append:aarch64 = "${@bb.utils.contains('TUNE_FEATURES', 'crypto', '', ' file://arm/0001-Fix-AES-crypto-SIGILL-on-rpi4-64.patch', d)}" @@ -138,10 +122,10 @@ PACKAGECONFIG[use-egl] = ",,virtual/egl virtual/libgles2" # be necessary but are OK to add). PACKAGECONFIG[component-build] = "" -# Starting with M61, Chromium defaults to building with its own copy of libc++ -# instead of the system's libstdc++. Add a knob to control this behavior -# https://groups.google.com/a/chromium.org/d/msg/chromium-packagers/8aYO3me2SCE/SZ8pJXhZAwAJ -PACKAGECONFIG[custom-libcxx] = "use_custom_libcxx=true,use_custom_libcxx=false,," +# As of Chromium 138, libc++ is required unconditionally. +# https://docs.google.com/document/d/193ig0qeM23rK1yH6bW14O3zIgVztyKaY4R1XRwgmUFk/edit?usp=sharing +# Force use_custom_libcxx=true as Chromium 138+ requires libc++ +GN_ARGS += "use_custom_libcxx=true" PACKAGECONFIG[cups] = "use_cups=true,use_cups=false,cups" PACKAGECONFIG[gtk4] = "" @@ -280,6 +264,10 @@ GN_ARGS += "disable_fieldtrial_testing_config=true" # See https://crrev.com/c/2424669 GN_ARGS += "chrome_pgo_phase=0" +# Disable passing --warning-suppression-mappings= flag to clang. +# This feature is available on Clang21+ +GN_ARGS += "clang_warning_suppression_file=""" + # API keys for accessing Google services. By default, we use an invalid key # only to prevent the "you are missing an API key" infobar from being shown on # startup. @@ -299,6 +287,9 @@ GN_ARGS += ' \ BUILD_CPPFLAGS:append:runtime-llvm = " -isysroot=${STAGING_DIR_NATIVE} -stdlib=libc++" # Use libgcc for native parts BUILD_LDFLAGS:append:runtime-llvm = " -rtlib=libgcc -unwindlib=libgcc -stdlib=libc++ -lc++abi -rpath ${STAGING_LIBDIR_NATIVE}" +LDFLAGS:append:runtime-llvm = " --target=${TARGET_SYS}" +CXXFLAGS:append:runtime-llvm = " --target=${TARGET_SYS} -nostdlib++" +CFLAGS:append:runtime-llvm = " --target=${TARGET_SYS}" # Toolchains we will use for the build. We need to point to the toolchain file # we've created, set the right target architecture and make sure we are not @@ -366,15 +357,16 @@ ARM_VERSION:armv6 = "6" # overriding what GN wants TUNE_CCARGS:remove = "-mthumb" +# Linking rust with partitionalloc is problematic in arm32 GN_ARGS:append:arm = ' \ arm_float_abi="${ARM_FLOAT_ABI}" \ arm_fpu="${ARM_FPU}" \ arm_tune="${ARM_TUNE}" \ arm_version=${ARM_VERSION} \ + use_partition_alloc_as_malloc=false \ + enable_backup_ref_ptr_support=false \ ' -# tcmalloc's atomicops-internals-arm-v6plus.h uses the "dmb" instruction that -# is not available on (some?) ARMv6 models, which causes the build to fail. -GN_ARGS:append:armv6 = ' use_partition_alloc_as_malloc=false enable_backup_ref_ptr_support=false' + # The WebRTC code fails to build on ARMv6 when NEON is enabled. # https://bugs.chromium.org/p/webrtc/issues/detail?id=6574 GN_ARGS:append:armv6 = ' arm_use_neon=false' @@ -446,6 +438,7 @@ python do_write_toolchain_file () { toolchain_file = os.path.join(toolchain_dir, "BUILD.gn") write_toolchain_file(d, toolchain_file) } + addtask write_toolchain_file after do_patch before do_configure do_add_nodejs_symlink () { @@ -467,16 +460,34 @@ do_add_clang_latest () { # directory that is a copy of /usr/lib/clang/$CLANG_VERSION. Chromium # manually links against libclang_rt.builtins.a and uses the `clang_version` # GN variable to find it. This allows us to set it to the same value for all - # Yocto releases. + # Yocto releases. Also copy headers so bindgen can find system headers. cd "${STAGING_LIBDIR_NATIVE}/clang" rm -rf latest - # find the directory containing the library + + # Find a directory with runtime libraries and copy it as base for dir in *; do - if [ -n "$(find $dir -name 'libclang_rt.builtins*')" ] ; then + if [ -d "$dir" ] && [ -n "$(find $dir -name 'libclang_rt.builtins*' 2>/dev/null)" ]; then + echo "Copying clang directory $dir to latest (runtime libraries)" cp -r "$dir" latest break fi done + + # Find and copy headers from any versioned directory to ensure bindgen can find them + headers_copied=false + for dir in *; do + if [ -d "$dir/include" ]; then + echo "Found headers in $dir/include, copying to latest/include" + mkdir -p latest/include + cp -r "$dir/include/"* latest/include/ + headers_copied=true + break + fi + done + + if [ "$headers_copied" = false ]; then + echo "Warning: No Clang headers found in any versioned directories" + fi } addtask add_clang_latest after do_configure before do_compile @@ -501,15 +512,15 @@ do_copy_target_rustlibs () { } addtask copy_target_rustlibs after do_configure before do_compile -do_copy_missing_third_party_sources () { - rm -rf ${S}/third_party/libavif/src - cp -r ${UNPACKDIR}/third_party/libavif/src/ ${S}/third_party/libavif/ -} -addtask copy_missing_third_party_sources after do_patch before do_configure - do_configure() { cd ${S} python3 ./build/linux/unbundle/replace_gn_files.py --system-libraries ${GN_UNBUNDLE_LIBS} + + # Add Rust-style target triples (converted by Yocto's rust-common.bbclass) + grep -qxF "${RUST_TARGET_SYS}" ${S}/build/rust/known-target-triples.txt || echo "${RUST_TARGET_SYS}" >> ${S}/build/rust/known-target-triples.txt + grep -qxF "${RUST_HOST_SYS}" ${S}/build/rust/known-target-triples.txt || echo "${RUST_HOST_SYS}" >> ${S}/build/rust/known-target-triples.txt + grep -qxF "${RUST_BUILD_SYS}" ${S}/build/rust/known-target-triples.txt || echo "${RUST_BUILD_SYS}" >> ${S}/build/rust/known-target-triples.txt + gn gen --args='${GN_ARGS}' "${OUTPUT_DIR}" } diff --git a/meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_136.0.7103.113.bb b/meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_138.0.7204.157.bb similarity index 100% rename from meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_136.0.7103.113.bb rename to meta-chromium/recipes-browser/chromium/chromium-ozone-wayland_138.0.7204.157.bb diff --git a/meta-chromium/recipes-browser/chromium/chromium-x11_136.0.7103.113.bb b/meta-chromium/recipes-browser/chromium/chromium-x11_138.0.7204.157.bb similarity index 100% rename from meta-chromium/recipes-browser/chromium/chromium-x11_136.0.7103.113.bb rename to meta-chromium/recipes-browser/chromium/chromium-x11_138.0.7204.157.bb diff --git a/meta-chromium/recipes-browser/chromium/chromium.inc b/meta-chromium/recipes-browser/chromium/chromium.inc index e2a4f585f..db9e29d1d 100644 --- a/meta-chromium/recipes-browser/chromium/chromium.inc +++ b/meta-chromium/recipes-browser/chromium/chromium.inc @@ -4,9 +4,8 @@ HOMEPAGE = "https://www.chromium.org/Home" CVE_PRODUCT = "chromium:chromium google:chrome" SRC_URI = "https://commondatastorage.googleapis.com/chromium-browser-official/chromium-${PV}.tar.xz" -SRC_URI[sha256sum] = "7c765bd13df842a28bb52279b8d711411ac6082151473e07bd70b9a482c0a0ac" - -S = "${WORKDIR}/chromium-${PV}" +SRC_URI[sha256sum] = "835dfd3228f6adb2a8c78e296c7d4981aee35daee401a2fe1493b4657d736d25" +S = "${UNPACKDIR}/chromium-${PV}" # GCC is not tested or officially supported upstream, and supporting it here # requires an ever-growing amount of backports and custom patches, without any @@ -71,6 +70,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/base/third_party/symbolize/LICENSE;md5=17ae3b22fe8fa438966625593e2eea85 \ file://${S}/base/third_party/xdg_user_dirs/LICENSE;md5=d998f250c491c329a8254dd1ca62c647 \ file://${S}/chrome/browser/resources/chromeos/accessibility/chromevox/mv2/third_party/tamachiyomi/LICENSE;md5=15772cfcf7016e701ce54554516c0688 \ + file://${S}/chrome/browser/resources/chromeos/accessibility/chromevox/mv3/third_party/tamachiyomi/LICENSE;md5=15772cfcf7016e701ce54554516c0688 \ file://${S}/chrome/installer/mac/third_party/bsdiff/LICENSE;md5=0dbe7a50f028269750631fcbded3846a \ file://${S}/chrome/installer/mac/third_party/xz/LICENSE;md5=84982e6bf3ed99ef2647e48626ffa984 \ file://${S}/chrome/third_party/mozilla_security_manager/LICENSE;md5=0c259b853bbf067b361100ce560adce7 \ @@ -91,48 +91,54 @@ LIC_FILES_CHKSUM = "\ file://${S}/net/third_party/quiche/src/LICENSE;md5=0fca02217a5d49a14dfe2d11837bb34d \ file://${S}/net/third_party/uri_template/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/abseil-cpp/LICENSE;md5=df52c6edb7adc22e533b2bacc3bd3915 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_datatransport_transport_api/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_datatransport_transport_backend_cct/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_datatransport_transport_runtime/LICENSE;md5=54d2f6e66dae1f9e4dbf3cb3cc197631 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth/LICENSE;md5=e207fc5a134660990a7068d5a02c7ea8 \ - file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_api_phone/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_base/LICENSE;md5=8b3fee5c5451b70decc3ea70f7bc0024 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_auth_blockstore/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ - file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_base/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_base/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_basement/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ - file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_fido/LICENSE;md5=ff76e1f91bd64c4593743be9d83a4c5f \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_cast/LICENSE;md5=57bc9b01373a90210affff003e590c19 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_cast_framework/LICENSE;md5=6e5955fc74e2abf3e5ca0bcb3878f837 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_clearcut/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_fido/LICENSE;md5=0b31499076a79957f503e7861250586c \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_flags/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_gcm/LICENSE;md5=92b728c587a67a1b9577e9ca141ca520 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_identity_credentials/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ - file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_tasks/LICENSE;md5=e5a8d882f7945961271786002e6a581d \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_iid/LICENSE;md5=05fc50851d6d550ef2bb400b326353f0 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_instantapps/LICENSE;md5=cd033bdb088af16ecdbcd95bd5562a30 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_location/LICENSE;md5=18ad89b4a01838496ae844c2b9bd2a7f \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_phenotype/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_stats/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_tasks/LICENSE;md5=4a37c94d22ef29d62102bef4fc7bd8b0 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_vision/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_gms_play_services_vision_common/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_libraries_identity_googleid_googleid/LICENSE;md5=498e555b4ebef5399d944fb662c61913 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_material_material/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_play_core_common/LICENSE;md5=73c6700ef062ad41c3d27cab65d0c1e2 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_android_play_feature_delivery/LICENSE;md5=014a1ef6f09b1a5ac887d86d5f18918d \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_auto_service_auto_service_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_code_findbugs_jsr305/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_firebase_firebase_encoders/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_firebase_firebase_encoders_json/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_firebase_firebase_encoders_proto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_guava_failureaccess/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_guava_guava/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_j2objc_j2objc_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/com_google_protobuf_protobuf_javalite/LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b \ file://${S}/third_party/android_deps/autorolled/committed/libs/com_squareup_okio_okio_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/javax_inject_javax_inject/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/org_checkerframework_checker_qual/LICENSE;md5=f32f668c1f4eea36fb53fc4cc7d96385 \ file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_play_services/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_serialization_core_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/autorolled/committed/libs/org_jspecify_jspecify/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_android_support_support_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_api/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ - file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_backend_cct/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ - file://${S}/third_party/android_deps/libs/com_google_android_datatransport_transport_runtime/LICENSE;md5=54d2f6e66dae1f9e4dbf3cb3cc197631 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast/LICENSE;md5=d4ace05266b66c1c0ccdeba11bbe745e \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cast_framework/LICENSE;md5=98aafe35230de6fc18e977f2feda5671 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_clearcut/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_cloud_messaging/LICENSE;md5=fff98c651a1ae8a04462a12f8ea1f7a4 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_flags/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_gcm/LICENSE;md5=92b728c587a67a1b9577e9ca141ca520 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_iid/LICENSE;md5=05fc50851d6d550ef2bb400b326353f0 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_instantapps/LICENSE;md5=cd033bdb088af16ecdbcd95bd5562a30 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_location/LICENSE;md5=18ad89b4a01838496ae844c2b9bd2a7f \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_phenotype/LICENSE;md5=32c019d8c1f8222a8aad9c3e1f1d2d10 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_stats/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_vision/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ - file://${S}/third_party/android_deps/libs/com_google_android_gms_play_services_vision_common/LICENSE;md5=d332b0f8bf5e67cef70541572ba69b61 \ - file://${S}/third_party/android_deps/libs/com_google_android_material_material/LICENSE;md5=7f7d74108ee1b7a743cca7d9a86784d6 \ - file://${S}/third_party/android_deps/libs/com_google_android_play_core_common/LICENSE;md5=73c6700ef062ad41c3d27cab65d0c1e2 \ - file://${S}/third_party/android_deps/libs/com_google_android_play_feature_delivery/LICENSE;md5=014a1ef6f09b1a5ac887d86d5f18918d \ file://${S}/third_party/android_deps/libs/com_google_ar_impress/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_dagger_dagger/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_annotations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -140,17 +146,12 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_common_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_components/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_datatransport/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders_json/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_encoders_proto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid/LICENSE;md5=57214b126e4e648c9066e6e23d43be77 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_iid_interop/LICENSE;md5=5c9317538bd492b4fd01eec9d6972640 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_installations/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_installations_interop/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_measurement_connector/LICENSE;md5=298bd2a9dc20becb6693cfdccd58001a \ file://${S}/third_party/android_deps/libs/com_google_firebase_firebase_messaging/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_guava_guava_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/com_google_protobuf_protobuf_javalite/LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b \ file://${S}/third_party/android_deps/libs/jakarta_inject_jakarta_inject_api/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_checkerframework_checker_util/LICENSE;md5=87abbc18e66acb445c00810347051776 \ file://${S}/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations/LICENSE;md5=a5dd953e661e22a77f7b8062ae790f6a \ @@ -158,7 +159,6 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_parcelize_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_atomicfu_jvm/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_guava/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_play_services/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_media/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/android_opengl/LICENSE;md5=d10e92761a860d4113a7a525c78daf13 \ file://${S}/third_party/android_provider/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -245,6 +245,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -271,6 +272,11 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_paging_paging_common_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ + file://${S}/third_party/androidx/committed/libs/androidx_paging_paging_runtime/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_palette_palette/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ @@ -325,12 +331,11 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/arcore-android-sdk/LICENSE;md5=c1bc4f7fbdd7a5a3f2c0dfefd457665e \ file://${S}/third_party/ashmem/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/axe-core/LICENSE;md5=f27a50d2e878867827842f2c60e30bfc \ - file://${S}/third_party/beto-core/LICENSE;md5=2b42edef8fa55315f34f2370b4715ca9 \ file://${S}/third_party/bidimapper/LICENSE;md5=2b42edef8fa55315f34f2370b4715ca9 \ file://${S}/third_party/bidimapper/licenses/LICENSE.mitt;md5=4ed316158806c1e39b6b6e457c85b10f \ file://${S}/third_party/bidimapper/licenses/LICENSE.zod;md5=7aa01d261c2ac4ca667875e22474c798 \ file://${S}/third_party/blink/LICENSE_FOR_ABOUT_CREDITS;md5=11e90d553b211de885f245900c4ccf89 \ - file://${S}/third_party/boringssl/src/LICENSE;md5=bfca80950e49496c340e3327990eb7e7 \ + file://${S}/third_party/boringssl/src/LICENSE;md5=0131a611be3a37729f61e0b26319da57 \ file://${S}/third_party/boringssl/src/third_party/fiat/LICENSE;md5=7a7086e01a29257a15f3b0c9a42a12ff \ file://${S}/third_party/brotli/LICENSE;md5=941ee9cd1609382f946352712a319b4b \ file://${S}/third_party/bspatch/LICENSE;md5=3e837ede9697ce4c789c3ca32aabe003 \ @@ -414,12 +419,13 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/devtools-frontend/src/front_end/third_party/third-party-web/LICENSE;md5=fdc2dc205eb6e6da8e9d1add67084a96 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/wasmparser/package/LICENSE;md5=2ee41112a44fe7014dce33e26468ba93 \ file://${S}/third_party/devtools-frontend/src/front_end/third_party/web-vitals/package/LICENSE;md5=2c4a96430ec4639e0a80babbfb239a96 \ - file://${S}/third_party/distributed_point_functions/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/dom_distiller_js/LICENSE;md5=6cd778870ce5c0c08c8e16fbdf378532 \ + file://${S}/third_party/dragonbox/src/LICENSE-Apache2-LLVM;md5=2e982d844baa4df1c80de75470e0c5cb \ + file://${S}/third_party/dragonbox/src/LICENSE-Boost;md5=e4224ccaecb14d942c71d31bef20d78c \ file://${S}/third_party/eigen3/LICENSE;md5=48a3fe23ed1353e0995dadfda05ffdb6 \ file://${S}/third_party/emoji-metadata/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/emoji-segmenter/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/expat/src/expat/COPYING;md5=7b3b078238d0901d3b339289117cb7fb \ + file://${S}/third_party/expat/src/expat/COPYING;md5=f4fedd6116da0e171f7cb4d2923d7ac2 \ file://${S}/third_party/farmhash/LICENSE;md5=7dfaa79e2b070897e495fec386e3acfc \ file://${S}/third_party/fast_float/src/LICENSE-MIT;md5=32b11d50c7d9788d4270f6a83f3e68eb \ file://${S}/third_party/fdlibm/LICENSE;md5=b17367d6c97e638599071d99a3049dfe \ @@ -429,7 +435,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/flatbuffers/LICENSE;md5=a873c5645c184d51e0f9b34e1d7cf559 \ file://${S}/third_party/fontconfig/src/COPYING;md5=00252fd272bf2e722925613ad74cb6c7 \ file://${S}/third_party/fp16/LICENSE;md5=855dd24c28c76c916c5c2301b1958728 \ - file://${S}/third_party/freetype/src/docs/FTL.TXT;md5=9f37b4e6afa3fef9dba8932b16bd3f97 \ + file://${S}/third_party/freetype/src/docs/FTL.TXT;md5=72d844cd2f3bcaf6a85244b508032be7 \ file://${S}/third_party/fuchsia-gn-sdk/LICENSE;md5=4c8b7c5e9d0a48795db3dcd70f738ea5 \ file://${S}/third_party/fusejs/LICENSE;md5=f61de6d0d3d200550e44d63cc2a4fb67 \ file://${S}/third_party/fxdiv/src/LICENSE;md5=0c2eed7ba400a6fea8fec2a582b177e8 \ @@ -488,7 +494,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/libwebp/src/PATENTS;md5=c6926d0cb07d296f886ab6e0cc5a85b7 \ file://${S}/third_party/libx11/LICENSE;md5=28a27b3e1a66e95206d5645b787dad69 \ file://${S}/third_party/libxcb-keysyms/LICENSE;md5=5b0382211c6a98f8e4861daa0f3f4322 \ - file://${S}/third_party/libxml/src/Copyright;md5=f437ed9058e8e5135e47c01e973376ba \ + file://${S}/third_party/libxml/src/Copyright;md5=5873615e8a9ecbf5c8857c4312ee05d6 \ file://${S}/third_party/libxslt/src/Copyright;md5=0cd9a07afbeb24026c9b03aecfeba458 \ file://${S}/third_party/libyuv/LICENSE;md5=464282cfb405b005b9637f11103a7325 \ file://${S}/third_party/libzip/LICENSE;md5=6bb2408fd544544b86946e67ec2da9a0 \ @@ -505,6 +511,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/metrics_proto/LICENSE;md5=cfaf8febfe7b4f3e07a08dd4d0db1914 \ file://${S}/third_party/microsoft_webauthn/LICENSE;md5=b98fddd052bb2f5ddbcdbd417ffb26a8 \ file://${S}/third_party/minigbm/LICENSE;md5=fcd558ab2fd7f53408826e80eb0c11eb \ + file://${S}/third_party/ml_dtypes/src/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/modp_b64/LICENSE;md5=eb7e2e0af1d4971360553aedadee8d86 \ file://${S}/third_party/nearby/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/neon_2_sse/LICENSE;md5=53abad1ded16f44100126962f4bbef6c \ @@ -517,7 +524,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/openscreen/src/LICENSE;md5=5ff965dafeb8e99de6698664decb9e89 \ file://${S}/third_party/openscreen/src/third_party/protobuf/LICENSE;md5=37b5762e07f0af8c74ce80a8bda4266b \ file://${S}/third_party/openscreen/src/third_party/tinycbor/src/LICENSE;md5=6c1ac30774dd6476b42b5b020cd2aa5f \ - file://${S}/third_party/opus/src/COPYING;md5=e304cdf74c2a1b0a33a5084c128a23a3 \ + file://${S}/third_party/opus/src/COPYING;md5=4b365c2155d66e550e1447075d6744a5 \ file://${S}/third_party/ots/LICENSE;md5=e06eff2aa65b917034a81599bea73dc4 \ file://${S}/third_party/pdfium/LICENSE;md5=c93507531cc9bb8e24a05f2a1a4036c7 \ file://${S}/third_party/pdfium/third_party/agg23/copying;md5=d501f87982fe576fa1bfdf4f0ae741af \ @@ -527,7 +534,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/pdfium/third_party/lcms/LICENSE;md5=e9ce323c4b71c943a785db90142b228a \ file://${S}/third_party/pdfium/third_party/libopenjpeg/LICENSE;md5=c648878b4840d7babaade1303e7f108c \ file://${S}/third_party/pdfium/third_party/libtiff/LICENSE.md;md5=a3e32d664d6db1386b4689c8121531c3 \ - file://${S}/third_party/perfetto/LICENSE;md5=65fc11c16d093b463bafae828ec00d41 \ + file://${S}/third_party/perfetto/LICENSE;md5=c0c9924c5c63b4834b8b1959816c8e3b \ file://${S}/third_party/pffft/LICENSE;md5=0f39e43e9bc20e7e103e54750e1ec3a2 \ file://${S}/third_party/polymer/LICENSE.polymer;md5=324f45ce459ffd97e41d175a4e95a4be \ file://${S}/third_party/private-join-and-compute/src/LICENSE;md5=175792518e4ac015ab6696d16c4f607e \ @@ -540,7 +547,7 @@ LIC_FILES_CHKSUM = "\ file://${S}/third_party/rjsmin/LICENSE;md5=34f8c1142fd6208a8be89399cb521df9 \ file://${S}/third_party/rnnoise/COPYING;md5=1890bf89a18f8339491894a0b45428bf \ file://${S}/third_party/ruy/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ - file://${S}/third_party/screen-ai/THIRD_PARTY_LICENSES;md5=c1060306f371a5ac9a35cfba2d55f902 \ + file://${S}/third_party/screen-ai/THIRD_PARTY_LICENSES;md5=b96cd7885ca6a74ae8f020ddafaa9349 \ file://${S}/third_party/securemessage/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57 \ file://${S}/third_party/selenium-atoms/LICENSE;md5=5cd827bdaf8605a596a7ac9dcf808ea1 \ file://${S}/third_party/selenium-atoms/LICENSE.sizzle;md5=7a9495742f21b7624515e120b720cc65 \ diff --git a/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch b/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch index ed58c3371..7780e076f 100644 --- a/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch +++ b/meta-chromium/recipes-browser/chromium/files/0001-Drop-GN-compiler-settings-conflicting-with-OE.patch @@ -1,7 +1,7 @@ -From feb3b8299f312e4074fc76fe7b6dfa236a5823d8 Mon Sep 17 00:00:00 2001 +From 69bdbc33f4e6f2882f6ac8fb6bae883577887239 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 25 Jun 2024 11:06:19 +0000 -Subject: [PATCH] Drop GN compiler settings conflicting with OE +Subject: [PATCH 01/11] Drop GN compiler settings conflicting with OE The clang cross compiler built with meta-clang has lots of these settings built-in and specifying them with GN confuses the compiler: @@ -19,10 +19,10 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 46 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index e7a60d56b5..b0e9e04bf3 100644 +index 59942a3cf6..cf5e62eb15 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -1211,26 +1211,6 @@ config("compiler_cpu_abi") { +@@ -1255,26 +1255,6 @@ config("compiler_cpu_abi") { "-msse3", ] } @@ -49,7 +49,7 @@ index e7a60d56b5..b0e9e04bf3 100644 } else if (current_cpu == "mipsel" && !is_nacl) { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { -@@ -1238,9 +1218,6 @@ config("compiler_cpu_abi") { +@@ -1282,9 +1262,6 @@ config("compiler_cpu_abi") { if (is_android) { cflags += [ "--target=mipsel-linux-android" ] ldflags += [ "--target=mipsel-linux-android" ] @@ -59,7 +59,7 @@ index e7a60d56b5..b0e9e04bf3 100644 } } else { cflags += [ "-EL" ] -@@ -1320,8 +1297,6 @@ config("compiler_cpu_abi") { +@@ -1364,8 +1341,6 @@ config("compiler_cpu_abi") { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { if (is_clang) { @@ -68,7 +68,7 @@ index e7a60d56b5..b0e9e04bf3 100644 } else { cflags += [ "-EB" ] ldflags += [ "-EB" ] -@@ -1370,8 +1345,6 @@ config("compiler_cpu_abi") { +@@ -1414,8 +1389,6 @@ config("compiler_cpu_abi") { cflags += [ "--target=mips64el-linux-android" ] ldflags += [ "--target=mips64el-linux-android" ] } else { @@ -77,7 +77,7 @@ index e7a60d56b5..b0e9e04bf3 100644 } } else { cflags += [ -@@ -1429,8 +1402,6 @@ config("compiler_cpu_abi") { +@@ -1473,8 +1446,6 @@ config("compiler_cpu_abi") { ldflags += [ "-Wl,--hash-style=sysv" ] if (custom_toolchain == "") { if (is_clang) { @@ -86,11 +86,10 @@ index e7a60d56b5..b0e9e04bf3 100644 } else { cflags += [ "-EB", -@@ -1599,23 +1570,6 @@ config("compiler_deterministic") { - } +@@ -1656,23 +1627,6 @@ config("compiler_deterministic") { } } -- + - # Tells the compiler not to use absolute paths when passing the default - # paths to the tools it invokes. We don't want this because we don't - # really need it and it can mess up the RBE cache entries. @@ -107,6 +106,10 @@ index e7a60d56b5..b0e9e04bf3 100644 - ldflags += [ "-no-canonical-prefixes" ] - } - } - } +- + if (use_libcxx_modules) { + cflags += [ + # This removes absolute paths from .pcm files. +-- +2.39.5 - config("clang_revision") { diff --git a/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch b/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch index 9261b47c1..4b9123942 100644 --- a/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch +++ b/meta-chromium/recipes-browser/chromium/files/0002-v8-qemu-wrapper.patch @@ -1,7 +1,7 @@ -From d326bbe195c6853c600d01b9d438b5ee430b55c2 Mon Sep 17 00:00:00 2001 +From d5d34b719189ca2c3318e6636bf55197962e500c Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Tue, 7 Nov 2017 15:24:32 +0100 -Subject: [PATCH] v8: qemu wrapper +Subject: [PATCH 02/11] v8: qemu wrapper The patch below makes the V8 binaries run during the build be invoked through QEMU, as they are built for the target. @@ -10,7 +10,6 @@ Upstream-Status: Inappropriate [embedder specific] Signed-off-by: Raphael Kubo da Costa Signed-off-by: Maksim Sisov - --- tools/v8_context_snapshot/BUILD.gn | 1 + v8/BUILD.gn | 4 ++++ @@ -22,25 +21,25 @@ index 70c32b2fc9..88497d1436 100644 +++ b/tools/v8_context_snapshot/BUILD.gn @@ -49,6 +49,7 @@ if (use_v8_context_snapshot) { output_path = rebase_path(output_file, root_build_dir) - + args = [ + "./v8-qemu-wrapper.sh", "./" + rebase_path( get_label_info( ":v8_context_snapshot_generator($v8_snapshot_toolchain)", diff --git a/v8/BUILD.gn b/v8/BUILD.gn -index 9a2b2cdd94..8c217b57c9 100644 +index f2f72a02c6..014104d3e7 100644 --- a/v8/BUILD.gn +++ b/v8/BUILD.gn -@@ -2228,6 +2228,7 @@ template("run_torque") { +@@ -2265,6 +2265,7 @@ template("run_torque") { } - + args = [ + "./v8-qemu-wrapper.sh", "./" + rebase_path( get_label_info(":torque($toolchain)", "root_out_dir") + "/torque", root_build_dir), -@@ -2391,6 +2392,7 @@ action("generate_bytecode_builtins_list") { +@@ -2440,6 +2441,7 @@ action("generate_bytecode_builtins_list") { outputs = [ "$target_gen_dir/builtins-generated/bytecodes-builtins-list.h" ] deps = [ ":bytecode_builtins_list_generator($v8_generator_toolchain)" ] args = [ @@ -48,19 +47,22 @@ index 9a2b2cdd94..8c217b57c9 100644 "./" + rebase_path( get_label_info( ":bytecode_builtins_list_generator($v8_generator_toolchain)", -@@ -2470,6 +2472,7 @@ template("run_mksnapshot") { +@@ -2523,6 +2525,7 @@ template("run_mksnapshot") { } - + args += [ + "./v8-qemu-wrapper.sh", "./" + rebase_path(get_label_info(":mksnapshot($v8_snapshot_toolchain)", "root_out_dir") + "/mksnapshot", root_build_dir), -@@ -7427,6 +7430,7 @@ if (v8_enable_i18n_support) { +@@ -7566,6 +7569,7 @@ if (v8_enable_i18n_support) { outputs = [ output_file ] - + args = [ + "./v8-qemu-wrapper.sh", "./" + rebase_path( get_label_info( ":gen-regexp-special-case($v8_generator_toolchain)", +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch b/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch index 5f012500a..f129d8e54 100644 --- a/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch +++ b/meta-chromium/recipes-browser/chromium/files/0003-wrapper-extra-flags.patch @@ -1,7 +1,7 @@ -From a8c669d57bd409ba07255bd52fe3c9f0817de2ac Mon Sep 17 00:00:00 2001 +From 5b09c349a19ed28bbadba3061ca16d5aabb044e5 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Wed, 8 Nov 2017 16:43:47 +0100 -Subject: [PATCH] wrapper: extra flags +Subject: [PATCH 03/11] wrapper: extra flags The patch below is used to allow running Chromium as root as well as passing extra flags to it by default. @@ -9,7 +9,6 @@ extra flags to it by default. Upstream-Status: Inappropriate [embedder specific] Signed-off-by: Raphael Kubo da Costa - --- chrome/installer/linux/common/wrapper | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) @@ -21,9 +20,12 @@ index aaa46bf71f..7d8c8dd5fb 100755 @@ -36,5 +36,7 @@ exec < /dev/null exec > >(exec cat) exec 2> >(exec cat >&2) - + +CHROME_EXTRA_ARGS="" + # Note: exec -a below is a bashism. -exec -a "$0" "$HERE/@@PROGNAME@@" "$@" +exec -a "$0" "$HERE/@@PROGNAME@@" ${CHROME_EXTRA_ARGS} "$@" +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch b/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch index 55401d2f2..83e32739d 100644 --- a/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch +++ b/meta-chromium/recipes-browser/chromium/files/0004-Delete-compiler-options-not-available-in-release-ver.patch @@ -1,8 +1,8 @@ -From 0a052a88bf96542e0bb7585ccbfd39e681a32e3d Mon Sep 17 00:00:00 2001 +From 5d022ee1f84195bafcc6436a6e1f9b6f00e986cc Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Wed, 4 Dec 2019 19:06:54 -0800 -Subject: [PATCH] Delete compiler options not available in release versions of - clang _yet_ +Subject: [PATCH 04/11] Delete compiler options not available in release + versions of clang _yet_ * CREL feature is not supported on the current clang toolchain version, which causes build to fail with the following error: @@ -15,32 +15,31 @@ Upstream-Status: Inappropriate [ clang/master already supports them ] Signed-off-by: Khem Raj Signed-off-by: Randy MacLeod Signed-off-by: Ariel D'Alessandro - --- build/config/compiler/BUILD.gn | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index b0e9e04bf3..46ed84d73c 100644 +index cf5e62eb15..58e0fd0a71 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -614,13 +614,6 @@ config("compiler") { - } else { +@@ -629,13 +629,6 @@ config("compiler") { cflags += [ "-ffp-contract=off" ] } -- + - # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF - # (excluding toolchains that use an older version of LLVM). -- if (is_linux && !llvm_android_mainline && +- # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. +- if (is_linux && use_lld && !llvm_android_mainline && current_cpu != "arm" && - default_toolchain != "//build/toolchain/cros:target") { - cflags += [ "-Wa,--crel,--allow-experimental-crel" ] - } } - + # C11/C++11 compiler flags setup. -@@ -1934,6 +1927,11 @@ config("default_warnings") { +@@ -2083,6 +2076,11 @@ config("default_warnings") { } - + cflags += [ + # Disable unknown warnings, since system clang may not have + # backported newer warning yet unlike internal clang that @@ -49,4 +48,7 @@ index b0e9e04bf3..46ed84d73c 100644 + # TODO(crbug.com/330524456): -Wcast-function-type is under -Wextra now. "-Wno-cast-function-type", + +-- +2.39.5 diff --git a/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch b/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch index ff4660210..d9d034854 100644 --- a/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch +++ b/meta-chromium/recipes-browser/chromium/files/0005-avoid-link-latomic-failure-on-CentOS-8-host.patch @@ -1,7 +1,7 @@ -From be9a610454f3a90e2610443a424efcb34b4903e5 Mon Sep 17 00:00:00 2001 +From 9eb5a6ed9a73d7fab673f049ea7e5b9c4218434c Mon Sep 17 00:00:00 2001 From: Hongxu Jia Date: Fri, 22 Jan 2021 00:02:25 +0800 -Subject: [PATCH] avoid link latomic failure on CentOS 8 host +Subject: [PATCH 05/11] avoid link latomic failure on CentOS 8 host When host (such as CentOS 8) did not install libatomic, there was a link failure on native. In fact, only target requires to link atomic, @@ -11,17 +11,16 @@ Upstream-Status: Inappropriate [oe specific] Signed-off-by: Hongxu Jia Signed-off-by: Randy MacLeod - --- base/BUILD.gn | 2 ++ build/config/linux/BUILD.gn | 2 ++ 2 files changed, 4 insertions(+) diff --git a/base/BUILD.gn b/base/BUILD.gn -index 57a6af0061..2703e81421 100644 +index 5b41b3d805..2925aa607e 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn -@@ -1104,7 +1104,9 @@ component("base") { +@@ -1107,7 +1107,9 @@ component("base") { # Needed for if using newer C++ library than sysroot, except if # building inside the cros_sdk environment - use host_toolchain as a # more robust check for this. @@ -38,10 +37,13 @@ index 131bb71d1d..20f918ab82 100644 @@ -40,7 +40,9 @@ config("runtime_library") { defines = [ "OS_CHROMEOS" ] } - + + # Only target requires if ((!is_chromeos || default_toolchain != "//build/toolchain/cros:target") && + (current_toolchain != host_toolchain) && (!use_custom_libcxx || current_cpu == "mipsel")) { libs = [ "atomic" ] } +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch b/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch index de6266158..7e8cf1391 100644 --- a/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch +++ b/meta-chromium/recipes-browser/chromium/files/0006-Don-t-pass-unknown-LLVM-options.patch @@ -1,7 +1,7 @@ -From 8e9bf93215630604eed49476d6e848ad3ec3bdba Mon Sep 17 00:00:00 2001 +From 9a75e4248e3099a26ddcdfb0efc42cf82b4ec131 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Fri, 8 Dec 2023 11:47:43 +0000 -Subject: [PATCH] Don't pass unknown LLVM options +Subject: [PATCH 06/11] Don't pass unknown LLVM options Because we compile and link with an older LLVM version than upstream, we need to drop the following LLVM options that aren't available yet: @@ -17,13 +17,13 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 18 deletions(-) diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn -index 46ed84d73c..5f7cfb69e9 100644 +index 58e0fd0a71..b3385edfc0 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn -@@ -590,24 +590,6 @@ config("compiler") { +@@ -604,24 +604,6 @@ config("compiler") { } } - + - # TODO(crbug.com/40283598): This causes binary size growth and potentially - # other problems. - # TODO(crbug.com/40284925): This isn't supported by Cronet's mainline llvm version. @@ -45,3 +45,6 @@ index 46ed84d73c..5f7cfb69e9 100644 # TODO(crbug.com/40192287): Investigate why/if this should be needed. if (is_win) { cflags += [ "/clang:-ffp-contract=off" ] +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch b/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch index c0dcffa6c..c13c3607a 100644 --- a/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch +++ b/meta-chromium/recipes-browser/chromium/files/0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch @@ -1,8 +1,8 @@ -From 713169dbbcb3081744ea103749597885bb1b97a2 Mon Sep 17 00:00:00 2001 +From feb7f0598505fa6240a84ef8441c8c47d8e7b8f2 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Fri, 22 Mar 2024 10:43:47 +0000 -Subject: [PATCH] Fix "constexpr variable must be initialized by a constant - expression" +Subject: [PATCH 07/11] Fix "constexpr variable must be initialized by a + constant expression" clang 17 still has some problems with constexpr strings. We need to change the problematic expressions to be const instead. @@ -26,5 +26,8 @@ index e926bdca5b..799c4b3f80 100644 +const inline std::string kLegacyHierarchyCountryCodeString{"XX"}; +const AddressCountryCode kLegacyHierarchyCountryCode = AddressCountryCode(kLegacyHierarchyCountryCodeString); - + // Creates an instance of the address hierarchy model corresponding to the +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch b/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch index 465bc5412..a3e473b6c 100644 --- a/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch +++ b/meta-chromium/recipes-browser/chromium/files/0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch @@ -1,7 +1,7 @@ -From 6fcf4a20eef34ad3577505b8c36cd2ecd5e97676 Mon Sep 17 00:00:00 2001 +From 22bb04cf2c85e78024a59cc0846026d9ecab8237 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 19 Dec 2023 12:14:05 +0000 -Subject: [PATCH] Use the correct path to libclang_rt.builtins.a +Subject: [PATCH 08/11] Use the correct path to libclang_rt.builtins.a Chromium needs to link against libclang_rt.builtins.a, but it expects it in a slightly different directory than where it actually is in our @@ -17,14 +17,14 @@ code, but only allows us to specify a single `clang_base_path`. Upstream-Status: Inappropriate [specific to our sysroot setup] Signed-off-by: Max Ihlenfeldt --- - build/config/clang/BUILD.gn | 9 +++++---- - 1 file changed, 5 insertions(+), 4 deletions(-) + build/config/clang/BUILD.gn | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn -index 0d648fa310..4652cb5127 100644 +index 8c03235f5d..7933242823 100644 --- a/build/config/clang/BUILD.gn +++ b/build/config/clang/BUILD.gn -@@ -199,14 +199,15 @@ template("clang_lib") { +@@ -208,16 +208,17 @@ template("clang_lib") { assert(false) # Unhandled cpu type } } else if (is_linux || is_chromeos) { @@ -41,6 +41,12 @@ index 0d648fa310..4652cb5127 100644 } else if (current_cpu == "arm64") { - _dir = "aarch64-unknown-linux-gnu" + _suffix = "-aarch64" + } else if (current_cpu == "loong64") { +- _dir = "loongarch64-unknown-linux-gnu" ++ _suffix = "-loong64" } else { assert(false) # Unhandled cpu type } +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch index 391c5cda7..3ab7a1cf8 100644 --- a/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch +++ b/meta-chromium/recipes-browser/chromium/files/0009-Adjust-the-Rust-build-to-our-needs.patch @@ -1,7 +1,7 @@ -From 394ff496602bf9695c7f815f5f73c0027ab4ddc7 Mon Sep 17 00:00:00 2001 +From 8a8e2590d1c05348ed6f3155cf4038a8c7a6ba48 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Tue, 16 Jan 2024 12:29:30 +0000 -Subject: [PATCH] Adjust the Rust build to our needs +Subject: [PATCH 09/11] Adjust the Rust build to our needs 1. Set `RUSTC_BOOTSTRAP=1` environment variable when calling rustc, to avoid the need of a nightly Rust toolchain. @@ -12,21 +12,25 @@ Subject: [PATCH] Adjust the Rust build to our needs part for the Rust "target triple", but Chromium assumes the vendor to always be "-unknown". This new arg lets us override the default when building target code. +4. Use host rust compiler when with host_system_allocator_toolchain +5. Remove arm32 bit specific clang flags from rust compiler invocation Upstream-Status: Inappropriate [specific to our build setup] +Co-authored-by: Caner Altinbasak Signed-off-by: Max Ihlenfeldt --- - build/config/rust.gni | 28 ++++++++++++++++++++-------- + build/config/rust.gni | 28 ++++++++++++++++++---------- + build/rust/filter_clang_args.py | 5 +++++ build/rust/rustc_wrapper.py | 1 + build/rust/std/BUILD.gn | 28 +++++++++++++++++++++------- build/rust/std/find_std_rlibs.py | 16 +++++++++++----- - 4 files changed, 53 insertions(+), 20 deletions(-) + 5 files changed, 56 insertions(+), 22 deletions(-) diff --git a/build/config/rust.gni b/build/config/rust.gni -index 74b3b9ced5..5affd0b445 100644 +index bd18d13119..e260576fba 100644 --- a/build/config/rust.gni +++ b/build/config/rust.gni -@@ -102,6 +102,11 @@ declare_args() { +@@ -95,6 +95,11 @@ declare_args() { # a platform. Mostly applicable to Windows, where new versions can handle ANSI # escape sequences but it's not reliable in general. force_rustc_color_output = false @@ -36,15 +40,15 @@ index 74b3b9ced5..5affd0b445 100644 + # platform default. + rust_target_triple_vendor_for_target = "" } - - # Use the Rust toolchain built in-tree. When false, we use the prebuilt Rust -@@ -174,14 +179,21 @@ if (enable_rust) { + + declare_args() { +@@ -187,14 +192,21 @@ if (enable_rust) { # https://issues.chromium.org/u/1/issues/372512092#comment5 for an example. rust_abi_target = "" if (is_linux || is_chromeos) { + vendor = "-unknown" + -+ is_host = current_toolchain == host_toolchain || toolchain_for_rust_host_build_tools ++ is_host = current_toolchain == host_toolchain || current_toolchain == host_system_allocator_toolchain || toolchain_for_rust_host_build_tools + if (!is_host && rust_target_triple_vendor_for_target != "") { + vendor = rust_target_triple_vendor_for_target + } @@ -63,26 +67,29 @@ index 74b3b9ced5..5affd0b445 100644 cargo_target_abi = "" } else if (current_cpu == "arm") { if (arm_float_abi == "hard") { -@@ -201,21 +213,21 @@ if (is_linux || is_chromeos) { +@@ -213,18 +225,14 @@ if (is_linux || is_chromeos) { + # The thumbv7 vs. armv7 distinction is for legacy reasons and both # targets in fact target Thumb, see: # https://github.com/rust-lang/rust/issues/44722 - if (arm_use_neon) { +- if (arm_use_neon) { - rust_abi_target = "thumbv7neon-unknown-linux-gnueabi" + float_suffix -+ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix - } else { +- } else { - rust_abi_target = "armv7-unknown-linux-gnueabi" + float_suffix -+ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix - } +- } ++ rust_abi_target = "armv7" + vendor + "-linux-gnueabi" + float_suffix cargo_target_abi = "eabi" + float_suffix } else { -- rust_abi_target = "arm-unknown-linux-gnueabi" + float_suffix -+ rust_abi_target = "arm" + vendor + "-linux-gnueabi" + float_suffix + rust_abi_target = "arm-unknown-linux-gnueabi" + float_suffix cargo_target_abi = "eabi" + float_suffix } } else if (current_cpu == "riscv64") { - rust_abi_target = "riscv64gc-unknown-linux-gnu" + rust_abi_target = "riscv64gc" + vendor + "-linux-gnu" cargo_target_abi = "" + } else if (current_cpu == "ppc64") { + rust_abi_target = "powerpc64le-unknown-linux-gnu" +@@ -237,7 +245,7 @@ if (is_linux || is_chromeos) { + cargo_target_abi = "" } else { # Best guess for other future platforms. - rust_abi_target = current_cpu + "-unknown-linux-gnu" @@ -90,6 +97,22 @@ index 74b3b9ced5..5affd0b445 100644 cargo_target_abi = "" } } else if (is_android) { +diff --git a/build/rust/filter_clang_args.py b/build/rust/filter_clang_args.py +index 5a1843c0df..7fb4b49516 100644 +--- a/build/rust/filter_clang_args.py ++++ b/build/rust/filter_clang_args.py +@@ -24,6 +24,11 @@ def filter_clang_args(clangargs): + pass + elif args[i].startswith('-plugin-arg'): + i += 2 ++ # Filter out ARM-specific flags that aren't supported on other architectures ++ elif args[i].startswith('-mbranch-protection'): ++ pass # Skip this argument ++ elif args[i].startswith('-mfpu='): ++ pass + else: + yield args[i] + i += 1 diff --git a/build/rust/rustc_wrapper.py b/build/rust/rustc_wrapper.py index 8f2096dfe5..de43d44eed 100755 --- a/build/rust/rustc_wrapper.py @@ -99,27 +122,27 @@ index 8f2096dfe5..de43d44eed 100755 ldflags = remaining_args[ldflags_separator + 1:rustenv_separator] rustenv = remaining_args[rustenv_separator + 1:sources_separator] + rustenv += ["RUSTC_BOOTSTRAP=1"] - + abs_build_root = os.getcwd().replace('\\', '/') + '/' is_windows = sys.platform == 'win32' or args.target_windows diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn -index 6b996aa1fe..146f6633ce 100644 +index bb2c988452..fdddf68d46 100644 --- a/build/rust/std/BUILD.gn +++ b/build/rust/std/BUILD.gn -@@ -207,7 +207,8 @@ if (toolchain_has_rust) { +@@ -168,7 +168,8 @@ if (toolchain_has_rust) { # our locally-built std. Both reside in root_out_dir: we must only have one of # each per GN toolchain anyway. - + - sysroot_lib_subdir = "lib/rustlib/$rust_abi_target/lib" + sysroot_target_subdir = "lib/rustlib/$rust_abi_target" + sysroot_lib_subdir = "$sysroot_target_subdir/lib" - + if (!rust_prebuilt_stdlib) { local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" -@@ -336,12 +337,12 @@ if (toolchain_has_rust) { +@@ -295,12 +296,12 @@ if (toolchain_has_rust) { rust_abi_target, ] - + - outputs = [] + outputs = [ "$target_out_dir/target.json" ] foreach(lib, all_stdlibs_to_copy) { @@ -130,9 +153,9 @@ index 6b996aa1fe..146f6633ce 100644 - outputs += [ "$target_out_dir/$lib" ] + outputs += [ "$target_out_dir/lib/$lib" ] } - + visibility = [ ":*" ] -@@ -354,8 +355,18 @@ if (toolchain_has_rust) { +@@ -313,8 +314,18 @@ if (toolchain_has_rust) { "enable_rust=false") deps = [ ":find_stdlib" ] sources = get_target_outputs(":find_stdlib") @@ -150,10 +173,10 @@ index 6b996aa1fe..146f6633ce 100644 + deps = [ ":find_stdlib" ] + sources = [ "$target_out_dir/target.json" ] + outputs = [ "$prebuilt_rustc_sysroot/$sysroot_target_subdir/target.json" ] - + visibility = [ ":*" ] } -@@ -395,7 +406,10 @@ if (toolchain_has_rust) { +@@ -354,7 +365,10 @@ if (toolchain_has_rust) { ":prebuilt_stdlib_libs", ":stdlib_public_dependent_libs", ] @@ -162,26 +185,26 @@ index 6b996aa1fe..146f6633ce 100644 + ":prebuilt_rustc_copy_to_sysroot", + ":prebuilt_rustc_copy_target_json_to_sysroot", + ] - - # The host builds tools toolchain supports Rust only and does not use - # the allocator remapping to point it to PartitionAlloc. + } + } + } diff --git a/build/rust/std/find_std_rlibs.py b/build/rust/std/find_std_rlibs.py index 386258f890..e8fdaa904a 100755 --- a/build/rust/std/find_std_rlibs.py +++ b/build/rust/std/find_std_rlibs.py @@ -17,7 +17,7 @@ import re from collections import defaultdict - + EXPECTED_STDLIB_INPUT_REGEX = re.compile(r"([0-9a-z_]+)(?:-([0-9]+))?$") -RLIB_NAME_REGEX = re.compile(r"lib([0-9a-z_]+)-([0-9a-f]+)\.rlib$") +RLIB_NAME_REGEX = re.compile(r"lib([0-9a-z_]+)(-([0-9a-f]+))?\.rlib$") - - + + def main(): @@ -52,6 +52,8 @@ def main(): rustc_args.extend(["--target", args.target]) rustlib_dir = subprocess.check_output(rustc_args).rstrip().decode() - + + lib_output_dir = os.path.join(args.output, 'lib') + # Copy the rlibs to a predictable location. Whilst we're doing so, @@ -193,7 +216,7 @@ index 386258f890..e8fdaa904a 100755 depfile.write( - "%s:" % (os.path.join(args.output, "lib%s.rlib" % args.depfile_target))) + "%s:" % (os.path.join(lib_output_dir, "lib%s.rlib" % args.depfile_target))) - + def copy_file(infile, outfile): depfile.write(f" {infile}") @@ -99,7 +101,7 @@ def main(): @@ -202,27 +225,30 @@ index 386258f890..e8fdaa904a 100755 # which we need for statically computable gn dependency rules. - (crate_name, metadata) = RLIB_NAME_REGEX.match(f).group(1, 2) + (crate_name, metadata) = RLIB_NAME_REGEX.match(f).group(1, 3) - + # Use the number of times we've seen this name to disambiguate the output # filenames. Since we sort the input filenames including the metadata, @@ -117,14 +119,18 @@ def main(): output_filename = f"lib{concise_name}.rlib" - + infile = os.path.join(rustlib_dir, f) - outfile = os.path.join(args.output, output_filename) + outfile = os.path.join(lib_output_dir, output_filename) copy_file(infile, outfile) - + for f in extra_libs: infile = os.path.join(rustlib_dir, f) - outfile = os.path.join(args.output, f) + outfile = os.path.join(lib_output_dir, f) copy_file(infile, outfile) - + + infile = os.path.join(os.environ['RUST_TARGET_PATH'], f'{args.target}.json') + outfile = os.path.join(args.output, 'target.json') + copy_file(infile, outfile) + depfile.write("\n") - + + +-- +2.39.5 diff --git a/meta-chromium/recipes-browser/chromium/files/0010-Don-t-require-profiler_builtins.rlib.patch b/meta-chromium/recipes-browser/chromium/files/0010-Don-t-require-profiler_builtins.rlib.patch index fa15e7d6a..56c5156a2 100644 --- a/meta-chromium/recipes-browser/chromium/files/0010-Don-t-require-profiler_builtins.rlib.patch +++ b/meta-chromium/recipes-browser/chromium/files/0010-Don-t-require-profiler_builtins.rlib.patch @@ -1,7 +1,7 @@ -From d6862d22d64ddb3e1b85c086eb8c14e69450ceb0 Mon Sep 17 00:00:00 2001 +From 6d8f717cb23cc00c25182d5377ffebc9028761d3 Mon Sep 17 00:00:00 2001 From: Max Ihlenfeldt Date: Wed, 7 Feb 2024 11:58:30 +0000 -Subject: [PATCH] Don't require profiler_builtins.rlib +Subject: [PATCH 10/11] Don't require profiler_builtins.rlib This library is only needed for profiling builds, which we don't do. Including it leads to compile errors on with OE's current master branch @@ -14,10 +14,10 @@ Signed-off-by: Max Ihlenfeldt 1 file changed, 1 deletion(-) diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn -index 38e5ab7645..b5f5291283 100644 +index fdddf68d46..7afe61ab75 100644 --- a/build/rust/std/BUILD.gn +++ b/build/rust/std/BUILD.gn -@@ -100,7 +100,6 @@ if (toolchain_has_rust) { +@@ -76,7 +76,6 @@ if (toolchain_has_rust) { # don't need to pass to the C++ linker because they're used for specialized # purposes. skip_stdlib_files = [ @@ -25,3 +25,6 @@ index 38e5ab7645..b5f5291283 100644 "rustc_std_workspace_alloc", "rustc_std_workspace_core", "rustc_std_workspace_std", +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch b/meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch deleted file mode 100644 index 0dca0b54d..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0011-Revert-Allow-and-use-std-hardware_destructive_interf.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 5f8558eedf059e4711d234e6da0413ab26b86334 Mon Sep 17 00:00:00 2001 -From: Max Ihlenfeldt -Date: Mon, 21 Oct 2024 10:08:17 +0000 -Subject: [PATCH] Revert "Allow (and use) - std::hardware_destructive_interference_size." - -libcxx only defines `std::hardware_{con,de}structive_interference_size` -if `__GCC_{CON,DE}STRUCTIVE_SIZE` are defined. clang only defines these -since version 19.1, see -https://github.com/llvm/llvm-project/commit/72c373bf. - -As Scarthgap uses clang 18.1, we need to revert -https://crrev.com/c/5767325, which uses -`std::hardware_destructive_interference_size`. - -Upstream-Status: Inappropriate [specific to older versions of clang] -Signed-off-by: Max Ihlenfeldt ---- - .../providers/cast/channel/enum_table.h | 4 +- - styleguide/c++/c++-features.md | 54 ++++++++++--------- - 2 files changed, 31 insertions(+), 27 deletions(-) - -diff --git a/components/media_router/common/providers/cast/channel/enum_table.h b/components/media_router/common/providers/cast/channel/enum_table.h -index fdf8941c32..9340ba2857 100644 ---- a/components/media_router/common/providers/cast/channel/enum_table.h -+++ b/components/media_router/common/providers/cast/channel/enum_table.h -@@ -12,7 +12,6 @@ - - #include - #include --#include - #include - #include - #include -@@ -368,7 +367,8 @@ class EnumTable { - - private: - #ifdef ARCH_CPU_64_BITS -- alignas(std::hardware_destructive_interference_size) -+ // Align the data on a cache line boundary. -+ alignas(64) - #endif - std::initializer_list data_; - bool is_sorted_; -diff --git a/styleguide/c++/c++-features.md b/styleguide/c++/c++-features.md -index fec5e73654..ffaf71fd54 100644 ---- a/styleguide/c++/c++-features.md -+++ b/styleguide/c++/c++-features.md -@@ -587,6 +587,35 @@ Overlaps with utilities in `base/strings/string_number_conversions.h`, which are - easier to use correctly. - *** - -+### std::hardware_{con,de}structive_interference_size [banned] -+ -+```c++ -+struct SharedData { -+ ReadOnlyFrequentlyUsed data; -+ alignas(std::hardware_destructive_interference_size) std::atomic counter; -+}; -+``` -+ -+**Description:** The `std::hardware_destructive_interference_size` constant is -+useful to avoid false sharing (destructive interference) between variables that -+would otherwise occupy the same cacheline. In contrast, -+`std::hardware_constructive_interference_size` is helpful to promote true -+sharing (constructive interference), e.g. to support better locality for -+non-contended data. -+ -+**Documentation:** -+[`std::hardware_destructive_interference_size`](https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size), -+[`std::hardware_constructive_interference_size`](https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size) -+ -+**Notes:** -+*** promo -+Banned for now since these are -+[not supported yet](https://github.com/llvm/llvm-project/issues/60174). Allow -+once supported. -+ -+[Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/cwktrFxxUY4) -+*** -+ - ### std::{pmr::memory_resource,polymorphic_allocator} [banned] - - ```c++ -@@ -1170,31 +1199,6 @@ avoiding the need to use the `erase(remove(...` paradigm. - [Migration bug](https://crbug.com/1414639) - *** - --### std::hardware_{con,de}structive_interference_size [allowed] -- --```c++ --struct SharedData { -- ReadOnlyFrequentlyUsed data; -- alignas(std::hardware_destructive_interference_size) std::atomic counter; --}; --``` -- --**Description:** The `std::hardware_destructive_interference_size` constant is --useful to avoid false sharing (destructive interference) between variables that --would otherwise occupy the same cacheline. In contrast, --`std::hardware_constructive_interference_size` is helpful to promote true --sharing (constructive interference), e.g. to support better locality for --non-contended data. -- --**Documentation:** --[`std::hardware_destructive_interference_size`](https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size), --[`std::hardware_constructive_interference_size`](https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size) -- --**Notes:** --*** promo --[Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/cwktrFxxUY4) --*** -- - ### std::is_[un]bounded_array [allowed] - - ```c++ diff --git a/meta-chromium/recipes-browser/chromium/files/0011-fix-check_version-Only-compare-node.js-major-version.patch b/meta-chromium/recipes-browser/chromium/files/0011-fix-check_version-Only-compare-node.js-major-version.patch new file mode 100644 index 000000000..0ac0c5bd1 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0011-fix-check_version-Only-compare-node.js-major-version.patch @@ -0,0 +1,42 @@ +From 8dec7cef6d2e041b363371f611156b473a8aca95 Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Wed, 2 Jul 2025 15:46:35 +0100 +Subject: [PATCH 11/11] fix(check_version):Only compare node.js major version + number + +Yocto has a slightly newer version of node.js available. Compare only +the major version to avoid build errors. + +Upstream-Status: Inappropriate [specific to our build setup] +--- + third_party/node/check_version.js | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/third_party/node/check_version.js b/third_party/node/check_version.js +index 0a037ab449..72201b473e 100644 +--- a/third_party/node/check_version.js ++++ b/third_party/node/check_version.js +@@ -28,12 +28,17 @@ async function main() { + await readFile(args.expected_version_file, {encoding: 'utf-8'}); + const expectedVersion = extractExpectedVersion(contents); + ++ // Extract major version numbers for comparison ++ const expectedMajor = expectedVersion.match(/^v?(\d+)/)?.[1]; ++ const actualMajor = process.version.match(/^v?(\d+)/)?.[1]; ++ + const errorMessage = +- `Failed NodeJS version check: Expected version '${expectedVersion}', ` + +- `but found '${process.version}'. Did you run 'gclient sync'? If the ` + ++ `Failed NodeJS version check: Expected major version '${expectedMajor}', ` + ++ `but found '${actualMajor}'. Expected full version was '${expectedVersion}', ` + ++ `actual version is '${process.version}'. Did you run 'gclient sync'? If the ` + + `problem persists try running 'gclient sync -f' instead, or deleting ` + + `third_party/node/{linux,win,mac} folders and trying again.`; + +- assert.equal(expectedVersion, process.version, errorMessage); ++ assert.equal(expectedMajor, actualMajor, errorMessage); + } + main(); +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch b/meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch deleted file mode 100644 index 0d291298b..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0012-Revert-Set-Rust-symbol-visibility-to-hidden-when-C-s.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 6302ea4936e24232af4d50257b94a079471063cf Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Thu, 14 Nov 2024 22:23:28 -0300 -Subject: [PATCH] Revert "Set Rust symbol visibility to hidden when C++ symbols - are" - -This reverts chromium commit ee3900fd57b3c580aefff15c64052904d81b7760. -* Change-Id: https://crrev.com/c/5966273 - -Fixes the following compilation error: - -``` -| FAILED: obj/third_party/rust/ryu/v1/lib/libryu_lib.rlib -[...] -| error: unknown unstable option: `default-visibility` -``` - -This patch will be dropped once Rust >= 1.83 is available, which -includes: -* rust-lang/rust#130005 [0] -* rust-lang/rust#131519 [1] - -Note that currently meta-lts-mixins [2] provides the following versions: -* kirkstone: `1.80.1` -* scarthgap: `1.81.0` - -[0] https://github.com/rust-lang/rust/pull/130005 -[1] https://github.com/rust-lang/rust/pull/131519 -[2] https://git.yoctoproject.org/meta-lts-mixins - -Upstream-Status: Inappropriate [specific to older versions of rust] -Signed-off-by: Ariel D'Alessandro ---- - build/config/gcc/BUILD.gn | 1 - - build/sanitizers/asan_suppressions.cc | 13 +++++++++++++ - 2 files changed, 13 insertions(+), 1 deletion(-) - -diff --git a/build/config/gcc/BUILD.gn b/build/config/gcc/BUILD.gn -index a659210b196aa..147ebfc53426c 100644 ---- a/build/config/gcc/BUILD.gn -+++ b/build/config/gcc/BUILD.gn -@@ -32,7 +32,6 @@ declare_args() { - # See http://gcc.gnu.org/wiki/Visibility - config("symbol_visibility_hidden") { - cflags = [ "-fvisibility=hidden" ] -- rustflags = [ "-Zdefault-visibility=hidden" ] - - # Visibility attribute is not supported on AIX. - if (current_os != "aix") { -diff --git a/build/sanitizers/asan_suppressions.cc b/build/sanitizers/asan_suppressions.cc -index bfbd4b792a919..f0557be762c40 100644 ---- a/build/sanitizers/asan_suppressions.cc -+++ b/build/sanitizers/asan_suppressions.cc -@@ -15,6 +15,19 @@ - // // http://crbug.com/178677 - // "interceptor_via_lib:libsqlite3.so\n" - char kASanDefaultSuppressions[] = -+ // https://crbug.com/1471542 false positive odr violations from Rust code. -+ "odr_violation:^core::\n" -+ "odr_violation:^object::\n" -+ "odr_violation:^std::io::\n" -+ "odr_violation:^serde::\n" -+ "odr_violation:^serde_json_lenient::\n" -+ "odr_violation:^std::panicking::\n" -+ "odr_violation:^std::thread::Builder::\n" -+ "odr_violation:^read_fonts::tables::\n" -+ "odr_violation:^std_detect::detect::cache::\n" -+ "odr_violation:^alloc::sync::\n" -+ "odr_violation:^log::\n" -+ - // End of suppressions. - // PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS. - ""; // Please keep this semicolon. diff --git a/meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch b/meta-chromium/recipes-browser/chromium/files/0012-chromium-fix-v4l2-compiler-error-on-arm.patch similarity index 90% rename from meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch rename to meta-chromium/recipes-browser/chromium/files/0012-chromium-fix-v4l2-compiler-error-on-arm.patch index 26823502e..10c8926d3 100644 --- a/meta-chromium/recipes-browser/chromium/files/0020-chromium-fix-v4l2-compiler-error-on-arm.patch +++ b/meta-chromium/recipes-browser/chromium/files/0012-chromium-fix-v4l2-compiler-error-on-arm.patch @@ -1,7 +1,7 @@ -From 568dd0026ac86878bf6f0c99ac837a964525860f Mon Sep 17 00:00:00 2001 +From 335090e8e3480bdeea12687f8aa2b0e3dd4bf868 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Sat, 26 Jul 2025 12:47:57 +0100 -Subject: [PATCH 20/20] chromium: fix v4l2 compiler error on arm +Subject: [PATCH 12/12] chromium: fix v4l2 compiler error on arm fixes: ../../media/gpu/v4l2/legacy/v4l2_video_decoder_backend_stateful.cc:450:20: diff --git a/meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch b/meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch deleted file mode 100644 index e47cc472f..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0013-pdfium-Fix-missing-PDFiumAPIStringBufferAdapter-temp.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 20deec0782b78d1c8302764e852ff174f7a4f564 Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Fri, 15 Nov 2024 00:49:27 -0300 -Subject: [PATCH] pdfium: Fix missing PDFiumAPIStringBufferAdapter template - argument - -Fix the following compilation error: - -``` -| ../../pdf/pdfium/pdfium_api_wrappers.cc:150:32: error: alias template - 'PDFiumAPIStringBufferAdapter' requires template arguments; argument - deduction only allowed for class templates -| 150 | PDFiumAPIStringBufferAdapter adapter(&name, expected_size, -| | ^ -| ../../pdf/pdfium/pdfium_api_string_buffer_adapter.h:173:1: note: template - is declared here -| 173 | using PDFiumAPIStringBufferAdapter = -| | ^ -| 1 error generated. -``` - -This patch will be dropped once clang >= 19.1.0 is available, which -includes support for class template argument deduction (CTAD) for type -alias templates (P1814R0 [0]) (#54051 [1]). - -Note that currently meta-clang [2] provides the clang version `18.1.6`. - -[0] https://wg21.link/p1814r0 -[1] https://github.com/llvm/llvm-project/issues/54051 -[2] https://github.com/kraj/meta-clang - -Upstream-Status: Inappropriate [specific to older versions of clang] -Signed-off-by: Ariel D'Alessandro ---- - pdf/pdfium/pdfium_api_wrappers.cc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/pdf/pdfium/pdfium_api_wrappers.cc b/pdf/pdfium/pdfium_api_wrappers.cc -index b13fe20875e61..60c973b7152ab 100644 ---- a/pdf/pdfium/pdfium_api_wrappers.cc -+++ b/pdf/pdfium/pdfium_api_wrappers.cc -@@ -147,7 +147,7 @@ std::u16string GetPageObjectMarkName(FPDF_PAGEOBJECTMARK mark) { - - // Number of characters, including the NUL. - const size_t expected_size = base::checked_cast(buflen_bytes / 2); -- PDFiumAPIStringBufferAdapter adapter(&name, expected_size, -+ PDFiumAPIStringBufferAdapter adapter(&name, expected_size, - /*check_expected_size=*/true); - unsigned long actual_buflen_bytes = 0; // NOLINT(runtime/int) - bool result = diff --git a/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch b/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch deleted file mode 100644 index d7f2d2abf..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0014-Revert-Remove-libavif-based-AVIF-decoder.patch +++ /dev/null @@ -1,3744 +0,0 @@ -From 75e1653ce9bf360e4c1cb5b86872ebff0b67a35b Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Thu, 17 Jul 2025 12:51:22 -0300 -Subject: [PATCH] Revert "Remove libavif based AVIF decoder" - -This reverts commit ce78002f97a433e53ab16805e16b895f9b52ea24. - -In order to disable crabbyavif to fix build errors, re-enable libavif so -it can be used in replacement. - -Upstream-Status: Inappropriate [upstream ticket https://crbug.com/357017325] -Signed-off-by: Ariel D'Alessandro ---- - .../common/ProductionSupportedFlagList.java | 3 + - chrome/browser/about_flags.cc | 4 + - chrome/browser/flag-metadata.json | 5 + - chrome/browser/flag_descriptions.cc | 5 + - chrome/browser/flag_descriptions.h | 3 + - third_party/blink/common/features.cc | 2 + - third_party/blink/public/common/features.h | 3 + - third_party/blink/renderer/platform/BUILD.gn | 13 + - .../renderer/platform/image-decoders/BUILD.gn | 12 +- - .../image-decoders/avif/avif_image_decoder.cc | 1295 ++++++++++++ - .../image-decoders/avif/avif_image_decoder.h | 191 ++ - .../avif/avif_image_decoder_fuzzer.cc | 30 + - .../avif/avif_image_decoder_test.cc | 1758 +++++++++++++++++ - .../avif/gen_crabbyavif_wrapper.py | 164 ++ - .../platform/image-decoders/image_decoder.cc | 17 +- - .../image_decoder_fuzzer_utils.cc | 8 + - .../image_decoder_fuzzer_utils.h | 1 + - 17 files changed, 3508 insertions(+), 6 deletions(-) - create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc - create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h - create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc - create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc - create mode 100644 third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py - -diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java -index def02f59887c1..3bfa4dc335cb1 100644 ---- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java -+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java -@@ -434,6 +434,9 @@ public final class ProductionSupportedFlagList { - Flag.baseFeature( - BaseFeatures.RUN_TASKS_BY_BATCHES, - "Run tasks in queue for 8ms before before sending a system message."), -+ Flag.baseFeature( -+ BlinkFeatures.CRABBY_AVIF, -+ "If enabled, CrabbyAvif will be used instead of libavif for decoding AVIF images."), - Flag.baseFeature( - NetworkServiceFeatures.DEPRECATE_UNLOAD, - "If false prevents the gradual deprecation of the unload event."), -diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index 5156e5d3f2c74..1a0e2039bda1d 100644 ---- a/chrome/browser/about_flags.cc -+++ b/chrome/browser/about_flags.cc -@@ -7780,6 +7780,10 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kAvifGainmapHdrImagesDescription, kOsAll, - FEATURE_VALUE_TYPE(blink::features::kAvifGainmapHdrImages)}, - -+ {"crabbyavif", flag_descriptions::kCrabbyAvifName, -+ flag_descriptions::kCrabbyAvifDescription, kOsAll, -+ FEATURE_VALUE_TYPE(blink::features::kCrabbyAvif)}, -+ - {"file-handling-icons", flag_descriptions::kFileHandlingIconsName, - flag_descriptions::kFileHandlingIconsDescription, kOsDesktop, - FEATURE_VALUE_TYPE(blink::features::kFileHandlingIcons)}, -diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index d6851e527feb1..bd09150e90221 100644 ---- a/chrome/browser/flag-metadata.json -+++ b/chrome/browser/flag-metadata.json -@@ -1658,6 +1658,11 @@ - "owners": ["sebsg@chromium.org", "bling-transactions-eng@google.com"], - "expiry_milestone": 135 - }, -+ { -+ "name": "crabbyavif", -+ "owners": [ "vigneshv@google.com", "image-codecs-eng@google.com" ], -+ "expiry_milestone": 133 -+ }, - { - "name": "cras-processor-wav-dump", - "owners": ["aaronyu@google.com", "chromeos-audio@google.com" ], -diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index c06c6c65b1398..1050fe6c31ca1 100644 ---- a/chrome/browser/flag_descriptions.cc -+++ b/chrome/browser/flag_descriptions.cc -@@ -446,6 +446,11 @@ const char kAvifGainmapHdrImagesDescription[] = - "If enabled, Chrome uses the gainmap (if present) in AVIF images to render " - "the HDR version on HDR displays and the SDR version on SDR displays."; - -+const char kCrabbyAvifName[] = "CrabbyAvif for decoding AVIF images"; -+const char kCrabbyAvifDescription[] = -+ "If enabled, CrabbyAvif will be used instead of libavif for decoding AVIF " -+ "images"; -+ - const char kTangibleSyncName[] = "Tangible Sync"; - const char kTangibleSyncDescription[] = - "Enables the tangible sync when a user starts the sync consent flow"; -diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index be3f7a6e96f05..0e4e01f106ba8 100644 ---- a/chrome/browser/flag_descriptions.h -+++ b/chrome/browser/flag_descriptions.h -@@ -312,6 +312,9 @@ extern const char kForestFeatureDescription[]; - extern const char kAvifGainmapHdrImagesName[]; - extern const char kAvifGainmapHdrImagesDescription[]; - -+extern const char kCrabbyAvifName[]; -+extern const char kCrabbyAvifDescription[]; -+ - extern const char kTangibleSyncName[]; - extern const char kTangibleSyncDescription[]; - -diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc -index 720c38313bd13..e4d60ff6ade3e 100644 ---- a/third_party/blink/common/features.cc -+++ b/third_party/blink/common/features.cc -@@ -510,6 +510,8 @@ BASE_FEATURE(kCorrectFloatExtensionTestForWebGL, - "CorrectFloatExtensionTestForWebGL", - base::FEATURE_ENABLED_BY_DEFAULT); - -+BASE_FEATURE(kCrabbyAvif, "CrabbyAvif", base::FEATURE_ENABLED_BY_DEFAULT); -+ - // When enabled, add a new option, {imageOrientation: 'none'}, to - // createImageBitmap, which ignores the image orientation metadata of the source - // and renders the image as encoded. -diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h -index 9970347ef0ccb..220f447a0725e 100644 ---- a/third_party/blink/public/common/features.h -+++ b/third_party/blink/public/common/features.h -@@ -366,6 +366,9 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM( - // See http://crbug.com/40788570. - BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDevToolsImprovedNetworkError); - -+// Enables the use of CrabbyAvif for decoding AVIF images. -+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kCrabbyAvif); -+ - // Enables input IPC to directly target the renderer's compositor thread without - // hopping through the IO thread first. - BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kDirectCompositorThreadIpc); -diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn -index 291676c658372..57505f9d2622c 100644 ---- a/third_party/blink/renderer/platform/BUILD.gn -+++ b/third_party/blink/renderer/platform/BUILD.gn -@@ -2786,6 +2786,19 @@ fuzzer_test("web_icon_sizes_fuzzer") { - additional_configs = [ "//third_party/blink/renderer:inside_blink" ] - } - -+# Fuzzer for blink::AVIFImageDecoder -+fuzzer_test("blink_avif_decoder_fuzzer") { -+ sources = [ "image-decoders/avif/avif_image_decoder_fuzzer.cc" ] -+ deps = [ -+ ":blink_fuzzer_test_support", -+ ":blink_image_decoder_fuzzer_test_support", -+ ":platform", -+ ] -+ seed_corpuses = [ "//third_party/blink/web_tests/images/resources/avif" ] -+ libfuzzer_options = [ "rss_limit_mb=8192" ] -+ additional_configs = [ "//third_party/blink/renderer:inside_blink" ] -+} -+ - # Fuzzer for blink::CrabbyAVIFImageDecoder - fuzzer_test("blink_crabbyavif_decoder_fuzzer") { - sources = [ "image-decoders/avif/crabbyavif_image_decoder_fuzzer.cc" ] -diff --git a/third_party/blink/renderer/platform/image-decoders/BUILD.gn b/third_party/blink/renderer/platform/image-decoders/BUILD.gn -index 870c7e8537249..e638c6bfcd93a 100644 ---- a/third_party/blink/renderer/platform/image-decoders/BUILD.gn -+++ b/third_party/blink/renderer/platform/image-decoders/BUILD.gn -@@ -79,11 +79,16 @@ component("image_decoders") { - - if (enable_av1_decoder) { - sources += [ -+ "avif/avif_image_decoder.cc", -+ "avif/avif_image_decoder.h", - "avif/crabbyavif_image_decoder.cc", - "avif/crabbyavif_image_decoder.h", - ] - -- deps += [ "//third_party/crabbyavif" ] -+ deps += [ -+ "//third_party/crabbyavif", -+ "//third_party/libavif", -+ ] - } - - if (enable_rust_png) { -@@ -130,6 +135,9 @@ source_set("unit_tests") { - } - - if (enable_av1_decoder) { -- sources += [ "avif/crabbyavif_image_decoder_test.cc" ] -+ sources += [ -+ "avif/avif_image_decoder_test.cc", -+ "avif/crabbyavif_image_decoder_test.cc", -+ ] - } - } -diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc -new file mode 100644 -index 0000000000000..eb9448d06d30a ---- /dev/null -+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc -@@ -0,0 +1,1295 @@ -+// Copyright 2020 The Chromium Authors -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifdef UNSAFE_BUFFERS_BUILD -+// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -+#pragma allow_unsafe_buffers -+#endif -+ -+#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "base/bits.h" -+#include "base/containers/adapters.h" -+#include "base/feature_list.h" -+#include "base/functional/bind.h" -+#include "base/logging.h" -+#include "base/memory/scoped_refptr.h" -+#include "base/metrics/histogram_functions.h" -+#include "base/numerics/safe_conversions.h" -+#include "base/timer/elapsed_timer.h" -+#include "build/build_config.h" -+#include "cc/base/math_util.h" -+#include "media/base/video_color_space.h" -+#include "skia/ext/cicp.h" -+#include "third_party/blink/public/common/features.h" -+#include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_animation.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" -+#include "third_party/blink/renderer/platform/image-decoders/rw_buffer.h" -+#include "third_party/libavif/src/include/avif/avif.h" -+#include "third_party/libyuv/include/libyuv.h" -+#include "third_party/skia/include/core/SkColorSpace.h" -+#include "third_party/skia/include/core/SkTypes.h" -+#include "third_party/skia/include/private/SkXmp.h" -+#include "ui/gfx/color_space.h" -+#include "ui/gfx/icc_profile.h" -+ -+#if defined(ARCH_CPU_BIG_ENDIAN) -+#error Blink assumes a little-endian target. -+#endif -+ -+namespace blink { -+ -+namespace { -+ -+// The maximum AVIF file size we are willing to decode. This helps libavif -+// detect invalid sizes and offsets in an AVIF file before the file size is -+// known. -+constexpr uint64_t kMaxAvifFileSize = 0x10000000; // 256 MB -+ -+const char* AvifDecoderErrorMessage(const avifDecoder* decoder) { -+ // decoder->diag.error is a char array that stores a null-terminated C string. -+ return *decoder->diag.error != '\0' ? decoder->diag.error -+ : "(no error message)"; -+} -+ -+// Builds a gfx::ColorSpace from the ITU-T H.273 (CICP) color description. -+gfx::ColorSpace GetColorSpace( -+ avifColorPrimaries color_primaries, -+ avifTransferCharacteristics transfer_characteristics, -+ avifMatrixCoefficients matrix_coefficients, -+ avifRange yuv_range, -+ bool grayscale) { -+ // (As of ISO/IEC 23000-22:2019 Amendment 2) MIAF Section 7.3.6.4 says: -+ // If a coded image has no associated colour property, the default property -+ // is defined as having colour_type equal to 'nclx' with properties as -+ // follows: -+ // – colour_primaries equal to 1, -+ // - transfer_characteristics equal to 13, -+ // - matrix_coefficients equal to 5 or 6 (which are functionally identical), -+ // and -+ // - full_range_flag equal to 1. -+ // ... -+ // These values correspond to AVIF_COLOR_PRIMARIES_BT709, -+ // AVIF_TRANSFER_CHARACTERISTICS_SRGB, and AVIF_MATRIX_COEFFICIENTS_BT601, -+ // respectively. -+ // -+ // Note that this only specifies the default color property when the color -+ // property is absent. It does not really specify the default values for -+ // colour_primaries, transfer_characteristics, and matrix_coefficients when -+ // they are equal to 2 (unspecified). But we will interpret it as specifying -+ // the default values for these variables because we must choose some defaults -+ // and these are the most reasonable defaults to choose. We also advocate that -+ // all AVIF decoders choose these defaults: -+ // https://github.com/AOMediaCodec/av1-avif/issues/84 -+ const auto primaries = color_primaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED -+ ? AVIF_COLOR_PRIMARIES_BT709 -+ : color_primaries; -+ const auto transfer = -+ transfer_characteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED -+ ? AVIF_TRANSFER_CHARACTERISTICS_SRGB -+ : transfer_characteristics; -+ const auto matrix = -+ (grayscale || matrix_coefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) -+ ? AVIF_MATRIX_COEFFICIENTS_BT601 -+ : matrix_coefficients; -+ const auto range = yuv_range == AVIF_RANGE_FULL -+ ? gfx::ColorSpace::RangeID::FULL -+ : gfx::ColorSpace::RangeID::LIMITED; -+ media::VideoColorSpace color_space(primaries, transfer, matrix, range); -+ if (color_space.IsSpecified()) { -+ return color_space.ToGfxColorSpace(); -+ } -+ // media::VideoColorSpace and gfx::ColorSpace do not support CICP -+ // MatrixCoefficients 12, 13, 14. -+ DCHECK_GE(matrix, 12); -+ DCHECK_LE(matrix, 14); -+ if (yuv_range == AVIF_RANGE_FULL) { -+ return gfx::ColorSpace::CreateJpeg(); -+ } -+ return gfx::ColorSpace::CreateREC709(); -+} -+ -+// Builds a gfx::ColorSpace from the ITU-T H.273 (CICP) color description in the -+// image. -+gfx::ColorSpace GetColorSpace(const avifImage* image) { -+ const bool grayscale = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400; -+ return GetColorSpace(image->colorPrimaries, image->transferCharacteristics, -+ image->matrixCoefficients, image->yuvRange, grayscale); -+} -+ -+// |y_size| is the width or height of the Y plane. Returns the width or height -+// of the U and V planes. |chroma_shift| represents the subsampling of the -+// chroma (U and V) planes in the x (for width) or y (for height) direction. -+int UVSize(int y_size, int chroma_shift) { -+ DCHECK(chroma_shift == 0 || chroma_shift == 1); -+ return (y_size + chroma_shift) >> chroma_shift; -+} -+ -+float FractionToFloat(auto numerator, uint32_t denominator) { -+ // First cast to double and not float because uint32_t->float conversion can -+ // cause precision loss. -+ return static_cast(numerator) / denominator; -+} -+ -+// If the image has a gain map, returns the alternate image's color space, if -+// it's different from the base image's and can be converted to a SkColorSpace. -+// If the alternate image color space is the same as the base image, there is no -+// need to specify it in SkGainmapInfo, and using the base image's color space -+// may be more accurate if the profile cannot be exactly represented as a -+// SkColorSpace object. -+sk_sp GetAltImageColorSpace(const avifImage& image) { -+ const avifGainMap* gain_map = image.gainMap; -+ if (!gain_map) { -+ return nullptr; -+ } -+ sk_sp color_space; -+ if (gain_map->altICC.size) { -+ if (image.icc.size == gain_map->altICC.size && -+ memcmp(gain_map->altICC.data, image.icc.data, gain_map->altICC.size) == -+ 0) { -+ // Same ICC as the base image, no need to specify it. -+ return nullptr; -+ } -+ std::unique_ptr profile = ColorProfile::Create( -+ base::span(gain_map->altICC.data, gain_map->altICC.size)); -+ if (!profile) { -+ DVLOG(1) << "Failed to parse gain map ICC profile"; -+ return nullptr; -+ } -+ const skcms_ICCProfile* icc_profile = profile->GetProfile(); -+ if (icc_profile->has_CICP) { -+ color_space = -+ skia::CICPGetSkColorSpace(icc_profile->CICP.color_primaries, -+ icc_profile->CICP.transfer_characteristics, -+ icc_profile->CICP.matrix_coefficients, -+ icc_profile->CICP.video_full_range_flag, -+ /*prefer_srgb_trfn=*/true); -+ } else if (icc_profile->has_toXYZD50) { -+ // The transfer function is irrelevant for gain map tone mapping, -+ // set it to something standard in case it's not set or not -+ // supported. -+ skcms_ICCProfile with_srgb = *icc_profile; -+ skcms_SetTransferFunction(&with_srgb, skcms_sRGB_TransferFunction()); -+ color_space = SkColorSpace::Make(with_srgb); -+ } -+ } else if (gain_map->altColorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED) { -+ if (image.icc.size == 0 && -+ image.colorPrimaries == gain_map->altColorPrimaries) { -+ // Same as base image, no need to specify it. -+ return nullptr; -+ } -+ const bool grayscale = (gain_map->altPlaneCount == 1); -+ const gfx::ColorSpace alt_color_space = GetColorSpace( -+ gain_map->altColorPrimaries, gain_map->altTransferCharacteristics, -+ gain_map->altMatrixCoefficients, gain_map->altYUVRange, grayscale); -+ color_space = alt_color_space.GetAsFullRangeRGB().ToSkColorSpace(); -+ } -+ -+ if (!color_space) { -+ DVLOG(1) << "Gain map image contains an unsupported color space"; -+ } -+ -+ return color_space; -+} -+ -+} // namespace -+ -+AVIFImageDecoder::AVIFImageDecoder(AlphaOption alpha_option, -+ HighBitDepthDecodingOption hbd_option, -+ ColorBehavior color_behavior, -+ cc::AuxImage aux_image, -+ wtf_size_t max_decoded_bytes, -+ AnimationOption animation_option) -+ : ImageDecoder(alpha_option, -+ hbd_option, -+ color_behavior, -+ aux_image, -+ max_decoded_bytes), -+ animation_option_(animation_option) {} -+ -+AVIFImageDecoder::~AVIFImageDecoder() = default; -+ -+String AVIFImageDecoder::FilenameExtension() const { -+ return "avif"; -+} -+ -+const AtomicString& AVIFImageDecoder::MimeType() const { -+ DEFINE_STATIC_LOCAL(const AtomicString, avif_mime_type, ("image/avif")); -+ return avif_mime_type; -+} -+ -+bool AVIFImageDecoder::ImageIsHighBitDepth() { -+ return bit_depth_ > 8; -+} -+ -+void AVIFImageDecoder::OnSetData(scoped_refptr data) { -+ have_parsed_current_data_ = false; -+ const bool all_data_received = IsAllDataReceived(); -+ avif_io_data_.reader = data_; -+ avif_io_data_.all_data_received = all_data_received; -+ avif_io_.sizeHint = all_data_received ? data_->size() : kMaxAvifFileSize; -+ -+ // ImageFrameGenerator::GetYUVAInfo() and ImageFrameGenerator::DecodeToYUV() -+ // assume that allow_decode_to_yuv_ and other image metadata are available -+ // after calling ImageDecoder::Create() with data_complete=true. -+ if (all_data_received) { -+ ParseMetadata(); -+ } -+} -+ -+cc::YUVSubsampling AVIFImageDecoder::GetYUVSubsampling() const { -+ switch (avif_yuv_format_) { -+ case AVIF_PIXEL_FORMAT_YUV420: -+ return cc::YUVSubsampling::k420; -+ case AVIF_PIXEL_FORMAT_YUV422: -+ return cc::YUVSubsampling::k422; -+ case AVIF_PIXEL_FORMAT_YUV444: -+ return cc::YUVSubsampling::k444; -+ case AVIF_PIXEL_FORMAT_YUV400: -+ return cc::YUVSubsampling::kUnknown; -+ case AVIF_PIXEL_FORMAT_NONE: -+ // avif_yuv_format_ is initialized to AVIF_PIXEL_FORMAT_NONE in the -+ // constructor. If we have called SetSize() successfully at the end -+ // of UpdateDemuxer(), avif_yuv_format_ cannot possibly be -+ // AVIF_PIXEL_FORMAT_NONE. -+ CHECK(!IsDecodedSizeAvailable()); -+ return cc::YUVSubsampling::kUnknown; -+ default: -+ break; -+ } -+ NOTREACHED() << "Invalid YUV format: " << avif_yuv_format_; -+} -+ -+gfx::Size AVIFImageDecoder::DecodedYUVSize(cc::YUVIndex index) const { -+ DCHECK(IsDecodedSizeAvailable()); -+ if (index == cc::YUVIndex::kU || index == cc::YUVIndex::kV) { -+ return gfx::Size(UVSize(Size().width(), chroma_shift_x_), -+ UVSize(Size().height(), chroma_shift_y_)); -+ } -+ return Size(); -+} -+ -+wtf_size_t AVIFImageDecoder::DecodedYUVWidthBytes(cc::YUVIndex index) const { -+ DCHECK(IsDecodedSizeAvailable()); -+ // Try to return the same width bytes as used by the dav1d library. This will -+ // allow DecodeToYUV() to copy each plane with a single memcpy() call. -+ // -+ // The comments for Dav1dPicAllocator in dav1d/picture.h require the pixel -+ // width be padded to a multiple of 128 pixels. -+ wtf_size_t aligned_width = static_cast( -+ base::bits::AlignUpDeprecatedDoNotUse(Size().width(), 128)); -+ if (index == cc::YUVIndex::kU || index == cc::YUVIndex::kV) { -+ aligned_width >>= chroma_shift_x_; -+ } -+ // When the stride is a multiple of 1024, dav1d_default_picture_alloc() -+ // slightly pads the stride to avoid a reduction in cache hit rate in most -+ // L1/L2 cache implementations. Match that trick here. (Note that this padding -+ // is not documented in dav1d/picture.h.) -+ if ((aligned_width & 1023) == 0) { -+ aligned_width += 64; -+ } -+ -+ // High bit depth YUV is stored as a uint16_t, double the number of bytes. -+ if (bit_depth_ > 8) { -+ DCHECK_LE(bit_depth_, 16); -+ aligned_width *= 2; -+ } -+ -+ return aligned_width; -+} -+ -+SkYUVColorSpace AVIFImageDecoder::GetYUVColorSpace() const { -+ DCHECK(CanDecodeToYUV()); -+ DCHECK_NE(yuv_color_space_, SkYUVColorSpace::kIdentity_SkYUVColorSpace); -+ return yuv_color_space_; -+} -+ -+uint8_t AVIFImageDecoder::GetYUVBitDepth() const { -+ DCHECK(CanDecodeToYUV()); -+ return bit_depth_; -+} -+ -+std::optional AVIFImageDecoder::GetHDRMetadata() const { -+ return hdr_metadata_; -+} -+ -+void AVIFImageDecoder::DecodeToYUV() { -+ DCHECK(image_planes_); -+ DCHECK(CanDecodeToYUV()); -+ -+ if (Failed()) { -+ return; -+ } -+ -+ DCHECK(decoder_); -+ DCHECK_EQ(decoded_frame_count_, 1u); // Not animation. -+ -+ // If the image is decoded progressively, just render the highest progressive -+ // frame in image_planes_ because the callers of DecodeToYUV() assume that a -+ // complete scan will not be updated. -+ const int frame_index = progressive_ ? (decoder_->imageCount - 1) : 0; -+ // TODO(crbug.com/943519): Implement YUV incremental decoding as in Decode(). -+ decoder_->allowIncremental = AVIF_FALSE; -+ -+ // libavif cannot decode to an external buffer. So we need to copy from -+ // libavif's internal buffer to |image_planes_|. -+ // TODO(crbug.com/1099825): Enhance libavif to decode to an external buffer. -+ auto ret = DecodeImage(frame_index); -+ if (ret != AVIF_RESULT_OK) { -+ if (ret != AVIF_RESULT_WAITING_ON_IO) { -+ SetFailed(); -+ } -+ return; -+ } -+ const avifImage* image = decoded_image_; -+ -+ DCHECK(!image->alphaPlane); -+ static_assert(cc::YUVIndex::kY == static_cast(AVIF_CHAN_Y), ""); -+ static_assert(cc::YUVIndex::kU == static_cast(AVIF_CHAN_U), ""); -+ static_assert(cc::YUVIndex::kV == static_cast(AVIF_CHAN_V), ""); -+ -+ // Disable subnormal floats which can occur when converting to half float. -+ std::unique_ptr disable_subnormals; -+ const bool is_f16 = image_planes_->color_type() == kA16_float_SkColorType; -+ if (is_f16) { -+ disable_subnormals = std::make_unique(); -+ } -+ const float kHighBitDepthMultiplier = -+ (is_f16 ? 1.0f : 65535.0f) / ((1 << bit_depth_) - 1); -+ -+ // Initialize |width| and |height| to the width and height of the luma plane. -+ uint32_t width = image->width; -+ uint32_t height = image->height; -+ -+ for (wtf_size_t plane_index = 0; plane_index < cc::kNumYUVPlanes; -+ ++plane_index) { -+ const cc::YUVIndex plane = static_cast(plane_index); -+ const wtf_size_t src_row_bytes = -+ base::strict_cast(image->yuvRowBytes[plane_index]); -+ const wtf_size_t dst_row_bytes = image_planes_->RowBytes(plane); -+ -+ if (bit_depth_ == 8) { -+ DCHECK_EQ(image_planes_->color_type(), kGray_8_SkColorType); -+ const uint8_t* src = image->yuvPlanes[plane_index]; -+ uint8_t* dst = static_cast(image_planes_->Plane(plane)); -+ libyuv::CopyPlane(src, src_row_bytes, dst, dst_row_bytes, width, height); -+ } else { -+ DCHECK_GT(bit_depth_, 8u); -+ DCHECK_LE(bit_depth_, 16u); -+ const uint16_t* src = -+ reinterpret_cast(image->yuvPlanes[plane_index]); -+ uint16_t* dst = static_cast(image_planes_->Plane(plane)); -+ if (image_planes_->color_type() == kA16_unorm_SkColorType) { -+ const wtf_size_t src_stride = src_row_bytes / 2; -+ const wtf_size_t dst_stride = dst_row_bytes / 2; -+ for (uint32_t j = 0; j < height; ++j) { -+ for (uint32_t i = 0; i < width; ++i) { -+ dst[j * dst_stride + i] = -+ src[j * src_stride + i] * kHighBitDepthMultiplier + 0.5f; -+ } -+ } -+ } else if (image_planes_->color_type() == kA16_float_SkColorType) { -+ // Note: Unlike CopyPlane_16, HalfFloatPlane wants the stride in bytes. -+ libyuv::HalfFloatPlane(src, src_row_bytes, dst, dst_row_bytes, -+ kHighBitDepthMultiplier, width, height); -+ } else { -+ NOTREACHED() << "Unsupported color type: " -+ << static_cast(image_planes_->color_type()); -+ } -+ } -+ if (plane == cc::YUVIndex::kY) { -+ // Having processed the luma plane, change |width| and |height| to the -+ // width and height of the chroma planes. -+ width = UVSize(width, chroma_shift_x_); -+ height = UVSize(height, chroma_shift_y_); -+ } -+ } -+ image_planes_->SetHasCompleteScan(); -+} -+ -+int AVIFImageDecoder::RepetitionCount() const { -+ if (decoded_frame_count_ > 1) { -+ switch (decoder_->repetitionCount) { -+ case AVIF_REPETITION_COUNT_INFINITE: -+ return kAnimationLoopInfinite; -+ case AVIF_REPETITION_COUNT_UNKNOWN: -+ // The AVIF file does not have repetitions specified using an EditList -+ // box. Loop infinitely for backward compatibility with older versions -+ // of Chrome. -+ return kAnimationLoopInfinite; -+ default: -+ return decoder_->repetitionCount; -+ } -+ } -+ return kAnimationNone; -+} -+ -+bool AVIFImageDecoder::FrameIsReceivedAtIndex(wtf_size_t index) const { -+ if (!IsDecodedSizeAvailable()) { -+ return false; -+ } -+ if (decoded_frame_count_ == 1) { -+ return ImageDecoder::FrameIsReceivedAtIndex(index); -+ } -+ if (index >= frame_buffer_cache_.size()) { -+ return false; -+ } -+ if (IsAllDataReceived()) { -+ return true; -+ } -+ avifExtent data_extent; -+ if (avifDecoderNthImageMaxExtent(decoder_.get(), index, &data_extent) != -+ AVIF_RESULT_OK) { -+ return false; -+ } -+ return data_extent.size == 0 || -+ data_extent.offset + data_extent.size <= data_->size(); -+} -+ -+std::optional AVIFImageDecoder::FrameTimestampAtIndex( -+ wtf_size_t index) const { -+ return index < frame_buffer_cache_.size() -+ ? frame_buffer_cache_[index].Timestamp() -+ : std::nullopt; -+} -+ -+base::TimeDelta AVIFImageDecoder::FrameDurationAtIndex(wtf_size_t index) const { -+ return index < frame_buffer_cache_.size() -+ ? frame_buffer_cache_[index].Duration() -+ : base::TimeDelta(); -+} -+ -+bool AVIFImageDecoder::ImageHasBothStillAndAnimatedSubImages() const { -+ // Per MIAF, all animated AVIF files must have a still image, even if it's -+ // just a pointer to the first frame of the animation. -+ return decoder_ && decoder_->imageSequenceTrackPresent; -+} -+ -+// static -+bool AVIFImageDecoder::MatchesAVIFSignature( -+ const FastSharedBufferReader& fast_reader) { -+ // avifPeekCompatibleFileType() clamps compatible brands at 32 when reading in -+ // the ftyp box in ISO BMFF for the 'avif' or 'avis' brand. So the maximum -+ // number of bytes read is 144 bytes (size 4 bytes, type 4 bytes, major brand -+ // 4 bytes, minor version 4 bytes, and 4 bytes * 32 compatible brands). -+ char buffer[144]; -+ avifROData input; -+ input.size = std::min(sizeof(buffer), fast_reader.size()); -+ input.data = reinterpret_cast( -+ fast_reader.GetConsecutiveData(0, input.size, buffer)); -+ return avifPeekCompatibleFileType(&input); -+} -+ -+gfx::ColorSpace AVIFImageDecoder::GetColorSpaceForTesting() const { -+ const auto* image = GetDecoderImage(); -+ CHECK(image); -+ return GetColorSpace(image); -+} -+ -+void AVIFImageDecoder::ParseMetadata() { -+ if (!UpdateDemuxer()) { -+ SetFailed(); -+ } -+} -+ -+void AVIFImageDecoder::DecodeSize() { -+ ParseMetadata(); -+} -+ -+wtf_size_t AVIFImageDecoder::DecodeFrameCount() { -+ if (!Failed()) { -+ ParseMetadata(); -+ } -+ return IsDecodedSizeAvailable() ? decoded_frame_count_ -+ : frame_buffer_cache_.size(); -+} -+ -+void AVIFImageDecoder::InitializeNewFrame(wtf_size_t index) { -+ auto& buffer = frame_buffer_cache_[index]; -+ if (decode_to_half_float_) { -+ buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16); -+ } -+ -+ // For AVIFs, the frame always fills the entire image. -+ buffer.SetOriginalFrameRect(gfx::Rect(Size())); -+ -+ avifImageTiming timing; -+ auto ret = avifDecoderNthImageTiming(decoder_.get(), index, &timing); -+ DCHECK_EQ(ret, AVIF_RESULT_OK); -+ buffer.SetTimestamp(base::Seconds(timing.pts)); -+ buffer.SetDuration(base::Seconds(timing.duration)); -+} -+ -+void AVIFImageDecoder::Decode(wtf_size_t index) { -+ if (Failed()) { -+ return; -+ } -+ -+ UpdateAggressivePurging(index); -+ -+ int frame_index = index; -+ // If the image is decoded progressively, find the highest progressive -+ // frame that we have received and decode from that frame index. Internally -+ // decoder_ still decodes the lower progressive frames, but they are only used -+ // as reference frames and not rendered. -+ if (progressive_) { -+ DCHECK_EQ(index, 0u); -+ // decoder_->imageIndex is the current image index. decoder_->imageIndex is -+ // initialized to -1. decoder_->imageIndex + 1 is the next image index. -+ DCHECK_LT(decoder_->imageIndex + 1, decoder_->imageCount); -+ for (frame_index = decoder_->imageIndex + 1; -+ frame_index + 1 < decoder_->imageCount; ++frame_index) { -+ avifExtent data_extent; -+ auto rv = avifDecoderNthImageMaxExtent(decoder_.get(), frame_index + 1, -+ &data_extent); -+ if (rv != AVIF_RESULT_OK) { -+ DVLOG(1) << "avifDecoderNthImageMaxExtent(" << frame_index + 1 -+ << ") failed: " << avifResultToString(rv) << ": " -+ << AvifDecoderErrorMessage(decoder_.get()); -+ SetFailed(); -+ return; -+ } -+ if (data_extent.size != 0 && -+ data_extent.offset + data_extent.size > data_->size()) { -+ break; -+ } -+ } -+ } -+ -+ // Allow AVIF frames to be partially decoded before all data is received. -+ // Only enabled for non-progressive still images because animations look -+ // better without incremental decoding and because progressive decoding makes -+ // incremental decoding unnecessary. -+ decoder_->allowIncremental = (decoder_->imageCount == 1); -+ -+ auto ret = DecodeImage(frame_index); -+ if (ret != AVIF_RESULT_OK && ret != AVIF_RESULT_WAITING_ON_IO) { -+ SetFailed(); -+ return; -+ } -+ const avifImage* image = decoded_image_; -+ -+ // ImageDecoder::SizeCalculationMayOverflow(), called by UpdateDemuxer() -+ // before being here, made sure the image height fits in an int. -+ int displayable_height = -+ static_cast(avifDecoderDecodedRowCount(decoder_.get())); -+ if (image == cropped_image_.get()) { -+ displayable_height -= clap_origin_.y(); -+ displayable_height = -+ std::clamp(displayable_height, 0, static_cast(image->height)); -+ } -+ -+ if (displayable_height == 0) { -+ return; // There is nothing to display. -+ } -+ -+ ImageFrame& buffer = frame_buffer_cache_[index]; -+ DCHECK_NE(buffer.GetStatus(), ImageFrame::kFrameComplete); -+ -+ if (buffer.GetStatus() == ImageFrame::kFrameEmpty) { -+ if (!InitFrameBuffer(index)) { -+ DVLOG(1) << "Failed to create frame buffer..."; -+ SetFailed(); -+ return; -+ } -+ DCHECK_EQ(buffer.GetStatus(), ImageFrame::kFramePartial); -+ // The buffer is transparent outside the decoded area while the image is -+ // loading. The correct alpha value for the frame will be set when it is -+ // fully decoded. -+ buffer.SetHasAlpha(true); -+ if (decoder_->allowIncremental) { -+ // In case of buffer disposal after decoding. -+ incrementally_displayed_height_ = 0; -+ } -+ } -+ -+ const int last_displayed_height = -+ decoder_->allowIncremental ? incrementally_displayed_height_ : 0; -+ if (displayable_height == last_displayed_height) { -+ return; // There is no new row to display. -+ } -+ DCHECK_GT(displayable_height, last_displayed_height); -+ -+ // Only render the newly decoded rows. -+ if (!RenderImage(image, last_displayed_height, &displayable_height, -+ &buffer)) { -+ SetFailed(); -+ return; -+ } -+ if (displayable_height == last_displayed_height) { -+ return; // There is no new row to display. -+ } -+ DCHECK_GT(displayable_height, last_displayed_height); -+ ColorCorrectImage(last_displayed_height, displayable_height, &buffer); -+ buffer.SetPixelsChanged(true); -+ if (decoder_->allowIncremental) { -+ incrementally_displayed_height_ = displayable_height; -+ } -+ -+ if (static_cast(displayable_height) == image->height && -+ (!progressive_ || frame_index + 1 == decoder_->imageCount)) { -+ buffer.SetHasAlpha(!!image->alphaPlane); -+ buffer.SetStatus(ImageFrame::kFrameComplete); -+ PostDecodeProcessing(index); -+ } -+} -+ -+bool AVIFImageDecoder::CanReusePreviousFrameBuffer(wtf_size_t index) const { -+ // (a) Technically we can reuse the bitmap of the previous frame because the -+ // AVIF decoder handles frame dependence internally and we never need to -+ // preserve previous frames to decode later ones, and (b) since this function -+ // will not currently be called, this is really more for the reader than any -+ // functional purpose. -+ return true; -+} -+ -+// static -+avifResult AVIFImageDecoder::ReadFromSegmentReader(avifIO* io, -+ uint32_t read_flags, -+ uint64_t offset, -+ size_t size, -+ avifROData* out) { -+ if (read_flags != 0) { -+ // Unsupported read_flags -+ return AVIF_RESULT_IO_ERROR; -+ } -+ -+ AvifIOData* io_data = static_cast(io->data); -+ -+ // Sanitize/clamp incoming request -+ if (offset > io_data->reader->size()) { -+ // The offset is past the end of the buffer or available data. -+ return io_data->all_data_received ? AVIF_RESULT_IO_ERROR -+ : AVIF_RESULT_WAITING_ON_IO; -+ } -+ -+ // It is more convenient to work with a variable of the size_t type. Since -+ // offset <= io_data->reader->size() <= SIZE_MAX, this cast is safe. -+ size_t position = static_cast(offset); -+ const size_t available_size = io_data->reader->size() - position; -+ if (size > available_size) { -+ if (!io_data->all_data_received) { -+ return AVIF_RESULT_WAITING_ON_IO; -+ } -+ size = available_size; -+ } -+ -+ out->size = size; -+ -+ base::span data = io_data->reader->GetSomeData(position); -+ if (data.size() >= size) { -+ out->data = data.data(); -+ return AVIF_RESULT_OK; -+ } -+ -+ io_data->buffer.clear(); -+ io_data->buffer.reserve(size); -+ -+ while (size != 0) { -+ data = io_data->reader->GetSomeData(position); -+ size_t copy_size = std::min(data.size(), size); -+ io_data->buffer.insert(io_data->buffer.end(), data.begin(), data.end()); -+ position += copy_size; -+ size -= copy_size; -+ } -+ -+ out->data = io_data->buffer.data(); -+ return AVIF_RESULT_OK; -+} -+ -+bool AVIFImageDecoder::UpdateDemuxer() { -+ DCHECK(!Failed()); -+ if (IsDecodedSizeAvailable()) { -+ return true; -+ } -+ -+ if (have_parsed_current_data_) { -+ return true; -+ } -+ have_parsed_current_data_ = true; -+ -+ if (!decoder_) { -+ decoder_.reset(avifDecoderCreate()); -+ if (!decoder_) { -+ return false; -+ } -+ -+ // For simplicity, use a hardcoded maxThreads of 2, independent of the image -+ // size and processor count. Note: even if we want maxThreads to depend on -+ // the image size, it is impossible to do so because maxThreads is passed to -+ // dav1d_open() inside avifDecoderParse(), but the image size is not known -+ // until avifDecoderParse() returns successfully. See -+ // https://github.com/AOMediaCodec/libavif/issues/636. -+ decoder_->maxThreads = 2; -+ -+ if (animation_option_ != AnimationOption::kUnspecified && -+ avifDecoderSetSource( -+ decoder_.get(), -+ animation_option_ == AnimationOption::kPreferAnimation -+ ? AVIF_DECODER_SOURCE_TRACKS -+ : AVIF_DECODER_SOURCE_PRIMARY_ITEM) != AVIF_RESULT_OK) { -+ return false; -+ } -+ -+ // Chrome doesn't use XMP and Exif metadata. Ignoring XMP and Exif will -+ // ensure avifDecoderParse() isn't waiting for some tiny Exif payload hiding -+ // at the end of a file. -+ decoder_->ignoreXMP = AVIF_TRUE; -+ decoder_->ignoreExif = AVIF_TRUE; -+ -+ // Turn off libavif's 'clap' (clean aperture) property validation. We -+ // validate 'clap' ourselves and ignore invalid 'clap' properties. -+ decoder_->strictFlags &= ~AVIF_STRICT_CLAP_VALID; -+ // Allow the PixelInformationProperty ('pixi') to be missing in AV1 image -+ // items. libheif v1.11.0 or older does not add the 'pixi' item property to -+ // AV1 image items. (This issue has been corrected in libheif v1.12.0.) See -+ // crbug.com/1198455. -+ decoder_->strictFlags &= ~AVIF_STRICT_PIXI_REQUIRED; -+ -+ if (base::FeatureList::IsEnabled(features::kAvifGainmapHdrImages) && -+ aux_image_ == cc::AuxImage::kGainmap) { -+ decoder_->imageContentToDecode = AVIF_IMAGE_CONTENT_GAIN_MAP; -+ } -+ -+ avif_io_.destroy = nullptr; -+ avif_io_.read = ReadFromSegmentReader; -+ avif_io_.write = nullptr; -+ avif_io_.persistent = AVIF_FALSE; -+ avif_io_.data = &avif_io_data_; -+ avifDecoderSetIO(decoder_.get(), &avif_io_); -+ } -+ -+ // If all data is received, there is no point in decoding progressively. -+ decoder_->allowProgressive = !IsAllDataReceived(); -+ -+ auto ret = avifDecoderParse(decoder_.get()); -+ if (ret == AVIF_RESULT_WAITING_ON_IO) { -+ return true; -+ } -+ if (ret != AVIF_RESULT_OK) { -+ DVLOG(1) << "avifDecoderParse failed: " << avifResultToString(ret) << ". " -+ << decoder_->diag.error; -+ return false; -+ } -+ -+ // Image metadata is available in decoder_->image after avifDecoderParse() -+ // even though decoder_->imageIndex is invalid (-1). -+ DCHECK_EQ(decoder_->imageIndex, -1); -+ // This variable is named |container| to emphasize the fact that the current -+ // contents of decoder_->image come from the container, not any frame. -+ const auto* container = GetDecoderImage(); -+ if (!container) { -+ return false; -+ } -+ -+ // The container width and container height are read from either the tkhd -+ // (track header) box of a track or the ispe (image spatial extents) property -+ // of an image item, both of which are mandatory in the spec. -+ if (container->width == 0 || container->height == 0) { -+ DVLOG(1) << "Container width and height must be present"; -+ return false; -+ } -+ -+ // The container depth is read from either the av1C box of a track or the av1C -+ // property of an image item, both of which are mandatory in the spec. -+ if (container->depth == 0) { -+ DVLOG(1) << "Container depth must be present"; -+ return false; -+ } -+ -+ DCHECK_GT(decoder_->imageCount, 0); -+ progressive_ = decoder_->progressiveState == AVIF_PROGRESSIVE_STATE_ACTIVE; -+ // If the image is progressive, decoder_->imageCount is the number of -+ // progressive frames, but there is only one still image. -+ decoded_frame_count_ = progressive_ ? 1 : decoder_->imageCount; -+ container_width_ = container->width; -+ container_height_ = container->height; -+ bit_depth_ = container->depth; -+ decode_to_half_float_ = -+ ImageIsHighBitDepth() && -+ high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat; -+ -+ // Verify that AVIF_PIXEL_FORMAT_{YUV444,YUV422,YUV420,YUV400} are -+ // consecutive. -+ static_assert(AVIF_PIXEL_FORMAT_YUV422 == AVIF_PIXEL_FORMAT_YUV444 + 1); -+ static_assert(AVIF_PIXEL_FORMAT_YUV420 == AVIF_PIXEL_FORMAT_YUV422 + 1); -+ static_assert(AVIF_PIXEL_FORMAT_YUV400 == AVIF_PIXEL_FORMAT_YUV420 + 1); -+ // Assert that after avifDecoderParse() returns AVIF_RESULT_OK, -+ // decoder_->image->yuvFormat (the same as container->yuvFormat) is one of the -+ // four YUV formats in AV1. -+ CHECK(container->yuvFormat >= AVIF_PIXEL_FORMAT_YUV444 && -+ container->yuvFormat <= AVIF_PIXEL_FORMAT_YUV400) -+ << "Invalid YUV format: " << container->yuvFormat; -+ avif_yuv_format_ = container->yuvFormat; -+ avifPixelFormatInfo format_info; -+ avifGetPixelFormatInfo(container->yuvFormat, &format_info); -+ chroma_shift_x_ = format_info.chromaShiftX; -+ chroma_shift_y_ = format_info.chromaShiftY; -+ -+ if (container->clli.maxCLL || container->clli.maxPALL) { -+ hdr_metadata_ = gfx::HDRMetadata(); -+ hdr_metadata_->cta_861_3 = gfx::HdrMetadataCta861_3( -+ container->clli.maxCLL, container->clli.maxPALL); -+ } -+ -+ // SetEmbeddedColorProfile() must be called before IsSizeAvailable() becomes -+ // true. So call SetEmbeddedColorProfile() before calling SetSize(). The color -+ // profile is either an ICC profile or the CICP color description. -+ -+ if (!IgnoresColorSpace()) { -+ // The CICP color description is always present because we can always get it -+ // from the AV1 sequence header for the frames. If an ICC profile is -+ // present, use it instead of the CICP color description. -+ if (container->icc.size) { -+ std::unique_ptr profile = ColorProfile::Create( -+ base::span(container->icc.data, container->icc.size)); -+ if (!profile) { -+ DVLOG(1) << "Failed to parse image ICC profile"; -+ return false; -+ } -+ uint32_t data_color_space = profile->GetProfile()->data_color_space; -+ const bool is_mono = container->yuvFormat == AVIF_PIXEL_FORMAT_YUV400; -+ if (is_mono) { -+ if (data_color_space != skcms_Signature_Gray && -+ data_color_space != skcms_Signature_RGB) { -+ profile = nullptr; -+ } -+ } else { -+ if (data_color_space != skcms_Signature_RGB) { -+ profile = nullptr; -+ } -+ } -+ if (!profile) { -+ DVLOG(1) -+ << "Image contains ICC profile that does not match its color space"; -+ return false; -+ } -+ SetEmbeddedColorProfile(std::move(profile)); -+ } else if (container->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED || -+ container->transferCharacteristics != -+ AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { -+ gfx::ColorSpace frame_cs = GetColorSpace(container); -+ -+ sk_sp sk_color_space = -+ frame_cs.GetAsFullRangeRGB().ToSkColorSpace(); -+ if (!sk_color_space) { -+ DVLOG(1) << "Image contains an unsupported color space"; -+ return false; -+ } -+ -+ skcms_ICCProfile profile; -+ sk_color_space->toProfile(&profile); -+ SetEmbeddedColorProfile(std::make_unique(profile)); -+ } -+ } -+ -+ // |angle| * 90 specifies the angle of anti-clockwise rotation in degrees. -+ // Legal values: [0-3]. -+ int angle = 0; -+ if (container->transformFlags & AVIF_TRANSFORM_IROT) { -+ angle = container->irot.angle; -+ CHECK_LT(angle, 4); -+ } -+ // |axis| specifies how the mirroring is performed. -+ // -1: No mirroring. -+ // 0: The top and bottom parts of the image are exchanged. -+ // 1: The left and right parts of the image are exchanged. -+ int axis = -1; -+ if (container->transformFlags & AVIF_TRANSFORM_IMIR) { -+ axis = container->imir.axis; -+ CHECK_LT(axis, 2); -+ } -+ // MIAF Section 7.3.6.7 (Clean aperture, rotation and mirror) says: -+ // These properties, if used, shall be indicated to be applied in the -+ // following order: clean aperture first, then rotation, then mirror. -+ // -+ // In the kAxisAngleToOrientation array, the first dimension is axis (with an -+ // offset of 1). The second dimension is angle. -+ constexpr std::array, 3> -+ kAxisAngleToOrientation = {{ -+ // No mirroring. -+ {ImageOrientationEnum::kOriginTopLeft, -+ ImageOrientationEnum::kOriginLeftBottom, -+ ImageOrientationEnum::kOriginBottomRight, -+ ImageOrientationEnum::kOriginRightTop}, -+ // Top-to-bottom mirroring. Change Top<->Bottom in the first row. -+ {ImageOrientationEnum::kOriginBottomLeft, -+ ImageOrientationEnum::kOriginLeftTop, -+ ImageOrientationEnum::kOriginTopRight, -+ ImageOrientationEnum::kOriginRightBottom}, -+ // Left-to-right mirroring. Change Left<->Right in the first row. -+ {ImageOrientationEnum::kOriginTopRight, -+ ImageOrientationEnum::kOriginRightBottom, -+ ImageOrientationEnum::kOriginBottomLeft, -+ ImageOrientationEnum::kOriginLeftTop}, -+ }}; -+ orientation_ = kAxisAngleToOrientation[axis + 1][angle]; -+ -+ // Determine whether the image can be decoded to YUV. -+ // * Alpha channel is not supported. -+ // * Multi-frame images (animations) are not supported. (The DecodeToYUV() -+ // method does not have an 'index' parameter.) -+ allow_decode_to_yuv_ = -+ avif_yuv_format_ != AVIF_PIXEL_FORMAT_YUV400 && !decoder_->alphaPresent && -+ decoded_frame_count_ == 1 && -+ GetColorSpace(container).ToSkYUVColorSpace(container->depth, -+ &yuv_color_space_) && -+ // TODO(crbug.com/911246): Support color space transforms for YUV decodes. -+ !ColorTransform(); -+ -+ // Record bpp information only for 8-bit, color, still images that do not have -+ // alpha. -+ if (container->depth == 8 && avif_yuv_format_ != AVIF_PIXEL_FORMAT_YUV400 && -+ !decoder_->alphaPresent && decoded_frame_count_ == 1) { -+ static constexpr char kType[] = "Avif"; -+ update_bpp_histogram_callback_ = base::BindOnce(&UpdateBppHistogram); -+ } -+ -+ unsigned width = container->width; -+ unsigned height = container->height; -+ // If the image is cropped, pass the size of the cropped image (the clean -+ // aperture) to SetSize(). -+ if (container->transformFlags & AVIF_TRANSFORM_CLAP) { -+ AVIFCleanApertureType clap_type; -+ avifCropRect crop_rect; -+ avifDiagnostics diag; -+ avifBool valid_clap = avifCropRectConvertCleanApertureBox( -+ &crop_rect, &container->clap, container->width, container->height, -+ container->yuvFormat, &diag); -+ if (!valid_clap) { -+ DVLOG(1) << "Invalid 'clap' property: " << diag.error -+ << "; showing the full image."; -+ clap_type = AVIFCleanApertureType::kInvalid; -+ ignore_clap_ = true; -+ } else if (crop_rect.x != 0 || crop_rect.y != 0) { -+ // To help discourage the creation of files with privacy risks, also -+ // consider 'clap' properties whose origins are not at (0, 0) as invalid. -+ // See https://github.com/AOMediaCodec/av1-avif/issues/188 and -+ // https://github.com/AOMediaCodec/av1-avif/issues/189. -+ DVLOG(1) << "Origin of 'clap' property anchored to (" << crop_rect.x -+ << ", " << crop_rect.y << "); showing the full image."; -+ clap_type = AVIFCleanApertureType::kNonzeroOrigin; -+ ignore_clap_ = true; -+ } else { -+ clap_type = AVIFCleanApertureType::kZeroOrigin; -+ clap_origin_.SetPoint(crop_rect.x, crop_rect.y); -+ width = crop_rect.width; -+ height = crop_rect.height; -+ } -+ clap_type_ = clap_type; -+ } -+ return SetSize(width, height); -+} -+ -+avifResult AVIFImageDecoder::DecodeImage(wtf_size_t index) { -+ const auto ret = avifDecoderNthImage(decoder_.get(), index); -+ // |index| should be less than what DecodeFrameCount() returns, so we should -+ // not get the AVIF_RESULT_NO_IMAGES_REMAINING error. -+ DCHECK_NE(ret, AVIF_RESULT_NO_IMAGES_REMAINING); -+ if (ret != AVIF_RESULT_OK && ret != AVIF_RESULT_WAITING_ON_IO) { -+ DVLOG(1) << "avifDecoderNthImage(" << index -+ << ") failed: " << avifResultToString(ret) << ": " -+ << AvifDecoderErrorMessage(decoder_.get()); -+ return ret; -+ } -+ -+ const auto* image = GetDecoderImage(); -+ CHECK(image); -+ // Frame size must be equal to container size. -+ if (image->width != container_width_ || image->height != container_height_) { -+ DVLOG(1) << "Frame size " << image->width << "x" << image->height -+ << " differs from container size " << container_width_ << "x" -+ << container_height_; -+ return AVIF_RESULT_UNKNOWN_ERROR; -+ } -+ // Frame bit depth must be equal to container bit depth. -+ if (image->depth != bit_depth_) { -+ DVLOG(1) << "Frame bit depth must be equal to container bit depth"; -+ return AVIF_RESULT_UNKNOWN_ERROR; -+ } -+ // Frame YUV format must be equal to container YUV format. -+ if (image->yuvFormat != avif_yuv_format_) { -+ DVLOG(1) << "Frame YUV format must be equal to container YUV format"; -+ return AVIF_RESULT_UNKNOWN_ERROR; -+ } -+ -+ decoded_image_ = image; -+ if ((image->transformFlags & AVIF_TRANSFORM_CLAP) && !ignore_clap_) { -+ CropDecodedImage(); -+ } -+ -+ if (ret == AVIF_RESULT_OK) { -+ if (IsAllDataReceived() && update_bpp_histogram_callback_) { -+ std::move(update_bpp_histogram_callback_).Run(Size(), data_->size()); -+ } -+ -+ if (clap_type_.has_value()) { -+ base::UmaHistogramEnumeration("Blink.ImageDecoders.Avif.CleanAperture", -+ clap_type_.value()); -+ clap_type_.reset(); -+ } -+ } -+ return ret; -+} -+ -+void AVIFImageDecoder::CropDecodedImage() { -+ DCHECK_NE(decoded_image_, cropped_image_.get()); -+ if (!cropped_image_) { -+ cropped_image_.reset(avifImageCreateEmpty()); -+ } -+ avifCropRect rect; -+ rect.x = clap_origin_.x(); -+ rect.y = clap_origin_.y(); -+ rect.width = Size().width(); -+ rect.height = Size().height(); -+ const avifResult result = -+ avifImageSetViewRect(cropped_image_.get(), decoded_image_, &rect); -+ CHECK_EQ(result, AVIF_RESULT_OK); -+ decoded_image_ = cropped_image_.get(); -+} -+ -+bool AVIFImageDecoder::RenderImage(const avifImage* image, -+ int from_row, -+ int* to_row, -+ ImageFrame* buffer) { -+ DCHECK_LT(from_row, *to_row); -+ -+ // libavif uses libyuv for the YUV 4:2:0 to RGB upsampling and/or conversion -+ // as follows: -+ // - convert the top RGB row 0, -+ // - convert the RGB rows 1 and 2, then RGB rows 3 and 4 etc., -+ // - convert the bottom (odd) RGB row if there is an even number of RGB rows. -+ // -+ // Unfortunately this cannot be applied incrementally as is. The RGB values -+ // would differ because the first and last RGB rows have a formula using only -+ // one UV row, while the other RGB rows use two UV rows as input each. -+ // See https://crbug.com/libyuv/934. -+ // -+ // The workaround is a backup of the last converted even RGB row, called top -+ // row, located right before |from_row|. The conversion is then called -+ // starting at this top row, overwriting it with invalid values. The remaining -+ // pairs of rows are correctly aligned and their freshly converted values are -+ // valid. Then the backed up row is put back, fixing the issue. -+ // The bottom row is postponed if the other half of the pair it belongs to is -+ // not yet decoded. -+ // -+ // UV rows | Y/RGB rows -+ // | all | first decoding | second decoding -+ // ____ 0 ____ 0 (from_row) -+ // 0 ---- ____ 1 ____ 1 -+ // ____ 2 ____ 2 ____ 2 (backed up) -+ // 1 ---- ____ 3 ____ 3 (postponed) ____ 3 (from_row) -+ // ____ 4 4 (*to_row) ____ 4 -+ // 2 ---- ____ 5 ____ 5 -+ // 6 (*to_row) -+ -+ const bool use_libyuv_bilinear_upsampling = -+ !decode_to_half_float_ && image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420; -+ const bool save_top_row = use_libyuv_bilinear_upsampling && from_row > 0; -+ const bool postpone_bottom_row = -+ use_libyuv_bilinear_upsampling && -+ static_cast(*to_row) < image->height; -+ if (postpone_bottom_row) { -+ // libavif outputs an even number of rows because 4:2:0 samples are decoded -+ // in pairs. -+ DCHECK(!(*to_row & 1)); -+ --*to_row; -+ if (from_row == *to_row) { -+ return true; // Nothing to do. -+ } -+ } -+ if (save_top_row) { -+ // |from_row| is odd because it is equal to the output value of |*to_row| -+ // from the previous RenderImage() call, and |*to_row| was even and then -+ // decremented at that time. -+ DCHECK(from_row & 1); -+ --from_row; -+ } -+ -+ // Focus |image| on rows [from_row, *to_row). -+ std::unique_ptr view( -+ nullptr, avifImageDestroy); -+ if (from_row > 0 || static_cast(*to_row) < image->height) { -+ const avifCropRect rect = {0, static_cast(from_row), image->width, -+ static_cast(*to_row - from_row)}; -+ view.reset(avifImageCreateEmpty()); -+ const avifResult result = avifImageSetViewRect(view.get(), image, &rect); -+ CHECK_EQ(result, AVIF_RESULT_OK); -+ image = view.get(); -+ } -+ -+ avifRGBImage rgb_image; -+ avifRGBImageSetDefaults(&rgb_image, image); -+ -+ if (decode_to_half_float_) { -+ rgb_image.depth = 16; -+ rgb_image.isFloat = AVIF_TRUE; -+ rgb_image.pixels = -+ reinterpret_cast(buffer->GetAddrF16(0, from_row)); -+ rgb_image.rowBytes = image->width * sizeof(uint64_t); -+ // When decoding to half float, the pixel ordering is always RGBA on all -+ // platforms. -+ rgb_image.format = AVIF_RGB_FORMAT_RGBA; -+ } else { -+ rgb_image.depth = 8; -+ rgb_image.pixels = reinterpret_cast(buffer->GetAddr(0, from_row)); -+ rgb_image.rowBytes = image->width * sizeof(uint32_t); -+ // When decoding to 8-bit, Android uses little-endian RGBA pixels. All other -+ // platforms use BGRA pixels. -+ static_assert(SK_B32_SHIFT == 16 - SK_R32_SHIFT); -+ static_assert(SK_G32_SHIFT == 8); -+ static_assert(SK_A32_SHIFT == 24); -+#if SK_B32_SHIFT -+ rgb_image.format = AVIF_RGB_FORMAT_RGBA; -+#else -+ rgb_image.format = AVIF_RGB_FORMAT_BGRA; -+#endif -+ } -+ rgb_image.alphaPremultiplied = buffer->PremultiplyAlpha(); -+ rgb_image.maxThreads = decoder_->maxThreads; -+ -+ if (save_top_row) { -+ previous_last_decoded_row_.resize(rgb_image.rowBytes); -+ memcpy(previous_last_decoded_row_.data(), rgb_image.pixels, -+ rgb_image.rowBytes); -+ } -+ const avifResult result = avifImageYUVToRGB(image, &rgb_image); -+ if (save_top_row) { -+ memcpy(rgb_image.pixels, previous_last_decoded_row_.data(), -+ rgb_image.rowBytes); -+ } -+ return result == AVIF_RESULT_OK; -+} -+ -+void AVIFImageDecoder::ColorCorrectImage(int from_row, -+ int to_row, -+ ImageFrame* buffer) { -+ // Postprocess the image data according to the profile. -+ const ColorProfileTransform* const transform = ColorTransform(); -+ if (!transform) { -+ return; -+ } -+ const auto alpha_format = (buffer->HasAlpha() && buffer->PremultiplyAlpha()) -+ ? skcms_AlphaFormat_PremulAsEncoded -+ : skcms_AlphaFormat_Unpremul; -+ if (decode_to_half_float_) { -+ const skcms_PixelFormat color_format = skcms_PixelFormat_RGBA_hhhh; -+ for (int y = from_row; y < to_row; ++y) { -+ ImageFrame::PixelDataF16* const row = buffer->GetAddrF16(0, y); -+ const bool success = skcms_Transform( -+ row, color_format, alpha_format, transform->SrcProfile(), row, -+ color_format, alpha_format, transform->DstProfile(), Size().width()); -+ DCHECK(success); -+ } -+ } else { -+ const skcms_PixelFormat color_format = XformColorFormat(); -+ for (int y = from_row; y < to_row; ++y) { -+ ImageFrame::PixelData* const row = buffer->GetAddr(0, y); -+ const bool success = skcms_Transform( -+ row, color_format, alpha_format, transform->SrcProfile(), row, -+ color_format, alpha_format, transform->DstProfile(), Size().width()); -+ DCHECK(success); -+ } -+ } -+} -+ -+bool AVIFImageDecoder::GetGainmapInfoAndData( -+ SkGainmapInfo& out_gainmap_info, -+ scoped_refptr& out_gainmap_data) const { -+ if (!base::FeatureList::IsEnabled(features::kAvifGainmapHdrImages)) { -+ return false; -+ } -+ // Ensure that parsing succeeded. -+ if (!IsDecodedSizeAvailable()) { -+ return false; -+ } -+ if (!decoder_->image->gainMap) { -+ return false; -+ } -+ const avifGainMap& gain_map = *decoder_->image->gainMap; -+ if (gain_map.baseHdrHeadroom.d == 0 || gain_map.alternateHdrHeadroom.d == 0) { -+ DVLOG(1) << "Invalid gainmap metadata: a denominator value is zero"; -+ return false; -+ } -+ const float base_headroom = std::exp2( -+ FractionToFloat(gain_map.baseHdrHeadroom.n, gain_map.baseHdrHeadroom.d)); -+ const float alternate_headroom = std::exp2(FractionToFloat( -+ gain_map.alternateHdrHeadroom.n, gain_map.alternateHdrHeadroom.d)); -+ const bool base_is_hdr = base_headroom > alternate_headroom; -+ out_gainmap_info.fDisplayRatioSdr = -+ base_is_hdr ? alternate_headroom : base_headroom; -+ out_gainmap_info.fDisplayRatioHdr = -+ base_is_hdr ? base_headroom : alternate_headroom; -+ out_gainmap_info.fBaseImageType = base_is_hdr -+ ? SkGainmapInfo::BaseImageType::kHDR -+ : SkGainmapInfo::BaseImageType::kSDR; -+ if (!gain_map.useBaseColorSpace) { -+ // Try to use the alternate image's color space. -+ out_gainmap_info.fGainmapMathColorSpace = -+ GetAltImageColorSpace(*decoder_->image); -+ } -+ for (int i = 0; i < 3; ++i) { -+ if (gain_map.gainMapMin[i].d == 0 || gain_map.gainMapMax[i].d == 0 || -+ gain_map.gainMapGamma[i].d == 0 || gain_map.baseOffset[i].d == 0 || -+ gain_map.alternateOffset[i].d == 0) { -+ DVLOG(1) << "Invalid gainmap metadata: a denominator value is zero"; -+ return false; -+ } -+ if (gain_map.gainMapGamma[i].n == 0) { -+ DVLOG(1) << "Invalid gainmap metadata: gamma is zero"; -+ return false; -+ } -+ -+ const float min_log2 = -+ FractionToFloat(gain_map.gainMapMin[i].n, gain_map.gainMapMin[i].d); -+ const float max_log2 = -+ FractionToFloat(gain_map.gainMapMax[i].n, gain_map.gainMapMax[i].d); -+ out_gainmap_info.fGainmapRatioMin[i] = std::exp2(min_log2); -+ out_gainmap_info.fGainmapRatioMax[i] = std::exp2(max_log2); -+ -+ // Numerator and denominator intentionally swapped to get 1.0/gamma. -+ out_gainmap_info.fGainmapGamma[i] = -+ FractionToFloat(gain_map.gainMapGamma[i].d, gain_map.gainMapGamma[i].n); -+ const float base_offset = -+ FractionToFloat(gain_map.baseOffset[i].n, gain_map.baseOffset[i].d); -+ const float alternate_offset = FractionToFloat( -+ gain_map.alternateOffset[i].n, gain_map.alternateOffset[i].d); -+ out_gainmap_info.fEpsilonSdr[i] = -+ base_is_hdr ? alternate_offset : base_offset; -+ out_gainmap_info.fEpsilonHdr[i] = -+ base_is_hdr ? base_offset : alternate_offset; -+ } -+ out_gainmap_data = data_; -+ return true; -+} -+ -+avifImage* AVIFImageDecoder::GetDecoderImage() const { -+ if (aux_image_ == cc::AuxImage::kGainmap) { -+ if (!decoder_->image->gainMap) { -+ DVLOG(1) << "Attempted to access gain map image, but gainMap is nullptr"; -+ return nullptr; -+ } -+ return decoder_->image->gainMap->image; -+ } -+ return decoder_->image; -+} -+ -+AVIFImageDecoder::AvifIOData::AvifIOData() = default; -+AVIFImageDecoder::AvifIOData::AvifIOData( -+ scoped_refptr reader, -+ bool all_data_received) -+ : reader(std::move(reader)), all_data_received(all_data_received) {} -+AVIFImageDecoder::AvifIOData::~AvifIOData() = default; -+ -+} // namespace blink -diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h -new file mode 100644 -index 0000000000000..75247663ef158 ---- /dev/null -+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h -@@ -0,0 +1,191 @@ -+// Copyright 2020 The Chromium Authors -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ -+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ -+ -+#include -+#include -+ -+#include "base/functional/callback.h" -+#include "third_party/blink/renderer/platform/allow_discouraged_type.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" -+#include "third_party/blink/renderer/platform/wtf/vector.h" -+#include "third_party/libavif/src/include/avif/avif.h" -+#include "third_party/skia/include/core/SkImageInfo.h" -+#include "ui/gfx/color_space.h" -+#include "ui/gfx/geometry/point.h" -+ -+namespace blink { -+ -+class FastSharedBufferReader; -+ -+class PLATFORM_EXPORT AVIFImageDecoder final : public ImageDecoder { -+ public: -+ AVIFImageDecoder(AlphaOption, -+ HighBitDepthDecodingOption, -+ ColorBehavior, -+ cc::AuxImage, -+ wtf_size_t max_decoded_bytes, -+ AnimationOption); -+ AVIFImageDecoder(const AVIFImageDecoder&) = delete; -+ AVIFImageDecoder& operator=(const AVIFImageDecoder&) = delete; -+ ~AVIFImageDecoder() override; -+ -+ // ImageDecoder: -+ String FilenameExtension() const override; -+ const AtomicString& MimeType() const override; -+ bool ImageIsHighBitDepth() override; -+ void OnSetData(scoped_refptr data) override; -+ bool GetGainmapInfoAndData( -+ SkGainmapInfo& out_gainmap_info, -+ scoped_refptr& out_gainmap_data) const override; -+ cc::YUVSubsampling GetYUVSubsampling() const override; -+ gfx::Size DecodedYUVSize(cc::YUVIndex) const override; -+ wtf_size_t DecodedYUVWidthBytes(cc::YUVIndex) const override; -+ SkYUVColorSpace GetYUVColorSpace() const override; -+ uint8_t GetYUVBitDepth() const override; -+ std::optional GetHDRMetadata() const override; -+ void DecodeToYUV() override; -+ int RepetitionCount() const override; -+ bool FrameIsReceivedAtIndex(wtf_size_t) const override; -+ std::optional FrameTimestampAtIndex( -+ wtf_size_t) const override; -+ base::TimeDelta FrameDurationAtIndex(wtf_size_t) const override; -+ bool ImageHasBothStillAndAnimatedSubImages() const override; -+ -+ // Returns true if the data in fast_reader begins with a valid FileTypeBox -+ // (ftyp) that supports the brand 'avif' or 'avis'. -+ static bool MatchesAVIFSignature(const FastSharedBufferReader& fast_reader); -+ -+ gfx::ColorSpace GetColorSpaceForTesting() const; -+ -+ private: -+ // If the AVIF image has a clean aperture ('clap') property, what kind of -+ // clean aperture it is. Values synced with 'AVIFCleanApertureType' in -+ // src/tools/metrics/histograms/enums.xml. -+ // -+ // These values are persisted to logs. Entries should not be renumbered and -+ // numeric values should never be reused. -+ enum class AVIFCleanApertureType { -+ kInvalid = 0, // The clean aperture property is invalid. -+ kNonzeroOrigin = 1, // The origin of the clean aperture is not (0, 0). -+ kZeroOrigin = 2, // The origin of the clean aperture is (0, 0). -+ kMaxValue = kZeroOrigin, -+ }; -+ -+ struct AvifIOData { -+ AvifIOData(); -+ AvifIOData(scoped_refptr reader, -+ bool all_data_received); -+ ~AvifIOData(); -+ -+ scoped_refptr reader; -+ std::vector buffer ALLOW_DISCOURAGED_TYPE("Required by libavif"); -+ bool all_data_received = false; -+ }; -+ -+ void ParseMetadata(); -+ -+ // ImageDecoder: -+ void DecodeSize() override; -+ wtf_size_t DecodeFrameCount() override; -+ void InitializeNewFrame(wtf_size_t) override; -+ void Decode(wtf_size_t) override; -+ bool CanReusePreviousFrameBuffer(wtf_size_t) const override; -+ -+ // Implements avifIOReadFunc, the |read| function in the avifIO struct. -+ static avifResult ReadFromSegmentReader(avifIO* io, -+ uint32_t read_flags, -+ uint64_t offset, -+ size_t size, -+ avifROData* out); -+ -+ // Creates |decoder_| if not yet created and decodes the size and frame count. -+ bool UpdateDemuxer(); -+ -+ // Decodes the frame at index |index| and checks if the frame's size, bit -+ // depth, and YUV format matches those reported by the container. The decoded -+ // frame is available in decoded_image_. -+ avifResult DecodeImage(wtf_size_t index); -+ -+ // Crops |decoded_image_|. -+ void CropDecodedImage(); -+ -+ // Renders the rows [from_row, *to_row) of |image| to |buffer|. Returns -+ // whether |image| was rendered successfully. On return, the in/out argument -+ // |*to_row| may be decremented in case of subsampled chroma needing more -+ // data. -+ bool RenderImage(const avifImage* image, -+ int from_row, -+ int* to_row, -+ ImageFrame* buffer); -+ -+ // Applies color profile correction to the rows [from_row, to_row) of -+ // |buffer|, if desired. -+ void ColorCorrectImage(int from_row, int to_row, ImageFrame* buffer); -+ -+ // Returns decoder_->image or decoder_->image->gainMap->image depending on -+ // aux_image_. May be nullptr if requesting the gain map image -+ // (cc::AuxImage::kGainmap) but no gain map is present. -+ avifImage* GetDecoderImage() const; -+ -+ bool have_parsed_current_data_ = false; -+ // The image width and height (before cropping, if any) from the container. -+ // -+ // Note: container_width_, container_height_, decoder_->image->width, and -+ // decoder_->image->height are the width and height of the full image. Size() -+ // returns the size of the cropped image (the clean aperture). -+ uint32_t container_width_ = 0; -+ uint32_t container_height_ = 0; -+ // The bit depth from the container. -+ uint8_t bit_depth_ = 0; -+ bool decode_to_half_float_ = false; -+ uint8_t chroma_shift_x_ = 0; -+ uint8_t chroma_shift_y_ = 0; -+ std::optional hdr_metadata_; -+ bool progressive_ = false; -+ // Number of displayed rows for a non-progressive still image. -+ int incrementally_displayed_height_ = 0; -+ // The YUV format from the container. -+ avifPixelFormat avif_yuv_format_ = AVIF_PIXEL_FORMAT_NONE; -+ wtf_size_t decoded_frame_count_ = 0; -+ SkYUVColorSpace yuv_color_space_ = SkYUVColorSpace::kIdentity_SkYUVColorSpace; -+ // Used to call UpdateBppHistogram<"Avif">() at most once to record the -+ // bits-per-pixel value of the image when the image is successfully decoded. -+ base::OnceCallback update_bpp_histogram_callback_; -+ std::optional clap_type_; -+ // Whether the 'clap' (clean aperture) property should be ignored, e.g. -+ // because the 'clap' property is invalid or unsupported. -+ bool ignore_clap_ = false; -+ // The origin (top left corner) of the clean aperture. Used only when the -+ // image has a valid 'clap' (clean aperture) property. -+ gfx::Point clap_origin_; -+ // A copy of decoder_->image with the width, height, and plane buffers -+ // adjusted to those of the clean aperture. Used only when the image has a -+ // 'clap' (clean aperture) property. -+ std::unique_ptr cropped_image_{ -+ nullptr, avifImageDestroy}; -+ // Set by a successful DecodeImage() call to either decoder_->image or -+ // cropped_image_.get() depending on whether the image has a 'clap' (clean -+ // aperture) property. -+ raw_ptr decoded_image_ = nullptr; -+ // The declaration order of the next three fields is important. decoder_ -+ // points to avif_io_, and avif_io_ points to avif_io_data_. The destructor -+ // must destroy them in that order. -+ AvifIOData avif_io_data_; -+ avifIO avif_io_ = {}; -+ std::unique_ptr decoder_{ -+ nullptr, avifDecoderDestroy}; -+ -+ const AnimationOption animation_option_; -+ -+ // Used temporarily for incremental decoding and for some YUV to RGB color -+ // conversions. -+ Vector previous_last_decoded_row_; -+}; -+ -+} // namespace blink -+ -+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_AVIF_AVIF_IMAGE_DECODER_H_ -diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc -new file mode 100644 -index 0000000000000..f337ad075244a ---- /dev/null -+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_fuzzer.cc -@@ -0,0 +1,30 @@ -+// Copyright 2024 The Chromium Authors -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" -+ -+#include -+#include -+#include -+ -+#include -+ -+#include "third_party/blink/renderer/platform/graphics/color_behavior.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h" -+#include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h" -+#include "third_party/blink/renderer/platform/testing/task_environment.h" -+#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" -+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h" -+ -+namespace blink { -+ -+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { -+ static BlinkFuzzerTestSupport test_support; -+ FuzzedDataProvider fdp(data, size); -+ FuzzDecoder(DecoderType::kAvifDecoder, fdp); -+ return 0; -+} -+ -+} // namespace blink -diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc -new file mode 100644 -index 0000000000000..9ec2b420bdf7d ---- /dev/null -+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc -@@ -0,0 +1,1758 @@ -+// Copyright 2020 The Chromium Authors -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifdef UNSAFE_BUFFERS_BUILD -+// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -+#pragma allow_unsafe_buffers -+#endif -+ -+#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "base/barrier_closure.h" -+#include "base/bit_cast.h" -+#include "base/functional/bind.h" -+#include "base/strings/stringprintf.h" -+#include "base/synchronization/waitable_event.h" -+#include "base/task/thread_pool.h" -+#include "base/test/metrics/histogram_tester.h" -+#include "base/test/scoped_feature_list.h" -+#include "base/test/task_environment.h" -+#include "testing/gtest/include/gtest/gtest.h" -+#include "third_party/blink/public/common/features.h" -+#include "third_party/blink/public/platform/platform.h" -+#include "third_party/blink/renderer/platform/image-decoders/image_decoder_test_helpers.h" -+#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" -+#include "ui/gfx/color_space.h" -+#include "ui/gfx/color_transform.h" -+ -+#define FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM 0 -+#define FIXME_SUPPORT_ICC_PROFILE_TRANSFORM 0 -+#define FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS 0 -+ -+namespace blink { -+ -+namespace { -+ -+std::unique_ptr CreateAVIFDecoderWithOptions( -+ ImageDecoder::AlphaOption alpha_option, -+ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_option, -+ ColorBehavior color_behavior, -+ cc::AuxImage aux_image, -+ ImageDecoder::AnimationOption animation_option) { -+ return std::make_unique( -+ alpha_option, high_bit_depth_option, color_behavior, aux_image, -+ ImageDecoder::kNoDecodedImageByteLimit, animation_option); -+} -+ -+std::unique_ptr CreateAVIFDecoder() { -+ return CreateAVIFDecoderWithOptions( -+ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth, -+ ColorBehavior::kTag, cc::AuxImage::kDefault, -+ ImageDecoder::AnimationOption::kUnspecified); -+} -+ -+std::unique_ptr CreateGainMapAVIFDecoder() { -+ return CreateAVIFDecoderWithOptions( -+ ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth, -+ ColorBehavior::kTag, cc::AuxImage::kGainmap, -+ ImageDecoder::AnimationOption::kUnspecified); -+} -+ -+struct ExpectedColor { -+ gfx::Point point; -+ SkColor color; -+}; -+ -+enum class ColorType { -+ kRgb, -+ kRgbA, -+ kMono, -+ kMonoA, -+}; -+ -+struct StaticColorCheckParam { -+ const char* path; -+ int bit_depth; -+ ColorType color_type; -+ ImageDecoder::CompressionFormat compression_format; -+ ImageDecoder::AlphaOption alpha_option; -+ ColorBehavior color_behavior; -+ ImageOrientationEnum orientation = ImageOrientationEnum::kDefault; -+ int color_threshold; -+ std::vector colors; -+}; -+ -+std::ostream& operator<<(std::ostream& os, const StaticColorCheckParam& param) { -+ const char* color_type; -+ switch (param.color_type) { -+ case ColorType::kRgb: -+ color_type = "kRgb"; -+ break; -+ case ColorType::kRgbA: -+ color_type = "kRgbA"; -+ break; -+ case ColorType::kMono: -+ color_type = "kMono"; -+ break; -+ case ColorType::kMonoA: -+ color_type = "kMonoA"; -+ break; -+ } -+ const char* alpha_option = -+ (param.alpha_option == ImageDecoder::kAlphaPremultiplied -+ ? "kAlphaPremultiplied" -+ : "kAlphaNotPremultiplied"); -+ const char* color_behavior; -+ if (param.color_behavior == ColorBehavior::kIgnore) { -+ color_behavior = "Ignore"; -+ } else if (param.color_behavior == ColorBehavior::kTag) { -+ color_behavior = "Tag"; -+ } else { -+ DCHECK(param.color_behavior == ColorBehavior::kTransformToSRGB); -+ color_behavior = "TransformToSRGB"; -+ } -+ const char* orientation; -+ switch (param.orientation) { -+ case ImageOrientationEnum::kOriginTopLeft: -+ orientation = "kOriginTopLeft"; -+ break; -+ case ImageOrientationEnum::kOriginTopRight: -+ orientation = "kOriginTopRight"; -+ break; -+ case ImageOrientationEnum::kOriginBottomRight: -+ orientation = "kOriginBottomRight"; -+ break; -+ case ImageOrientationEnum::kOriginBottomLeft: -+ orientation = "kOriginBottomLeft"; -+ break; -+ case ImageOrientationEnum::kOriginLeftTop: -+ orientation = "kOriginLeftTop"; -+ break; -+ case ImageOrientationEnum::kOriginRightTop: -+ orientation = "kOriginRightTop"; -+ break; -+ case ImageOrientationEnum::kOriginRightBottom: -+ orientation = "kOriginRightBottom"; -+ break; -+ case ImageOrientationEnum::kOriginLeftBottom: -+ orientation = "kOriginLeftBottom"; -+ break; -+ } -+ return os << "\nStaticColorCheckParam {\n path: \"" << param.path -+ << "\",\n bit_depth: " << param.bit_depth -+ << ",\n color_type: " << color_type -+ << ",\n alpha_option: " << alpha_option -+ << ",\n color_behavior: " << color_behavior -+ << ",\n orientation: " << orientation << "\n}"; -+} -+ -+StaticColorCheckParam kTestParams[] = { -+ { -+ "/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLossyFormat, -+ ImageDecoder::kAlphaNotPremultiplied, // q=60(lossy) -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ {}, // we just check that this image is lossy. -+ }, -+ { -+ "/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLossyFormat, -+ ImageDecoder::kAlphaNotPremultiplied, // q=60(lossy) -+ ColorBehavior::kIgnore, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ {}, // we just check that the decoder won't crash when -+ // ColorBehavior::kIgnore is used. -+ }, -+ {"/images/resources/avif/red-with-alpha-8bpc.avif", -+ 8, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 3, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(127, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-unspecified-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/silver-full-range-srgb-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, -+ }}, -+ {"/images/resources/avif/silver-400-matrix-6.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, -+ }}, -+ {"/images/resources/avif/silver-400-matrix-0.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 192, 192, 192)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 192, 192, 192)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-limited-range-8bpc.avif", -+ 8, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-full-range-8bpc.avif", -+ 8, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+ {"/images/resources/avif/red-with-alpha-8bpc.avif", -+ 8, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 4, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(127, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM -+ {"/images/resources/avif/red-with-profile-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kIgnore, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, -+ }}, -+#endif -+#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM -+ {"/images/resources/avif/red-with-profile-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ /* -+ * "Color Spin" ICC profile, embedded in this image, -+ * changes blue to red. -+ */ -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+#endif -+ {"/images/resources/avif/red-with-alpha-10bpc.avif", -+ 10, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 2, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-with-alpha-10bpc.avif", -+ 10, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 2, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-420-10bpc.avif", -+ 10, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-limited-range-10bpc.avif", -+ 10, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-full-range-10bpc.avif", -+ 10, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM -+ {"/images/resources/avif/red-with-profile-10bpc.avif", -+ 10, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kIgnore, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, -+ }}, -+#endif -+#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM -+ {"/images/resources/avif/red-with-profile-10bpc.avif", -+ 10, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ /* -+ * "Color Spin" ICC profile, embedded in this image, -+ * changes blue to red. -+ */ -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+#endif -+ {"/images/resources/avif/red-with-alpha-12bpc.avif", -+ 12, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 2, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-with-alpha-12bpc.avif", -+ 12, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 2, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(0, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(128, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-420-12bpc.avif", -+ 12, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-limited-range-12bpc.avif", -+ 12, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+ {"/images/resources/avif/alpha-mask-full-range-12bpc.avif", -+ 12, -+ ColorType::kMono, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 128, 128, 128)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 255, 255)}, -+ }}, -+#if FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM -+ {"/images/resources/avif/red-with-profile-12bpc.avif", -+ 12, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kIgnore, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 0, 0, 255)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 0, 0, 255)}, -+ }}, -+#endif -+#if FIXME_SUPPORT_ICC_PROFILE_TRANSFORM -+ {"/images/resources/avif/red-with-profile-12bpc.avif", -+ 12, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTransformToSRGB, -+ ImageOrientationEnum::kOriginTopLeft, -+ 1, -+ { -+ /* -+ * "Color Spin" ICC profile, embedded in this image, -+ * changes blue to red. -+ */ -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+#endif -+ {"/images/resources/avif/red-and-purple-crop.avif", -+ 8, -+ ColorType::kRgbA, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopLeft, -+ 0, -+ { -+ // The clean aperture's size is 200x50. The left half is red and the -+ // right half is purple. Alpha values in the clean aperture are 255. -+ // (Alpha values to the right of the clean aperture are 128.) -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, // red -+ {gfx::Point(99, 24), SkColorSetARGB(255, 255, 0, 0)}, // red -+ {gfx::Point(100, 25), SkColorSetARGB(255, 127, 0, 128)}, // purple -+ {gfx::Point(199, 49), SkColorSetARGB(255, 127, 0, 128)}, // purple -+ }}, -+ {"/images/resources/avif/red-full-range-angle-1-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginLeftBottom, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-mode-0-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginBottomLeft, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-mode-1-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopRight, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-angle-2-mode-0-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginTopRight, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ {"/images/resources/avif/red-full-range-angle-3-mode-1-420-8bpc.avif", -+ 8, -+ ColorType::kRgb, -+ ImageDecoder::kLosslessFormat, -+ ImageDecoder::kAlphaNotPremultiplied, -+ ColorBehavior::kTag, -+ ImageOrientationEnum::kOriginLeftTop, -+ 0, -+ { -+ {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)}, -+ {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)}, -+ }}, -+ // TODO(ryoh): Add other color profile images, such as BT2020CL, -+ // SMPTE 274M -+ // TODO(ryoh): Add images with different combinations of ColorPrimaries, -+ // TransferFunction and MatrixCoefficients, -+ // such as: -+ // sRGB ColorPrimaries, BT.2020 TransferFunction and -+ // BT.709 MatrixCoefficients -+ // TODO(ryoh): Add Mono + Alpha Images. -+}; -+ -+enum class ErrorPhase { kParse, kDecode }; -+ -+// If 'error_phase' is ErrorPhase::kParse, error is expected during parse -+// (SetData() call); else error is expected during decode -+// (DecodeFrameBufferAtIndex() call). -+void TestInvalidStaticImage(const char* avif_file, ErrorPhase error_phase) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ -+ scoped_refptr data = ReadFileToSharedBuffer(avif_file); -+ ASSERT_TRUE(data.get()); -+ decoder->SetData(std::move(data), true); -+ -+ if (error_phase == ErrorPhase::kParse) { -+ EXPECT_FALSE(decoder->IsSizeAvailable()); -+ EXPECT_TRUE(decoder->Failed()); -+ EXPECT_EQ(0u, decoder->FrameCount()); -+ EXPECT_FALSE(decoder->DecodeFrameBufferAtIndex(0)); -+ } else { -+ EXPECT_TRUE(decoder->IsSizeAvailable()); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_GT(decoder->FrameCount(), 0u); -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus()); -+ EXPECT_TRUE(decoder->Failed()); -+ } -+} -+ -+float HalfFloatToUnorm(uint16_t h) { -+ const uint32_t f = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1c000) << 13) | -+ ((h & 0x03ff) << 13); -+ return base::bit_cast(f); -+} -+ -+void ReadYUV(const char* file_name, -+ const gfx::Size& expected_y_size, -+ const gfx::Size& expected_uv_size, -+ SkColorType color_type, -+ int bit_depth, -+ gfx::Point3F* rgb_pixel = nullptr) { -+ scoped_refptr data = -+ ReadFileToSharedBuffer("web_tests/images/resources/avif/", file_name); -+ ASSERT_TRUE(data); -+ -+ auto decoder = CreateAVIFDecoder(); -+ decoder->SetData(std::move(data), true); -+ -+ ASSERT_TRUE(decoder->IsDecodedSizeAvailable()); -+ ASSERT_TRUE(decoder->CanDecodeToYUV()); -+ EXPECT_NE(decoder->GetYUVSubsampling(), cc::YUVSubsampling::kUnknown); -+ EXPECT_NE(decoder->GetYUVColorSpace(), -+ SkYUVColorSpace::kIdentity_SkYUVColorSpace); -+ EXPECT_EQ(decoder->GetYUVBitDepth(), bit_depth); -+ -+ gfx::Size size = decoder->DecodedSize(); -+ gfx::Size y_size = decoder->DecodedYUVSize(cc::YUVIndex::kY); -+ gfx::Size u_size = decoder->DecodedYUVSize(cc::YUVIndex::kU); -+ gfx::Size v_size = decoder->DecodedYUVSize(cc::YUVIndex::kV); -+ -+ EXPECT_EQ(size, y_size); -+ EXPECT_EQ(u_size, v_size); -+ -+ EXPECT_EQ(expected_y_size, y_size); -+ EXPECT_EQ(expected_uv_size, u_size); -+ -+ wtf_size_t row_bytes[3]; -+ row_bytes[0] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kY); -+ row_bytes[1] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kU); -+ row_bytes[2] = decoder->DecodedYUVWidthBytes(cc::YUVIndex::kV); -+ -+ size_t planes_data_size = row_bytes[0] * y_size.height() + -+ row_bytes[1] * u_size.height() + -+ row_bytes[2] * v_size.height(); -+ auto planes_data = std::make_unique(planes_data_size); -+ -+ void* planes[3]; -+ planes[0] = planes_data.get(); -+ planes[1] = static_cast(planes[0]) + row_bytes[0] * y_size.height(); -+ planes[2] = static_cast(planes[1]) + row_bytes[1] * u_size.height(); -+ -+ decoder->SetImagePlanes( -+ std::make_unique(planes, row_bytes, color_type)); -+ -+ decoder->DecodeToYUV(); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_TRUE(decoder->HasDisplayableYUVData()); -+ -+ auto metadata = decoder->MakeMetadataForDecodeAcceleration(); -+ EXPECT_EQ(cc::ImageType::kAVIF, metadata.image_type); -+ EXPECT_EQ(size, metadata.image_size); -+ if (expected_y_size == expected_uv_size) { -+ EXPECT_EQ(cc::YUVSubsampling::k444, metadata.yuv_subsampling); -+ } else if (expected_y_size.height() == expected_uv_size.height()) { -+ EXPECT_EQ(cc::YUVSubsampling::k422, metadata.yuv_subsampling); -+ } else { -+ EXPECT_EQ(cc::YUVSubsampling::k420, metadata.yuv_subsampling); -+ } -+ -+ if (!rgb_pixel) { -+ return; -+ } -+ -+ if (bit_depth > 8) { -+ rgb_pixel->set_x(reinterpret_cast(planes[0])[0]); -+ rgb_pixel->set_y(reinterpret_cast(planes[1])[0]); -+ rgb_pixel->set_z(reinterpret_cast(planes[2])[0]); -+ } else { -+ rgb_pixel->set_x(reinterpret_cast(planes[0])[0]); -+ rgb_pixel->set_y(reinterpret_cast(planes[1])[0]); -+ rgb_pixel->set_z(reinterpret_cast(planes[2])[0]); -+ } -+ -+ if (color_type == kGray_8_SkColorType) { -+ const float max_channel = (1 << bit_depth) - 1; -+ rgb_pixel->set_x(rgb_pixel->x() / max_channel); -+ rgb_pixel->set_y(rgb_pixel->y() / max_channel); -+ rgb_pixel->set_z(rgb_pixel->z() / max_channel); -+ } else if (color_type == kA16_unorm_SkColorType) { -+ constexpr float kR16MaxChannel = 65535.0f; -+ rgb_pixel->set_x(rgb_pixel->x() / kR16MaxChannel); -+ rgb_pixel->set_y(rgb_pixel->y() / kR16MaxChannel); -+ rgb_pixel->set_z(rgb_pixel->z() / kR16MaxChannel); -+ } else { -+ DCHECK_EQ(color_type, kA16_float_SkColorType); -+ rgb_pixel->set_x(HalfFloatToUnorm(rgb_pixel->x())); -+ rgb_pixel->set_y(HalfFloatToUnorm(rgb_pixel->y())); -+ rgb_pixel->set_z(HalfFloatToUnorm(rgb_pixel->z())); -+ } -+ -+ // Convert our YUV pixel to RGB to avoid an excessive amounts of test -+ // expectations. We otherwise need bit_depth * yuv_sampling * color_type. -+ gfx::ColorTransform::Options options; -+ options.src_bit_depth = bit_depth; -+ options.dst_bit_depth = bit_depth; -+ auto transform = gfx::ColorTransform::NewColorTransform( -+ reinterpret_cast(decoder.get()) -+ ->GetColorSpaceForTesting(), -+ gfx::ColorSpace(), options); -+ transform->Transform(rgb_pixel, 1); -+} -+ -+void TestYUVRed(const char* file_name, -+ const gfx::Size& expected_uv_size, -+ SkColorType color_type = kGray_8_SkColorType, -+ int bit_depth = 8) { -+ SCOPED_TRACE(base::StringPrintf("file_name=%s, color_type=%d", file_name, -+ int{color_type})); -+ -+ constexpr gfx::Size kRedYSize(3, 3); -+ -+ gfx::Point3F decoded_pixel; -+ ASSERT_NO_FATAL_FAILURE(ReadYUV(file_name, kRedYSize, expected_uv_size, -+ color_type, bit_depth, &decoded_pixel)); -+ -+ // Allow the RGB value to be off by one step. 1/max_value is the minimum -+ // amount of error possible if error exists for integer sources. -+ // -+ // For half float values we have additional error from precision limitations, -+ // which gets worse at the extents of [-0.5, 1] -- which is the case for our R -+ // channel since we're using a pure red source. -+ // -+ // https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Precision_limitations_on_decimal_values_in_[0,_1] -+ const double kMinError = 1.0 / ((1 << bit_depth) - 1); -+ const double kError = color_type == kA16_float_SkColorType -+ ? kMinError + std::pow(2, -11) -+ : kMinError; -+ EXPECT_NEAR(decoded_pixel.x(), 1, kError); // R -+ EXPECT_NEAR(decoded_pixel.y(), 0, kMinError); // G -+ EXPECT_NEAR(decoded_pixel.z(), 0, kMinError); // B -+} -+ -+void DecodeTask(const Vector* data, base::RepeatingClosure* done) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ -+ scoped_refptr data_copy = SharedBuffer::Create(); -+ data_copy->Append(*data); -+ decoder->SetData(std::move(data_copy), true); -+ -+ EXPECT_TRUE(decoder->IsSizeAvailable()); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(decoder->FrameCount(), 1u); -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); -+ EXPECT_FALSE(decoder->Failed()); -+ -+ done->Run(); -+} -+ -+void InspectImage( -+ const StaticColorCheckParam& param, -+ ImageDecoder::HighBitDepthDecodingOption high_bit_depth_option) { -+ std::unique_ptr decoder = CreateAVIFDecoderWithOptions( -+ param.alpha_option, high_bit_depth_option, param.color_behavior, -+ cc::AuxImage::kDefault, ImageDecoder::AnimationOption::kUnspecified); -+ scoped_refptr data = ReadFileToSharedBuffer(param.path); -+ ASSERT_TRUE(data.get()); -+#if FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS -+ EXPECT_EQ(param.compression_format, -+ ImageDecoder::GetCompressionFormat(data, "image/avif")); -+#endif -+ decoder->SetData(std::move(data), true); -+ EXPECT_EQ(1u, decoder->FrameCount()); -+ EXPECT_EQ(kAnimationNone, decoder->RepetitionCount()); -+ EXPECT_EQ(param.bit_depth > 8, decoder->ImageIsHighBitDepth()); -+ auto metadata = decoder->MakeMetadataForDecodeAcceleration(); -+ EXPECT_EQ(cc::ImageType::kAVIF, metadata.image_type); -+ // TODO(wtc): Check metadata.yuv_subsampling. -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus()); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(param.orientation, decoder->Orientation()); -+ EXPECT_EQ(param.color_type == ColorType::kRgbA || -+ param.color_type == ColorType::kMonoA, -+ frame->HasAlpha()); -+ auto get_color_channel = [](SkColorChannel channel, SkColor color) { -+ switch (channel) { -+ case SkColorChannel::kR: -+ return SkColorGetR(color); -+ case SkColorChannel::kG: -+ return SkColorGetG(color); -+ case SkColorChannel::kB: -+ return SkColorGetB(color); -+ case SkColorChannel::kA: -+ return SkColorGetA(color); -+ } -+ }; -+ auto color_difference = [get_color_channel](SkColorChannel channel, -+ SkColor color1, -+ SkColor color2) -> int { -+ return std::abs(static_cast(get_color_channel(channel, color1)) - -+ static_cast(get_color_channel(channel, color2))); -+ }; -+ for (const auto& expected : param.colors) { -+ const SkBitmap& bitmap = frame->Bitmap(); -+ SkColor frame_color = -+ bitmap.getColor(expected.point.x(), expected.point.y()); -+ -+ EXPECT_LE(color_difference(SkColorChannel::kR, frame_color, expected.color), -+ param.color_threshold); -+ EXPECT_LE(color_difference(SkColorChannel::kG, frame_color, expected.color), -+ param.color_threshold); -+ EXPECT_LE(color_difference(SkColorChannel::kB, frame_color, expected.color), -+ param.color_threshold); -+ // TODO(ryoh): Create alpha_threshold field for alpha channels. -+ EXPECT_LE(color_difference(SkColorChannel::kA, frame_color, expected.color), -+ param.color_threshold); -+ if (param.color_type == ColorType::kMono || -+ param.color_type == ColorType::kMonoA) { -+ EXPECT_EQ(SkColorGetR(frame_color), SkColorGetG(frame_color)); -+ EXPECT_EQ(SkColorGetR(frame_color), SkColorGetB(frame_color)); -+ } -+ } -+} -+ -+void TestAvifBppHistogram(const char* image_name, -+ const char* histogram_name = nullptr, -+ base::HistogramBase::Sample32 sample = 0) { -+ TestBppHistogram(CreateAVIFDecoder, "Avif", image_name, histogram_name, -+ sample); -+} -+ -+struct AVIFImageParam { -+ const char* path; -+ size_t expected_frame_count; -+ int expected_repetition_count; -+}; -+ -+constexpr AVIFImageParam kAnimatedTestParams[] = { -+ // star-animated-8bpc.avif, star-animated-10bpc.avif, and -+ // star-animated-12bpc.avif contain an EditListBox whose `flags` field is -+ // equal to 0, meaning the edit list is not repeated. Therefore their -+ // `expected_repetition_count` is 0. -+ {"/images/resources/avif/star-animated-8bpc.avif", 5u, 0}, -+ {"/images/resources/avif/star-animated-8bpc-with-alpha.avif", 5u, -+ kAnimationLoopInfinite}, -+ {"/images/resources/avif/star-animated-10bpc.avif", 5u, 0}, -+ {"/images/resources/avif/star-animated-10bpc-with-alpha.avif", 5u, -+ kAnimationLoopInfinite}, -+ {"/images/resources/avif/star-animated-12bpc.avif", 5u, 0}, -+ {"/images/resources/avif/star-animated-12bpc-with-alpha.avif", 5u, -+ kAnimationLoopInfinite}, -+ {"/images/resources/avif/star-animated-8bpc-1-repetition.avif", 5u, 1}, -+ {"/images/resources/avif/star-animated-8bpc-10-repetition.avif", 5u, 10}, -+ {"/images/resources/avif/star-animated-8bpc-infinite-repetition.avif", 5u, -+ kAnimationLoopInfinite}, -+}; -+ -+constexpr AVIFImageParam kStaticTestParams[] = { -+ {"/images/resources/avif/red-at-12-oclock-with-color-profile-lossy.avif", 1, -+ kAnimationNone}, -+ {"/images/resources/avif/red-at-12-oclock-with-color-profile-8bpc.avif", 1, -+ kAnimationNone}, -+ {"/images/resources/avif/red-at-12-oclock-with-color-profile-10bpc.avif", 1, -+ kAnimationNone}, -+ {"/images/resources/avif/red-at-12-oclock-with-color-profile-12bpc.avif", 1, -+ kAnimationNone}, -+ {"/images/resources/avif/tiger_3layer_1res.avif", 1, kAnimationNone}, -+ {"/images/resources/avif/tiger_3layer_3res.avif", 1, kAnimationNone}, -+ {"/images/resources/avif/tiger_420_8b_grid1x13.avif", 1, kAnimationNone}, -+ {"/images/resources/avif/dice_444_10b_grid4x3.avif", 1, kAnimationNone}, -+ {"/images/resources/avif/gracehopper_422_12b_grid2x4.avif", 1, -+ kAnimationNone}, -+ {"/images/resources/avif/small-with-gainmap-iso.avif", 1, kAnimationNone}, -+}; -+ -+using AVIFValidImagesTest = ::testing::TestWithParam; -+ -+INSTANTIATE_TEST_SUITE_P(AnimatedAVIF, -+ AVIFValidImagesTest, -+ ::testing::ValuesIn(kAnimatedTestParams)); -+ -+INSTANTIATE_TEST_SUITE_P(StaticAVIF, -+ AVIFValidImagesTest, -+ ::testing::ValuesIn(kStaticTestParams)); -+ -+TEST_P(AVIFValidImagesTest, ByteByByteDecode) { -+ TestByteByByteDecode(&CreateAVIFDecoder, GetParam().path, -+ GetParam().expected_frame_count, -+ GetParam().expected_repetition_count); -+} -+ -+TEST(AnimatedAVIFTests, HasMultipleSubImages) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/star-animated-8bpc.avif"), -+ true); -+ EXPECT_TRUE(decoder->ImageHasBothStillAndAnimatedSubImages()); -+} -+ -+TEST(StaticAVIFTests, DoesNotHaveMultipleSubImages) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/" -+ "red-at-12-oclock-with-color-profile-8bpc.avif"), -+ true); -+ EXPECT_FALSE(decoder->ImageHasBothStillAndAnimatedSubImages()); -+} -+ -+TEST(StaticAVIFTests, HasTimingInformation) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/" -+ "red-at-12-oclock-with-color-profile-8bpc.avif"), -+ true); -+ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(0)); -+ -+ // libavif has placeholder values for timestamp and duration on still images, -+ // so any duration value is valid, but the timestamp should be zero. -+ EXPECT_EQ(base::TimeDelta(), decoder->FrameTimestampAtIndex(0)); -+} -+ -+TEST(AnimatedAVIFTests, HasTimingInformation) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/star-animated-8bpc.avif"), -+ true); -+ -+ constexpr auto kDuration = base::Milliseconds(100); -+ -+ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(0)); -+ EXPECT_EQ(base::TimeDelta(), decoder->FrameTimestampAtIndex(0)); -+ EXPECT_EQ(kDuration, decoder->FrameDurationAtIndex(0)); -+ -+ EXPECT_TRUE(!!decoder->DecodeFrameBufferAtIndex(1)); -+ EXPECT_EQ(kDuration, decoder->FrameTimestampAtIndex(1)); -+ EXPECT_EQ(kDuration, decoder->FrameDurationAtIndex(1)); -+} -+ -+TEST(StaticAVIFTests, NoCrashWhenCheckingForMultipleSubImages) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ constexpr char kHeader[] = {0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70}; -+ auto buffer = SharedBuffer::Create(); -+ buffer->Append(kHeader); -+ decoder->SetData(std::move(buffer), false); -+ EXPECT_FALSE(decoder->ImageHasBothStillAndAnimatedSubImages()); -+} -+ -+// TODO(ryoh): Add corrupted video tests. -+ -+TEST(StaticAVIFTests, invalidImages) { -+ // Image data is truncated. -+ TestInvalidStaticImage( -+ "/images/resources/avif/" -+ "red-at-12-oclock-with-color-profile-truncated.avif", -+ ErrorPhase::kParse); -+ // Chunk size in AV1 frame header doesn't match the file size. -+ TestInvalidStaticImage( -+ "/images/resources/avif/" -+ "red-at-12-oclock-with-color-profile-with-wrong-frame-header.avif", -+ ErrorPhase::kDecode); -+} -+ -+TEST(StaticAVIFTests, GetIsoGainmapInfoAndData) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ scoped_refptr data = ReadFileToSharedBuffer( -+ "/images/resources/avif/small-with-gainmap-iso.avif"); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(decoder->Size(), gfx::Size(134, 100)); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_TRUE(has_gainmap); -+ -+ // Check gainmap metadata. -+ constexpr double kEpsilon = 0.00001; -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[0], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[1], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[2], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[0], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[1], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[2], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[3], 1., kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[0], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[1], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[2], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[0], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[1], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[2], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[0], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[1], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[2], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fDisplayRatioSdr, 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fDisplayRatioHdr, std::exp2(1.4427), kEpsilon); -+ -+ EXPECT_EQ(gainmap_info.fBaseImageType, SkGainmapInfo::BaseImageType::kSDR); -+ -+ EXPECT_EQ(gainmap_info.fGainmapMathColorSpace, nullptr); -+ -+ // Check that the gainmap can be decoded. -+ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); -+ gainmap_decoder->SetData(gainmap_data, true); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(gainmap_decoder->Size(), gfx::Size(33, 25)); -+ ImageFrame* gainmap_frame = gainmap_decoder->DecodeFrameBufferAtIndex(0); -+ EXPECT_TRUE(gainmap_frame); -+} -+ -+TEST(StaticAVIFTests, GetIsoGainmapInfoAndDataHdrToSdr) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ scoped_refptr data = ReadFileToSharedBuffer( -+ "/images/resources/avif/small-with-gainmap-iso-hdrbase.avif"); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(decoder->Size(), gfx::Size(134, 100)); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_TRUE(has_gainmap); -+ -+ // Check gainmap metadata. -+ constexpr double kEpsilon = 0.00001; -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[0], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[1], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[2], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMin[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[0], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[1], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[2], std::exp2(1.4427), kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapRatioMax[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[0], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[1], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[2], 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fGainmapGamma[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[0], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[1], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[2], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonSdr[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[0], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[1], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[2], 0.015625, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fEpsilonHdr[3], 1.0, kEpsilon); -+ -+ EXPECT_NEAR(gainmap_info.fDisplayRatioSdr, 1.0, kEpsilon); -+ EXPECT_NEAR(gainmap_info.fDisplayRatioHdr, std::exp2(1.4427), kEpsilon); -+ -+ EXPECT_EQ(gainmap_info.fBaseImageType, SkGainmapInfo::BaseImageType::kHDR); -+ -+ // Check that the gainmap can be decoded. -+ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); -+ gainmap_decoder->SetData(gainmap_data, true); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(gainmap_decoder->Size(), gfx::Size(33, 25)); -+ ImageFrame* gainmap_frame = gainmap_decoder->DecodeFrameBufferAtIndex(0); -+ EXPECT_TRUE(gainmap_frame); -+} -+ -+TEST(StaticAVIFTests, GetIsoGainmapColorSpaceSameICC) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ // The image has use_base_color_space set to false (i.e. use the alternate -+ // image's color space), and the base and alternate image ICC profiles are the -+ // same, so the alternate image color space should be ignored. -+ scoped_refptr data = ReadFileToSharedBuffer( -+ "/images/resources/avif/small-with-gainmap-iso-usealtcolorspace.avif"); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_TRUE(has_gainmap); -+ -+ EXPECT_EQ(gainmap_info.fGainmapMathColorSpace, nullptr); -+} -+ -+void ExpectMatrixNear(const skcms_Matrix3x3& lhs, -+ const skcms_Matrix3x3& rhs, -+ float epsilon) { -+ for (int r = 0; r < 3; r++) { -+ for (int c = 0; c < 3; c++) { -+ EXPECT_NEAR(lhs.vals[r][c], rhs.vals[r][c], epsilon); -+ } -+ } -+} -+ -+TEST(StaticAVIFTests, GetIsoGainmapColorSpaceDifferentICC) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ // The image has use_base_color_space set to false (i.e. use the alternate -+ // image's color space), and the base and alternate image ICC profiles are -+ // different, so the alternate ICC profile should be set as -+ // fGainmapMathColorSpace. -+ // Base is sRGB, alternate is P3. -+ scoped_refptr data = ReadFileToSharedBuffer( -+ "/images/resources/avif/" -+ "small-with-gainmap-iso-usealtcolorspace-differenticc.avif"); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_TRUE(has_gainmap); -+ -+ // Check that the gain map color space is specified. -+ EXPECT_NE(gainmap_info.fGainmapMathColorSpace, nullptr); -+ // Only compare the color primaries, the transfer function is irrelevant. -+ skcms_Matrix3x3 matrix; -+ ASSERT_TRUE(gainmap_info.fGainmapMathColorSpace->toXYZD50(&matrix)); -+ ExpectMatrixNear(matrix, SkNamedGamut::kDisplayP3, 0.001); -+} -+ -+TEST(StaticAVIFTests, GetIsoGainmapColorSpaceDifferentCICP) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ // The image has use_base_color_space set to false (i.e. use the alternate -+ // image's color space), and the base and alternate images don't have ICC -+ // but CICP values instead. The alternate image's CICP values should be used. -+ // Base is sRGB, alternate is Rec 2020. -+ scoped_refptr data = ReadFileToSharedBuffer( -+ "/images/resources/avif/gainmap-sdr-srgb-to-hdr-wcg-rec2020.avif"); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_TRUE(has_gainmap); -+ -+ // Check that the gain map color space is specified. -+ EXPECT_NE(gainmap_info.fGainmapMathColorSpace, nullptr); -+ // Only compare the color primaries, the transfer function is irrelevant. -+ skcms_Matrix3x3 matrix; -+ ASSERT_TRUE(gainmap_info.fGainmapMathColorSpace->toXYZD50(&matrix)); -+ ExpectMatrixNear(matrix, SkNamedGamut::kRec2020, 0.0001); -+} -+ -+TEST(StaticAVIFTests, GetGainmapInfoAndDataWithFeatureDisabled) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{}, -+ /*disabled_features=*/{features::kAvifGainmapHdrImages}); -+ -+ const std::string image = "small-with-gainmap-iso.avif"; -+ scoped_refptr data = -+ ReadFileToSharedBuffer("web_tests/images/resources/avif", image.c_str()); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_FALSE(has_gainmap); -+ -+ // Check that we get an error if we try decoding the gain map. -+ std::unique_ptr gainmap_decoder = CreateGainMapAVIFDecoder(); -+ gainmap_decoder->SetData(data, true); -+ EXPECT_FALSE(gainmap_decoder->IsSizeAvailable()); -+ EXPECT_TRUE(gainmap_decoder->Failed()); -+ EXPECT_EQ(gainmap_decoder->FrameCount(), 0u); -+ EXPECT_FALSE(gainmap_decoder->DecodeFrameBufferAtIndex(0)); -+} -+ -+TEST(StaticAVIFTests, GetGainmapInfoAndDataWithTruncatedData) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ const std::string image = "small-with-gainmap-iso.avif"; -+ const Vector data_vector = -+ ReadFile("web_tests/images/resources/avif", image.c_str()); -+ scoped_refptr half_data = SharedBuffer::Create( -+ base::span(data_vector).first(data_vector.size() / 2)); -+ -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(half_data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_FALSE(has_gainmap); -+} -+ -+TEST(StaticAVIFTests, GetGainmapWithGammaZero) { -+ base::test::ScopedFeatureList scoped_feature_list; -+ scoped_feature_list.InitWithFeatures( -+ /*enabled_features=*/{features::kAvifGainmapHdrImages}, -+ /*disabled_features=*/{}); -+ -+ const std::string image = "small-with-gainmap-iso-gammazero.avif"; -+ scoped_refptr data = -+ ReadFileToSharedBuffer("web_tests/images/resources/avif", image.c_str()); -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(data, true); -+ SkGainmapInfo gainmap_info; -+ scoped_refptr gainmap_data; -+ const bool has_gainmap = -+ decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data); -+ ASSERT_FALSE(has_gainmap); -+} -+ -+TEST(StaticAVIFTests, YUV) { -+ // 3x3, YUV 4:2:0 -+ constexpr gfx::Size kUVSize420(2, 2); -+ TestYUVRed("red-limited-range-420-8bpc.avif", kUVSize420); -+ TestYUVRed("red-full-range-420-8bpc.avif", kUVSize420); -+ -+ // 3x3, YUV 4:2:2 -+ constexpr gfx::Size kUVSize422(2, 3); -+ TestYUVRed("red-limited-range-422-8bpc.avif", kUVSize422); -+ -+ // 3x3, YUV 4:4:4 -+ constexpr gfx::Size kUVSize444(3, 3); -+ TestYUVRed("red-limited-range-444-8bpc.avif", kUVSize444); -+ -+ // Full range BT709 color space is uncommon, but should be supported. -+ TestYUVRed("red-full-range-bt709-444-8bpc.avif", kUVSize444); -+ -+ for (const auto ct : {kA16_unorm_SkColorType, kA16_float_SkColorType}) { -+ // 3x3, YUV 4:2:0, 10bpc -+ TestYUVRed("red-limited-range-420-10bpc.avif", kUVSize420, ct, 10); -+ -+ // 3x3, YUV 4:2:2, 10bpc -+ TestYUVRed("red-limited-range-422-10bpc.avif", kUVSize422, ct, 10); -+ -+ // 3x3, YUV 4:4:4, 10bpc -+ TestYUVRed("red-limited-range-444-10bpc.avif", kUVSize444, ct, 10); -+ -+ // 3x3, YUV 4:2:0, 12bpc -+ TestYUVRed("red-limited-range-420-12bpc.avif", kUVSize420, ct, 12); -+ -+ // 3x3, YUV 4:2:2, 12bpc -+ TestYUVRed("red-limited-range-422-12bpc.avif", kUVSize422, ct, 12); -+ -+ // 3x3, YUV 4:4:4, 12bpc -+ TestYUVRed("red-limited-range-444-12bpc.avif", kUVSize444, ct, 12); -+ -+ // Various common color spaces should be supported. -+ TestYUVRed("red-full-range-bt2020-pq-444-10bpc.avif", kUVSize444, ct, 10); -+ TestYUVRed("red-full-range-bt2020-pq-444-12bpc.avif", kUVSize444, ct, 12); -+ TestYUVRed("red-full-range-bt2020-hlg-444-10bpc.avif", kUVSize444, ct, 10); -+ TestYUVRed("red-full-range-bt2020-hlg-444-12bpc.avif", kUVSize444, ct, 12); -+ } -+} -+ -+TEST(StaticAVIFTests, SizeAvailableBeforeAllDataReceived) { -+ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); -+ scoped_refptr segment_reader = -+ SegmentReader::CreateFromSharedBuffer(stream_buffer); -+ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( -+ "image/avif", segment_reader, /*data_complete=*/false, -+ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, -+ ColorBehavior::kTag, cc::AuxImage::kDefault, -+ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), -+ ImageDecoder::AnimationOption::kUnspecified); -+ EXPECT_FALSE(decoder->IsSizeAvailable()); -+ -+ Vector data = -+ ReadFile("/images/resources/avif/red-limited-range-420-8bpc.avif"); -+ stream_buffer->Append(data); -+ EXPECT_EQ(stream_buffer->size(), 318u); -+ decoder->SetData(stream_buffer, /*all_data_received=*/false); -+ // All bytes are appended so we should have size, even though we pass -+ // all_data_received=false. -+ EXPECT_TRUE(decoder->IsSizeAvailable()); -+ -+ decoder->SetData(stream_buffer, /*all_data_received=*/true); -+ EXPECT_TRUE(decoder->IsSizeAvailable()); -+} -+ -+TEST(StaticAVIFTests, ProgressiveDecoding) { -+ base::HistogramTester histogram_tester; -+ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); -+ scoped_refptr segment_reader = -+ SegmentReader::CreateFromSharedBuffer(stream_buffer); -+ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( -+ "image/avif", segment_reader, /*data_complete=*/false, -+ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, -+ ColorBehavior::kTag, cc::AuxImage::kDefault, -+ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), -+ ImageDecoder::AnimationOption::kUnspecified); -+ -+ Vector data = ReadFile("/images/resources/avif/tiger_3layer_1res.avif"); -+ ASSERT_EQ(data.size(), 70944u); -+ -+ // This image has three layers. The first layer is 8299 bytes. Because of -+ // image headers and other overhead, if we pass exactly 8299 bytes to the -+ // decoder, the decoder does not have enough data to decode the first layer. -+ stream_buffer->Append(base::span(data).first(8299u)); -+ decoder->SetData(stream_buffer, /*all_data_received=*/false); -+ EXPECT_TRUE(decoder->IsSizeAvailable()); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(decoder->FrameCount(), 1u); -+ histogram_tester.ExpectTotalCount("Blink.DecodedImage.AvifDensity.Count.02MP", -+ 0); -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameEmpty); -+ EXPECT_FALSE(decoder->Failed()); -+ -+ // An additional 301 bytes are enough data for the decoder to decode the first -+ // layer. With progressive decoding, the frame buffer status will transition -+ // to ImageFrame::kFramePartial. -+ stream_buffer->Append(base::span(data).subspan(8299u, 301u)); -+ decoder->SetData(stream_buffer, /*all_data_received=*/false); -+ EXPECT_FALSE(decoder->Failed()); -+ frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFramePartial); -+ EXPECT_FALSE(decoder->Failed()); -+ -+ base::HistogramTester::CountsMap expected_counts; -+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( -+ "Blink.DecodedImage.AvifDensity.Count."), -+ testing::ContainerEq(expected_counts)); -+ -+ // Now send the rest of the data. -+ stream_buffer->Append(base::span(data).subspan(8299u + 301u, 62344u)); -+ decoder->SetData(stream_buffer, /*all_data_received=*/true); -+ EXPECT_FALSE(decoder->Failed()); -+ frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_EQ(frame->GetStatus(), ImageFrame::kFrameComplete); -+ EXPECT_FALSE(decoder->Failed()); -+ -+ constexpr int kImageArea = 1216 * 832; // = 1011712 -+ constexpr int kFileSize = 70944; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 56 -+ histogram_tester.ExpectUniqueSample( -+ "Blink.DecodedImage.AvifDensity.Count.02MP", kSample, 1); -+ expected_counts["Blink.DecodedImage.AvifDensity.Count.02MP"] = 1; -+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( -+ "Blink.DecodedImage.AvifDensity.Count."), -+ testing::ContainerEq(expected_counts)); -+} -+ -+TEST(StaticAVIFTests, IncrementalDecoding) { -+ base::HistogramTester histogram_tester; -+ scoped_refptr stream_buffer = WTF::SharedBuffer::Create(); -+ scoped_refptr segment_reader = -+ SegmentReader::CreateFromSharedBuffer(stream_buffer); -+ std::unique_ptr decoder = ImageDecoder::CreateByMimeType( -+ "image/avif", segment_reader, /*data_complete=*/false, -+ ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth, -+ ColorBehavior::kTag, cc::AuxImage::kDefault, -+ Platform::GetMaxDecodedImageBytes(), SkISize::MakeEmpty(), -+ ImageDecoder::AnimationOption::kUnspecified); -+ -+ Vector data = -+ ReadFile("/images/resources/avif/tiger_420_8b_grid1x13.avif"); -+ -+ constexpr int kImageArea = 1216 * 832; // = 1011712 -+ constexpr int kFileSize = 72257; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 57 -+ -+ struct Step { -+ size_t size; // In bytes. -+ ImageFrame::Status status; -+ int num_decoded_rows; // In pixels. -+ }; -+ // There are 13 tiles. Tiles are as wide as the image and 64 pixels tall. -+ // |num_decoded_rows| may be odd due to an output pixel row missing the -+ // following upsampled decoded chroma row (belonging to the next tile). -+ const Step steps[] = { -+ {2000, ImageFrame::kFrameEmpty, 0}, -+ // Decoding half of the bytes gives 6 tile rows. -+ {data.size() / 2, ImageFrame::kFramePartial, 6 * 64 - 1}, -+ // Decoding all bytes but one gives 12 tile rows. -+ {data.size() - 1, ImageFrame::kFramePartial, 12 * 64 - 1}, -+ // Decoding all bytes gives all 13 tile rows. -+ {data.size(), ImageFrame::kFrameComplete, 13 * 64}}; -+ size_t previous_size = 0; -+ auto data_span = base::span(data); -+ for (const Step& step : steps) { -+ stream_buffer->Append( -+ data_span.subspan(previous_size, step.size - previous_size)); -+ decoder->SetData(stream_buffer, step.status == ImageFrame::kFrameComplete); -+ -+ EXPECT_EQ(decoder->FrameCount(), 1u); -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ ASSERT_FALSE(decoder->Failed()); -+ EXPECT_EQ(frame->GetStatus(), step.status); -+ -+ const SkBitmap& bitmap = frame->Bitmap(); -+ for (int y = 0; y < bitmap.height(); ++y) { -+ const uint32_t* row = bitmap.getAddr32(0, y); -+ const bool is_row_decoded = y < step.num_decoded_rows; -+ for (int x = 0; x < bitmap.width(); ++x) { -+ // The input image is opaque. Pixels outside the decoded area are fully -+ // transparent black pixels, with each channel value being 0. -+ const bool is_pixel_decoded = row[x] != 0x00000000u; -+ ASSERT_EQ(is_pixel_decoded, is_row_decoded); -+ } -+ } -+ previous_size = step.size; -+ -+ base::HistogramTester::CountsMap expected_counts; -+ if (step.status == ImageFrame::kFrameComplete) { -+ histogram_tester.ExpectUniqueSample( -+ "Blink.DecodedImage.AvifDensity.Count.02MP", kSample, 1); -+ expected_counts["Blink.DecodedImage.AvifDensity.Count.02MP"] = 1; -+ } -+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( -+ "Blink.DecodedImage.AvifDensity.Count."), -+ testing::ContainerEq(expected_counts)); -+ } -+} -+ -+// Reproduces crbug.com/1402841. Decodes a large AVIF image 104 times in -+// parallel from base::ThreadPool. Should not cause temporary deadlock of -+// base::ThreadPool. -+TEST(StaticAVIFTests, ParallelDecoding) { -+ // The base::test::TaskEnvironment constructor creates a base::ThreadPool -+ // instance with 4 foreground threads. The number 4 comes from the -+ // test::TaskEnvironment::kNumForegroundThreadPoolThreads constant. -+ base::test::TaskEnvironment task_environment; -+ -+ // This test image is fast to decode (all neutral gray pixels) and its -+ // allocation size is large enough to cause -+ // media::PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels() to pick -+ // n_tasks > 1 if AVIFImageDecoder did not pass disable_threading=true to it. -+ Vector data = ReadFile("/images/resources/avif/gray1024x704.avif"); -+ -+ // Task timeout in tests is 30 seconds (see https://crrev.com/c/1949028). -+ // Four blocking tasks cause a temporary deadlock (1.2 seconds) of -+ // base::ThreadPool, so we need at least 30 / 1.2 * 4 = 100 decodes for the -+ // test to time out without the bug fix. We add a margin of 4 decodes, i.e., -+ // (30 / 1.2 + 1) * 4 = 104. -+ const size_t n_decodes = 104; -+ base::WaitableEvent event; -+ base::RepeatingClosure barrier = base::BarrierClosure( -+ n_decodes, -+ base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event))); -+ -+ for (size_t i = 0; i < n_decodes; ++i) { -+ base::ThreadPool::PostTask( -+ FROM_HERE, -+ base::BindOnce(DecodeTask, base::Unretained(&data), &barrier)); -+ } -+ -+ event.Wait(); -+} -+ -+TEST(StaticAVIFTests, AlphaHasNoIspeProperty) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/green-no-alpha-ispe.avif"), -+ true); -+ EXPECT_FALSE(decoder->IsSizeAvailable()); -+ EXPECT_TRUE(decoder->Failed()); -+} -+ -+TEST(StaticAVIFTests, UnsupportedTransferFunctionInColrProperty) { -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData(ReadFileToSharedBuffer( -+ "/images/resources/avif/red-unsupported-transfer.avif"), -+ true); -+ EXPECT_FALSE(decoder->IsSizeAvailable()); -+ EXPECT_TRUE(decoder->Failed()); -+} -+ -+TEST(StaticAVIFTests, ClapPropertyZeroOrigin) { -+ constexpr int kClapWidth = 200; -+ constexpr int kClapHeight = 50; -+ std::unique_ptr decoder1 = CreateAVIFDecoder(); -+ decoder1->SetData( -+ ReadFileToSharedBuffer("/images/resources/avif/red-and-purple-crop.avif"), -+ true); -+ ASSERT_TRUE(decoder1->IsSizeAvailable()); -+ gfx::Size size1 = decoder1->Size(); -+ ASSERT_EQ(size1.width(), kClapWidth); -+ ASSERT_EQ(size1.height(), kClapHeight); -+ ImageFrame* frame1 = decoder1->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame1); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame1->GetStatus()); -+ EXPECT_FALSE(decoder1->Failed()); -+ const SkBitmap& bitmap1 = frame1->Bitmap(); -+ -+ // The second image is the uncropped version of the first image. -+ std::unique_ptr decoder2 = CreateAVIFDecoder(); -+ decoder2->SetData(ReadFileToSharedBuffer( -+ "/images/resources/avif/red-and-purple-and-blue.avif"), -+ true); -+ ASSERT_TRUE(decoder2->IsSizeAvailable()); -+ gfx::Size size2 = decoder2->Size(); -+ ASSERT_EQ(size2.width(), 300); -+ ASSERT_EQ(size2.height(), 100); -+ ImageFrame* frame2 = decoder2->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame2); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame2->GetStatus()); -+ EXPECT_FALSE(decoder2->Failed()); -+ const SkBitmap& bitmap2 = frame2->Bitmap(); -+ -+ // Compare pixel data. -+ for (int row = 0; row < kClapHeight; ++row) { -+ for (int col = 0; col < kClapWidth; ++col) { -+ EXPECT_EQ(bitmap1.getColor(/*x=*/col, /*y=*/row), -+ bitmap2.getColor(/*x=*/col, /*y=*/row)); -+ } -+ } -+} -+ -+// Verifies that an invalid 'clap' (clean aperture) image property is handled by -+// ignoring the 'clap' property and showing the full image. -+TEST(StaticAVIFTests, InvalidClapPropertyHandling) { -+ // The first image has a valid 'clap' property. The full image has size -+ // 320x280. The clean aperture has size 180x100, located at (40, 80) of the -+ // full image. -+ // -+ // Since the origin of the clean aperture is not located at (0, 0), we treat -+ // the 'clap' property as invalid. So the full image is shown. -+ std::unique_ptr decoder1 = CreateAVIFDecoder(); -+ decoder1->SetData(ReadFileToSharedBuffer( -+ "/images/resources/avif/blue-and-magenta-crop.avif"), -+ true); -+ ASSERT_TRUE(decoder1->IsSizeAvailable()); -+ gfx::Size size1 = decoder1->Size(); -+ ASSERT_EQ(size1.width(), 320); -+ ASSERT_EQ(size1.height(), 280); -+ ImageFrame* frame1 = decoder1->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame1); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame1->GetStatus()); -+ EXPECT_FALSE(decoder1->Failed()); -+ const SkBitmap& bitmap1 = frame1->Bitmap(); -+ -+ // The second image is the same as the first image except that the 'clap' -+ // property is invalid. In this case the full image is shown. -+ std::unique_ptr decoder2 = CreateAVIFDecoder(); -+ decoder2->SetData( -+ ReadFileToSharedBuffer( -+ "/images/resources/avif/blue-and-magenta-crop-invalid.avif"), -+ true); -+ ASSERT_TRUE(decoder2->IsSizeAvailable()); -+ gfx::Size size2 = decoder2->Size(); -+ ASSERT_EQ(size2.width(), 320); -+ ASSERT_EQ(size2.height(), 280); -+ ImageFrame* frame2 = decoder2->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame2); -+ EXPECT_EQ(ImageFrame::kFrameComplete, frame2->GetStatus()); -+ EXPECT_FALSE(decoder2->Failed()); -+ const SkBitmap& bitmap2 = frame2->Bitmap(); -+ -+ // Compare pixel data. -+ for (int row = 0; row < size1.height(); ++row) { -+ for (int col = 0; col < size1.width(); ++col) { -+ EXPECT_EQ(bitmap1.getColor(/*x=*/col, /*y=*/row), -+ bitmap2.getColor(/*x=*/col, /*y=*/row)); -+ } -+ } -+} -+ -+TEST(StaticAVIFTests, BppHistogramSmall) { -+ constexpr int kImageArea = 768 * 512; // = 393216 -+ constexpr int kFileSize = 25724; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 52 -+ TestAvifBppHistogram("/images/resources/avif/kodim03.avif", -+ "Blink.DecodedImage.AvifDensity.Count.0.4MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramSmall3x3) { -+ // The centi bpp = 318 * 100 * 8 / (3 * 3) ~= 28267, which is greater than the -+ // histogram's max value (1000), so this sample goes into the overflow bucket. -+ constexpr int kSample = 1000; -+ TestAvifBppHistogram("/images/resources/avif/red-full-range-420-8bpc.avif", -+ "Blink.DecodedImage.AvifDensity.Count.0.1MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramSmall900000) { -+ constexpr int kImageArea = 1200 * 750; // = 900000 -+ constexpr int kFileSize = 8144; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 7 -+ TestAvifBppHistogram("/images/resources/avif/peach_900000.avif", -+ "Blink.DecodedImage.AvifDensity.Count.0.9MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramBig) { -+ constexpr int kImageArea = 4032 * 3024; // = 12192768 -+ constexpr int kFileSize = 88692; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 6 -+ TestAvifBppHistogram("/images/resources/avif/bee.avif", -+ "Blink.DecodedImage.AvifDensity.Count.13MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramBig13000000) { -+ constexpr int kImageArea = 4000 * 3250; // = 13000000 -+ constexpr int kFileSize = 16725; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 -+ TestAvifBppHistogram("/images/resources/avif/peach_13000000.avif", -+ "Blink.DecodedImage.AvifDensity.Count.13MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramHuge) { -+ constexpr int kImageArea = 4624 * 3472; // = 16054528 -+ constexpr int kFileSize = 20095; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 -+ TestAvifBppHistogram("/images/resources/avif/peach.avif", -+ "Blink.DecodedImage.AvifDensity.Count.14+MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramHuge13000002) { -+ constexpr int kImageArea = 3961 * 3282; // = 13000002 -+ constexpr int kFileSize = 16379; -+ constexpr int kSample = -+ (kFileSize * 100 * 8 + kImageArea / 2) / kImageArea; // = 1 -+ TestAvifBppHistogram("/images/resources/avif/peach_13000002.avif", -+ "Blink.DecodedImage.AvifDensity.Count.14+MP", kSample); -+} -+ -+TEST(StaticAVIFTests, BppHistogramInvalid) { -+ base::HistogramTester histogram_tester; -+ std::unique_ptr decoder = CreateAVIFDecoder(); -+ decoder->SetData( -+ ReadFileToSharedBuffer( -+ "/images/resources/avif/" -+ "red-at-12-oclock-with-color-profile-with-wrong-frame-header.avif"), -+ true); -+ ASSERT_TRUE(decoder->IsSizeAvailable()); -+ EXPECT_FALSE(decoder->Failed()); -+ EXPECT_EQ(decoder->FrameCount(), 1u); -+ ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0); -+ ASSERT_TRUE(frame); -+ EXPECT_NE(ImageFrame::kFrameComplete, frame->GetStatus()); -+ EXPECT_TRUE(decoder->Failed()); -+ const base::HistogramTester::CountsMap empty_counts; -+ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix( -+ "Blink.DecodedImage.AvifDensity.Count."), -+ testing::ContainerEq(empty_counts)); -+} -+ -+TEST(StaticAVIFTests, BppHistogram10bit) { -+ TestAvifBppHistogram("/images/resources/avif/red-full-range-420-10bpc.avif"); -+} -+ -+TEST(StaticAVIFTests, BppHistogramMonochrome) { -+ TestAvifBppHistogram("/images/resources/avif/silver-400-matrix-6.avif"); -+} -+ -+TEST(StaticAVIFTests, BppHistogramAlpha) { -+ TestAvifBppHistogram("/images/resources/avif/red-with-alpha-8bpc.avif"); -+} -+ -+TEST(StaticAVIFTests, BppHistogramAnimated) { -+ TestAvifBppHistogram("/images/resources/avif/star-animated-8bpc.avif"); -+} -+ -+using StaticAVIFColorTests = ::testing::TestWithParam; -+ -+INSTANTIATE_TEST_SUITE_P(Parameterized, -+ StaticAVIFColorTests, -+ ::testing::ValuesIn(kTestParams)); -+ -+TEST_P(StaticAVIFColorTests, InspectImage) { -+ InspectImage(GetParam(), ImageDecoder::kDefaultBitDepth); -+} -+ -+TEST_P(StaticAVIFColorTests, InspectImageHalfFloat) { -+ InspectImage(GetParam(), ImageDecoder::kHighBitDepthToHalfFloat); -+} -+ -+} // namespace -+ -+} // namespace blink -diff --git a/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py b/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py -new file mode 100644 -index 0000000000000..82f1c0c3df73a ---- /dev/null -+++ b/third_party/blink/renderer/platform/image-decoders/avif/gen_crabbyavif_wrapper.py -@@ -0,0 +1,164 @@ -+# Copyright 2024 The Chromium Authors -+# Use of this source code is governed by a BSD-style license that can be -+# found in the LICENSE file. -+"""Script to generate crabbyavif wrapper using the libavif wrapper as the base. -+ -+When the libavif wrapper files (any of avif_image_decoder*) are changed, this -+script must be run to update the crabbyavif wrappers -+(crabbyavif_image_decoder*). -+""" -+ -+import os -+import re -+ -+ -+def _read_file(filename): -+ with open(filename) as file: -+ return file.read() -+ -+ -+def _write_file(filename, contents): -+ with open(filename, "w") as file: -+ file.write(contents) -+ -+ -+def _apply_replacements(contents, replacements): -+ for find, replace in replacements: -+ contents = re.sub(find, replace, contents) -+ return contents -+ -+ -+_COMMON_REPLACEMENTS = ( -+ (r"Copyright 2020", "Copyright 2024"), -+ (r"third_party/libavif/src", "third_party/crabbyavif/src"), -+ (r"avif_image_decoder.h", "crabbyavif_image_decoder.h"), -+ (r"AVIFImageDecoder", "CrabbyAVIFImageDecoder"), -+ (r"AVIF_TRUE", "CRABBY_AVIF_TRUE"), -+ (r"AVIF_FALSE", "CRABBY_AVIF_FALSE"), -+ (r"AVIF_REPETITION_COUNT_", "CRABBY_AVIF_REPETITION_COUNT_"), -+) -+ -+_NOTICE = """// WARNING: Auto-generated by gen_crabbyavif_wrapper.py. -+// Do not modify manually. -+""" -+ -+ -+def _generate_crabbyavif_file(source_file, replacements): -+ contents = _read_file(source_file) -+ contents = _apply_replacements(contents, _COMMON_REPLACEMENTS) -+ contents = _apply_replacements(contents, replacements) -+ contents = contents.split("\n") -+ # Copyright notice is 3 lines, insert the notice as the 4th line. -+ contents.insert(3, _NOTICE) -+ contents = "\n".join(contents) -+ crabby_source_file = "crabby%(source_file)s" % locals() -+ _write_file(crabby_source_file, contents) -+ os.system("clang-format -style chromium -i %(crabby_source_file)s" % -+ locals()) -+ -+ -+_HEADER_REPLACEMENTS = ( -+ (r"AVIF_AVIF_IMAGE_DECODER_H_", "AVIF_CRABBYAVIF_IMAGE_DECODER_H_"), -+ (r"AVIF_PIXEL_FORMAT_NONE", "crabbyavif::AVIF_PIXEL_FORMAT_NONE"), -+ # Functions -+ (r"avifDecoderDestroy", "crabbyavif::crabby_avifDecoderDestroy"), -+ (r"avifImageDestroy", "crabbyavif::crabby_avifImageDestroy"), -+ # Types -+ (r"avifIO", "crabbyavif::avifIO"), -+ (r"avifPixelFormat", "crabbyavif::avifPixelFormat"), -+ (r"avifROData", "crabbyavif::avifROData"), -+ (r"avifResult", "crabbyavif::avifResult"), -+ (r"\bavifDecoder\b", "crabbyavif::avifDecoder"), -+ (r"\bavifImage\b", "crabbyavif::avifImage"), -+) -+ -+_CC_REPLACEMENTS = ( -+ # Functions (to be namespaced and prefixed with "crabby_") -+ ( -+ r"\bavifCropRectConvertCleanApertureBox\b", -+ "crabbyavif::crabby_avifCropRectConvertCleanApertureBox", -+ ), -+ (r"\bavifDecoderCreate\b", "crabbyavif::crabby_avifDecoderCreate"), -+ ( -+ r"\bavifDecoderDecodedRowCount\b", -+ "crabbyavif::crabby_avifDecoderDecodedRowCount", -+ ), -+ (r"\bavifDecoderDestroy\b", "crabbyavif::crabby_avifDecoderDestroy"), -+ (r"\bavifDecoderNthImage\b", "crabbyavif::crabby_avifDecoderNthImage"), -+ ( -+ r"\bavifDecoderNthImageMaxExtent\b", -+ "crabbyavif::crabby_avifDecoderNthImageMaxExtent", -+ ), -+ ( -+ r"\bavifDecoderNthImageMaxExtent\b", -+ "crabbyavif::crabby_avifDecoderNthImageMaxExtent", -+ ), -+ ( -+ r"\bavifDecoderNthImageTiming\b", -+ "crabbyavif::crabby_avifDecoderNthImageTiming", -+ ), -+ (r"\bavifDecoderParse\b", "crabbyavif::crabby_avifDecoderParse"), -+ (r"\bavifDecoderSetIO\b", "crabbyavif::crabby_avifDecoderSetIO"), -+ (r"\bavifDecoderSetSource\b", "crabbyavif::crabby_avifDecoderSetSource"), -+ ( -+ r"\bavifGetPixelFormatInfo\b", -+ "crabbyavif::crabby_avifGetPixelFormatInfo", -+ ), -+ (r"\bavifImageCreateEmpty\b", "crabbyavif::crabby_avifImageCreateEmpty"), -+ (r"\bavifImageDestroy\b", "crabbyavif::crabby_avifImageDestroy"), -+ (r"\bavifImageSetViewRect\b", "crabbyavif::crabby_avifImageSetViewRect"), -+ (r"\bavifImageYUVToRGB\b", "crabbyavif::crabby_avifImageYUVToRGB"), -+ ( -+ r"\bavifPeekCompatibleFileType\b", -+ "crabbyavif::crabby_avifPeekCompatibleFileType", -+ ), -+ ( -+ r"\bavifRGBImageSetDefaults\b", -+ "crabbyavif::crabby_avifRGBImageSetDefaults", -+ ), -+ (r"\bavifResultToString\b", "crabbyavif::crabby_avifResultToString"), -+ # Symbols (to be namespaced). -+ (r"avifBool\b", "crabbyavif::avifBool"), -+ (r"avifColorPrimaries\b", "crabbyavif::avifColorPrimaries"), -+ (r"avifCropRect\b", "crabbyavif::avifCropRect"), -+ (r"avifDecoder\b", "crabbyavif::avifDecoder"), -+ (r"avifDiagnostics\b", "crabbyavif::avifDiagnostics"), -+ (r"avifExtent\b", "crabbyavif::avifExtent"), -+ (r"avifGainMap\b", "crabbyavif::avifGainMap"), -+ (r"avifGainMapMetadata\b", "crabbyavif::avifGainMapMetadata"), -+ (r"avifIO\b", "crabbyavif::avifIO"), -+ (r"avifImage\b", "crabbyavif::avifImage"), -+ (r"avifImageTiming\b", "crabbyavif::avifImageTiming"), -+ (r"avifMatrixCoefficients\b", "crabbyavif::avifMatrixCoefficients"), -+ (r"avifPixelFormatInfo\b", "crabbyavif::avifPixelFormatInfo"), -+ (r"avifRGBImage\b", "crabbyavif::avifRGBImage"), -+ (r"avifROData\b", "crabbyavif::avifROData"), -+ (r"avifRange\b", "crabbyavif::avifRange"), -+ (r"avifResult\b", "crabbyavif::avifResult"), -+ ( -+ r"avifTransferCharacteristics\b", -+ "crabbyavif::avifTransferCharacteristics", -+ ), -+ (r"\bAVIF_", "crabbyavif::AVIF_"), -+ (r"\bCRABBY_AVIF_", "crabbyavif::CRABBY_AVIF_"), -+) -+ -+_TEST_REPLACEMENTS = ( -+ (r"\bAVIFValidImagesTest\b", "CrabbyAVIFValidImagesTest"), -+ (r"\bAnimatedAVIFTests\b", "CrabbyAnimatedAVIFTests"), -+ (r"\bStaticAVIFColorTests\b", "CrabbyStaticAVIFColorTests"), -+ (r"\bStaticAVIFTests\b", "CrabbyStaticAVIFTests"), -+) -+ -+_FUZZER_REPLACEMENTS = ((r"kAvifDecoder", "kCrabbyAvifDecoder"), ) -+ -+def main(): -+ _generate_crabbyavif_file("avif_image_decoder.h", _HEADER_REPLACEMENTS) -+ _generate_crabbyavif_file("avif_image_decoder.cc", _CC_REPLACEMENTS) -+ _generate_crabbyavif_file("avif_image_decoder_test.cc", _TEST_REPLACEMENTS) -+ _generate_crabbyavif_file("avif_image_decoder_fuzzer.cc", -+ _FUZZER_REPLACEMENTS) -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -index 0cc5dfa276964..0523ccbd18509 100644 ---- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -@@ -52,6 +52,7 @@ - #include "ui/gfx/geometry/size_conversions.h" - - #if BUILDFLAG(ENABLE_AV1_DECODER) -+#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" - #include "third_party/blink/renderer/platform/image-decoders/avif/crabbyavif_image_decoder.h" - #endif - -@@ -197,7 +198,9 @@ String SniffMimeTypeInternal(scoped_refptr reader) { - return "image/bmp"; - } - #if BUILDFLAG(ENABLE_AV1_DECODER) -- if (CrabbyAVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { -+ if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif) -+ ? CrabbyAVIFImageDecoder::MatchesAVIFSignature(fast_reader) -+ : AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { - return "image/avif"; - } - #endif -@@ -307,9 +310,15 @@ std::unique_ptr ImageDecoder::CreateByMimeType( - max_decoded_bytes); - #if BUILDFLAG(ENABLE_AV1_DECODER) - } else if (mime_type == "image/avif") { -- decoder = std::make_unique( -- alpha_option, high_bit_depth_decoding_option, color_behavior, aux_image, -- max_decoded_bytes, animation_option); -+ if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif)) { -+ decoder = std::make_unique( -+ alpha_option, high_bit_depth_decoding_option, color_behavior, -+ aux_image, max_decoded_bytes, animation_option); -+ } else { -+ decoder = std::make_unique( -+ alpha_option, high_bit_depth_decoding_option, color_behavior, -+ aux_image, max_decoded_bytes, animation_option); -+ } - #endif - } - -diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc -index 93805eafce9fd..d07cbcd599f15 100644 ---- a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc -+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.cc -@@ -5,6 +5,7 @@ - #include "third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h" - - #include "third_party/blink/renderer/platform/graphics/color_behavior.h" -+#include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" - #include "third_party/blink/renderer/platform/image-decoders/avif/crabbyavif_image_decoder.h" - #include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h" - #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h" -@@ -72,6 +73,13 @@ std::unique_ptr CreateImageDecoder(DecoderType decoder_type, - /*max_decoded_bytes=*/fdp.ConsumeIntegral(), - /*offset=*/fdp.ConsumeIntegral()); - } -+ case DecoderType::kAvifDecoder: { -+ return std::make_unique( -+ GetAlphaOption(fdp), GetHbdOption(fdp), GetColorBehavior(fdp), -+ GetAuxImageType(fdp), -+ /*max_decoded_bytes=*/fdp.ConsumeIntegral(), -+ GetAnimationOption(fdp)); -+ } - case DecoderType::kCrabbyAvifDecoder: { - return std::make_unique( - GetAlphaOption(fdp), GetHbdOption(fdp), GetColorBehavior(fdp), -diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h -index f6ad7fe6b4c52..d1227387a9145 100644 ---- a/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h -+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder_fuzzer_utils.h -@@ -15,6 +15,7 @@ enum class DecoderType { - kBmpDecoder, - kJpegDecoder, - kPngDecoder, -+ kAvifDecoder, - kCrabbyAvifDecoder, - }; - diff --git a/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch b/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch deleted file mode 100644 index f0991eb71..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0015-Revert-Remove-third_party-libavif.patch +++ /dev/null @@ -1,333 +0,0 @@ -From d71a0fc5b00c20492fbfd15a561548c2e40ae2d4 Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Thu, 17 Jul 2025 13:11:35 -0300 -Subject: [PATCH] Revert "Remove third_party/libavif" - -This reverts commit 16708cb7341629a2793a6a06a370e2b846313e56. - -In order to disable crabbyavif to fix build errors, re-enable libavif so -it can be used in replacement. - -Upstream-Status: Inappropriate [upstream ticket https://crbug.com/357017325] -Signed-off-by: Ariel D'Alessandro ---- - DEPS | 7 + - third_party/libavif/BUILD.gn | 172 +++++++++++++++++++ - third_party/libavif/DIR_METADATA | 6 + - third_party/libavif/OWNERS | 2 + - third_party/libavif/README.chromium | 13 ++ - third_party/libavif/avif_apps_shared_stubs.c | 50 ++++++ - third_party/libavif/src | 1 + - 7 files changed, 251 insertions(+) - create mode 100644 third_party/libavif/BUILD.gn - create mode 100644 third_party/libavif/DIR_METADATA - create mode 100644 third_party/libavif/OWNERS - create mode 100644 third_party/libavif/README.chromium - create mode 100644 third_party/libavif/avif_apps_shared_stubs.c - create mode 160000 third_party/libavif/src - -diff --git a/DEPS b/DEPS -index f527d6b0d4b78..3b31fee5d5d3a 100644 ---- a/DEPS -+++ b/DEPS -@@ -445,6 +445,10 @@ vars = { - # and whatever else without interference from each other. - 'wuffs_revision': 'e3f919ccfe3ef542cfc983a82146070258fb57f8', - # Three lines of non-changing comments so that -+ # the commit queue can handle CLs rolling libavif -+ # and whatever else without interference from each other. -+ 'libavif_revision': 'e7b34a1f5e9f7024d08311c7bae156061b889882', -+ # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling crabbyavif - # and whatever else without interference from each other. - 'crabbyavif_revision': '02d0fad2c512380b7270d6e704c86521075d7d54', -@@ -2268,6 +2272,9 @@ deps = { - 'src/third_party/libaom/source/libaom': - Var('aomedia_git') + '/aom.git' + '@' + '9680f2b1781fb33b9eeb52409b75c679c8a954be', - -+ 'src/third_party/libavif/src': -+ Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'), -+ - 'src/third_party/crabbyavif/src': - Var('chromium_git') + '/external/github.com/webmproject/CrabbyAvif.git' + '@' + Var('crabbyavif_revision'), - -diff --git a/third_party/libavif/BUILD.gn b/third_party/libavif/BUILD.gn -new file mode 100644 -index 0000000000000..c2cc9e2d9fd87 ---- /dev/null -+++ b/third_party/libavif/BUILD.gn -@@ -0,0 +1,172 @@ -+# Copyright 2020 The Chromium Authors -+# Use of this source code is governed by a BSD-style license that can be -+# found in the LICENSE file. -+ -+import("//media/media_options.gni") -+import("//testing/libfuzzer/fuzzer_test.gni") -+ -+# Public configuration exported to users of the libavif target. -+config("avif_public_config") { -+ if (is_component_build) { -+ defines = [ "AVIF_DLL" ] -+ } -+} -+ -+# Private configuration used in building libavif. -+config("avif_config") { -+ include_dirs = [ "src/include/" ] -+ defines = [ "AVIF_LIBYUV_ENABLED" ] -+ if (is_component_build) { -+ defines += [ "AVIF_BUILDING_SHARED_LIBS" ] -+ } -+ -+ if (enable_dav1d_decoder) { -+ include_dirs += [ "../dav1d/libdav1d/include/" ] -+ defines += [ "AVIF_CODEC_DAV1D" ] -+ } -+} -+ -+libavif_decoder_sources = [ -+ "src/include/avif/internal.h", -+ "src/src/alpha.c", -+ "src/src/avif.c", -+ "src/src/colr.c", -+ "src/src/colrconvert.c", -+ "src/src/diag.c", -+ "src/src/exif.c", -+ "src/src/gainmap.c", -+ "src/src/io.c", -+ "src/src/mem.c", -+ "src/src/obu.c", -+ "src/src/properties.c", -+ "src/src/rawdata.c", -+ "src/src/read.c", -+ "src/src/reformat.c", -+ "src/src/reformat_libsharpyuv.c", -+ "src/src/reformat_libyuv.c", -+ "src/src/scale.c", -+ "src/src/stream.c", -+ "src/src/utils.c", -+] -+ -+component("libavif") { -+ public = [ "src/include/avif/avif.h" ] -+ public_configs = [ ":avif_public_config" ] -+ -+ sources = libavif_decoder_sources -+ -+ configs += [ ":avif_config" ] -+ -+ deps = [ "//third_party/libyuv" ] -+ -+ if (enable_dav1d_decoder) { -+ sources += [ "src/src/codec_dav1d.c" ] -+ deps += [ "//third_party/dav1d" ] -+ } -+} -+ -+# Note only the decoder fuzzers are enabled as only the decoder is being used -+# in Chrome. avif_fuzztest_read_image is not enabled due to libpng not having -+# PNG_READ_iTXt_SUPPORTED enabled. -+config("avif_fuzztest_config") { -+ include_dirs = [ -+ "src/include", -+ "src/apps/shared", -+ ] -+} -+ -+# This is used to satisfy dependencies in avif_fuzztest_helpers. The encoder -+# functions are not used. -+component("libavif_enc") { -+ public = [ "src/include/avif/avif.h" ] -+ public_configs = [ ":avif_public_config" ] -+ -+ sources = libavif_decoder_sources + [ -+ "src/src/write.c", -+ ] -+ testonly = true -+ -+ configs += [ ":avif_config" ] -+ -+ deps = [ -+ "//third_party/libwebp:libwebp_sharpyuv", -+ "//third_party/libyuv", -+ ] -+ defines = [ "AVIF_LIBSHARPYUV_ENABLED" ] -+ -+ if (enable_dav1d_decoder) { -+ sources += [ "src/src/codec_dav1d.c" ] -+ deps += [ "//third_party/dav1d" ] -+ } -+} -+ -+source_set("avif_apps_shared") { -+ sources = [ -+ "avif_apps_shared_stubs.c", -+ "src/apps/shared/avifexif.c", -+ "src/apps/shared/avifexif.h", -+ "src/apps/shared/avifjpeg.h", -+ "src/apps/shared/avifpng.h", -+ "src/apps/shared/avifutil.c", -+ "src/apps/shared/avifutil.h", -+ "src/apps/shared/y4m.c", -+ "src/apps/shared/y4m.h", -+ ] -+ testonly = true -+ configs += [ ":avif_fuzztest_config" ] -+ deps = [ ":libavif_enc" ] -+} -+ -+source_set("avif_fuzztest_helpers") { -+ sources = [ -+ "src/tests/gtest/avif_fuzztest_helpers.cc", -+ "src/tests/gtest/avifincrtest_helpers.cc", -+ "src/tests/gtest/aviftest_helpers.cc", -+ ] -+ testonly = true -+ configs += [ ":avif_fuzztest_config" ] -+ deps = [ -+ ":avif_apps_shared", -+ ":libavif_enc", -+ "//testing/gtest", -+ "//third_party/fuzztest:fuzztest", -+ ] -+} -+ -+# TODO: b/308013905 - These tests require seeds from -+# third_party/libavif/src/tests/data which -+# aren't available in the fuzzing environment. These targets can be enabled if -+# they are made hermetic. -+# -+# test("avif_fuzztest_dec") { -+# sources = [ "src/tests/gtest/avif_fuzztest_dec.cc" ] -+# fuzztests = [ "DecodeAvifTest.Decode" ] -+# configs += [ ":avif_fuzztest_config" ] -+# deps = [ -+# ":avif_fuzztest_helpers", -+# ":libavif_enc", -+# "//third_party/fuzztest:fuzztest_gtest_main", -+# ] -+# } -+# -+# test("avif_fuzztest_dec_incr") { -+# sources = [ "src/tests/gtest/avif_fuzztest_dec_incr.cc" ] -+# fuzztests = [ "DecodeAvifFuzzTest.DecodeIncr" ] -+# configs += [ ":avif_fuzztest_config" ] -+# deps = [ -+# ":avif_fuzztest_helpers", -+# ":libavif_enc", -+# "//third_party/fuzztest:fuzztest_gtest_main", -+# ] -+# } -+ -+test("avif_fuzztest_yuvrgb") { -+ sources = [ "src/tests/gtest/avif_fuzztest_yuvrgb.cc" ] -+ fuzztests = [ "YuvRgbFuzzTest.Convert" ] -+ configs += [ ":avif_fuzztest_config" ] -+ deps = [ -+ ":avif_fuzztest_helpers", -+ ":libavif_enc", -+ "//third_party/fuzztest:fuzztest_gtest_main", -+ ] -+} -diff --git a/third_party/libavif/DIR_METADATA b/third_party/libavif/DIR_METADATA -new file mode 100644 -index 0000000000000..beadfa62dc3ad ---- /dev/null -+++ b/third_party/libavif/DIR_METADATA -@@ -0,0 +1,6 @@ -+monorail: { -+ component: "Internals>Images>Codecs" -+} -+buganizer_public: { -+ component_id: 1456316 -+} -diff --git a/third_party/libavif/OWNERS b/third_party/libavif/OWNERS -new file mode 100644 -index 0000000000000..6946c0b7788b5 ---- /dev/null -+++ b/third_party/libavif/OWNERS -@@ -0,0 +1,2 @@ -+file://media/OWNERS -+wtc@google.com -diff --git a/third_party/libavif/README.chromium b/third_party/libavif/README.chromium -new file mode 100644 -index 0000000000000..81904b69c35c5 ---- /dev/null -+++ b/third_party/libavif/README.chromium -@@ -0,0 +1,13 @@ -+Name: libavif - Library for encoding and decoding .avif files -+Short Name: libavif -+URL: https://github.com/AOMediaCodec/libavif -+Version: N/A -+Revision: DEPS -+License: BSD-2-Clause, MIT -+License File: src/LICENSE -+Security Critical: yes -+Shipped: yes -+ -+Description: -+This contains the source to the AV1 image format demuxer; used for demuxing and -+decoding .avif files in Chromium. -diff --git a/third_party/libavif/avif_apps_shared_stubs.c b/third_party/libavif/avif_apps_shared_stubs.c -new file mode 100644 -index 0000000000000..18980cbc5b0b5 ---- /dev/null -+++ b/third_party/libavif/avif_apps_shared_stubs.c -@@ -0,0 +1,50 @@ -+// Copyright 2024 The Chromium Authors -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+// This file provides functions necessary to link the decoder fuzz tests. The -+// functions are not used by the tests being built, but their dependencies in -+// apps/shared and avif_*_helpers.cc unconditionally reference them. -+ -+#include -+ -+#include "src/apps/shared/avifjpeg.h" -+#include "src/apps/shared/avifpng.h" -+ -+avifBool avifJPEGRead(const char * inputFilename, -+ avifImage * avif, -+ avifPixelFormat requestedFormat, -+ uint32_t requestedDepth, -+ avifChromaDownsampling chromaDownsampling, -+ avifBool ignoreColorProfile, -+ avifBool ignoreExif, -+ avifBool ignoreXMP, -+ avifBool ignoreGainMap, -+ uint32_t imageSizeLimit) { -+ fprintf(stderr, "The tests were built without JPEG support!\n"); -+ return AVIF_FALSE; -+} -+ -+avifBool avifPNGRead(const char * inputFilename, -+ avifImage * avif, -+ avifPixelFormat requestedFormat, -+ uint32_t requestedDepth, -+ avifChromaDownsampling chromaDownsampling, -+ avifBool ignoreColorProfile, -+ avifBool ignoreExif, -+ avifBool ignoreXMP, -+ avifBool allowChangingCicp, -+ uint32_t imageSizeLimit, -+ uint32_t * outPNGDepth) { -+ fprintf(stderr, "The tests were built without PNG support!\n"); -+ return AVIF_FALSE; -+} -+ -+avifBool avifPNGWrite(const char * outputFilename, -+ const avifImage * avif, -+ uint32_t requestedDepth, -+ avifChromaUpsampling chromaUpsampling, -+ int compressionLevel) { -+ fprintf(stderr, "The tests were built without PNG support!\n"); -+ return AVIF_FALSE; -+} -diff --git a/third_party/libavif/src b/third_party/libavif/src -new file mode 160000 -index 0000000000000..e7b34a1f5e9f7 ---- /dev/null -+++ b/third_party/libavif/src -@@ -0,0 +1 @@ -+Subproject commit e7b34a1f5e9f7024d08311c7bae156061b889882 diff --git a/meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch b/meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch deleted file mode 100644 index cdd4e27a1..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0016-Disable-crabbyavif-to-fix-build-errors.patch +++ /dev/null @@ -1,97 +0,0 @@ -From a51bea5534076916587c4bebc2299d7881d6fad7 Mon Sep 17 00:00:00 2001 -From: Max Ihlenfeldt -Date: Mon, 5 Aug 2024 09:33:42 +0000 -Subject: [PATCH] Disable crabbyavif to fix build errors - -It seems the way upstream sets up bindgen does not play well with -cross-compiling in certain configurations. Currently, this only affects -building the crabbyavif third-party library, which is still being tested -behind a feature flag. Until we find a way to fix the bindgen setup -(either upstream or downstream), we just disable crabbyavif by reverting -the CL that added it to the build, https://crrev.com/c/5542730. - -Upstream-Status: Inappropriate [upstream ticket https://crbug.com/357017325] -Signed-off-by: Max Ihlenfeldt ---- - .../renderer/platform/image-decoders/BUILD.gn | 8 +------- - .../platform/image-decoders/image_decoder.cc | 18 ++++-------------- - 2 files changed, 5 insertions(+), 21 deletions(-) - -diff --git a/third_party/blink/renderer/platform/image-decoders/BUILD.gn b/third_party/blink/renderer/platform/image-decoders/BUILD.gn -index 6dbddaa2e3..2b5f660196 100644 ---- a/third_party/blink/renderer/platform/image-decoders/BUILD.gn -+++ b/third_party/blink/renderer/platform/image-decoders/BUILD.gn -@@ -82,12 +82,9 @@ component("image_decoders") { - sources += [ - "avif/avif_image_decoder.cc", - "avif/avif_image_decoder.h", -- "avif/crabbyavif_image_decoder.cc", -- "avif/crabbyavif_image_decoder.h", - ] - - deps += [ -- "//third_party/crabbyavif", - "//third_party/libavif", - ] - } -@@ -136,9 +133,6 @@ source_set("unit_tests") { - } - - if (enable_av1_decoder) { -- sources += [ -- "avif/avif_image_decoder_test.cc", -- "avif/crabbyavif_image_decoder_test.cc", -- ] -+ sources += [ "avif/avif_image_decoder_test.cc" ] - } - } -diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -index bdbbe08142..8b08f320cd 100644 ---- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc -@@ -37,7 +37,6 @@ - #include "media/media_buildflags.h" - #include "skia/ext/cicp.h" - #include "third_party/blink/public/common/buildflags.h" --#include "third_party/blink/public/common/features.h" - #include "third_party/blink/public/platform/platform.h" - #include "third_party/blink/renderer/platform/image-decoders/bmp/bmp_image_decoder.h" - #include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h" -@@ -53,7 +52,6 @@ - - #if BUILDFLAG(ENABLE_AV1_DECODER) - #include "third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h" --#include "third_party/blink/renderer/platform/image-decoders/avif/crabbyavif_image_decoder.h" - #endif - - namespace blink { -@@ -198,9 +196,7 @@ String SniffMimeTypeInternal(scoped_refptr reader) { - return "image/bmp"; - } - #if BUILDFLAG(ENABLE_AV1_DECODER) -- if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif) -- ? CrabbyAVIFImageDecoder::MatchesAVIFSignature(fast_reader) -- : AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { -+ if (AVIFImageDecoder::MatchesAVIFSignature(fast_reader)) { - return "image/avif"; - } - #endif -@@ -310,15 +306,9 @@ std::unique_ptr ImageDecoder::CreateByMimeType( - max_decoded_bytes); - #if BUILDFLAG(ENABLE_AV1_DECODER) - } else if (mime_type == "image/avif") { -- if (base::FeatureList::IsEnabled(blink::features::kCrabbyAvif)) { -- decoder = std::make_unique( -- alpha_option, high_bit_depth_decoding_option, color_behavior, -- aux_image, max_decoded_bytes, animation_option); -- } else { -- decoder = std::make_unique( -- alpha_option, high_bit_depth_decoding_option, color_behavior, -- aux_image, max_decoded_bytes, animation_option); -- } -+ decoder = std::make_unique( -+ alpha_option, high_bit_depth_decoding_option, color_behavior, -+ aux_image, max_decoded_bytes, animation_option); - #endif - } - diff --git a/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch b/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch deleted file mode 100644 index 6cae0abc0..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0017-rust-Use-adler-instead-of-adler2.patch +++ /dev/null @@ -1,38 +0,0 @@ -From d1d686cb5cebca571cb9eafc5a3cabff6971ddb6 Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Sat, 26 Apr 2025 17:19:24 -0300 -Subject: [PATCH] rust: Use adler instead of adler2 - -Upstream Rust replaced adler with adler2, for older versions of Rust we still -need to tell GN that we have the older lib when it tries to copy the Rust -sysroot into the bulid directory. - -It looks that in rust-1.85.0, `adler` has not yet been replaced with `adler2`. -Therefore, the condition should likely be adjusted. - -This patch will be dropped once Rust >= 1.86.0 is available, which -replaced adler with adler2. - -Note that currently layer provides Rust version `1.85.1`. - -[0] https://git.yoctoproject.org/meta-lts-mixins/log/?h=scarthgap/rust - -Upstream-Status: Inappropriate [specific to older versions of rust] -Signed-off-by: Ariel D'Alessandro ---- - build/rust/std/BUILD.gn | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/build/rust/std/BUILD.gn b/build/rust/std/BUILD.gn -index 7f64198b9f..d29e3466b1 100644 ---- a/build/rust/std/BUILD.gn -+++ b/build/rust/std/BUILD.gn -@@ -89,7 +89,7 @@ if (toolchain_has_rust) { - # These are no longer present in the Windows toolchain. - stdlib_files += [ - "addr2line", -- "adler2", -+ "adler", - "gimli", - "libc", - "memchr", diff --git a/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch b/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch deleted file mode 100644 index 30a9206b9..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0018-third_party-node-update_node_binaries-Update-nodejs-.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 6e3d91964a64dbacfddc4c7a38c64fd221fcdd2b Mon Sep 17 00:00:00 2001 -From: Ariel D'Alessandro -Date: Wed, 16 Jul 2025 18:44:44 -0300 -Subject: [PATCH] update_node_binaries: Update nodejs version to v22.15.0 - -Update the NodeJS version to match exactly the one shipped in these -images. - -Upstream-Status: Inappropriate [specific to our build setup] -Signed-off-by: Ariel D'Alessandro ---- - third_party/node/update_node_binaries | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/third_party/node/update_node_binaries b/third_party/node/update_node_binaries -index 4aab805844..5c3714f574 100755 ---- a/third_party/node/update_node_binaries -+++ b/third_party/node/update_node_binaries -@@ -19,7 +19,7 @@ set -eu - cd "$(dirname "$0")" - - BASE_URL="https://nodejs.org/dist" --NODE_VERSION="v22.11.0" -+NODE_VERSION="v22.15.0" - - upload=false # Default value - diff --git a/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch b/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch deleted file mode 100644 index 881863306..000000000 --- a/meta-chromium/recipes-browser/chromium/files/0019-rust-filter-out-arm-specific-flags-from-rust-compile.patch +++ /dev/null @@ -1,29 +0,0 @@ -From f29addf315e55f371c7c9a973002607a628ea3ea Mon Sep 17 00:00:00 2001 -From: Caner Altinbasak -Date: Fri, 25 Jul 2025 13:00:14 +0100 -Subject: [PATCH 19/19] rust: filter out arm specific flags from rust compiler - -Upstream-Status: Inappropriate [specific to our build setup] ---- - build/rust/filter_clang_args.py | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/build/rust/filter_clang_args.py b/build/rust/filter_clang_args.py -index 5a1843c0df..7fb4b49516 100644 ---- a/build/rust/filter_clang_args.py -+++ b/build/rust/filter_clang_args.py -@@ -24,6 +24,11 @@ def filter_clang_args(clangargs): - pass - elif args[i].startswith('-plugin-arg'): - i += 2 -+ # Filter out ARM-specific flags that aren't supported on other architectures -+ elif args[i].startswith('-mbranch-protection'): -+ pass # Skip this argument -+ elif args[i].startswith('-mfpu='): -+ pass - else: - yield args[i] - i += 1 --- -2.39.5 - diff --git a/meta-chromium/recipes-browser/chromium/gn-native_136.0.7103.113.bb b/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb similarity index 97% rename from meta-chromium/recipes-browser/chromium/gn-native_136.0.7103.113.bb rename to meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb index 5a3225be9..674283371 100644 --- a/meta-chromium/recipes-browser/chromium/gn-native_136.0.7103.113.bb +++ b/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb @@ -6,7 +6,7 @@ require chromium.inc inherit native -S = "${WORKDIR}/chromium-${PV}" +S = "${UNPACKDIR}/chromium-${PV}" # bootstrap.py --no_clean hardcodes the build location to out_bootstrap. # Omitting --no_clean causes the script to create a temporary directory with a From 266d4024d9b8fb1348b535b940ed86048c458186 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 26 Aug 2025 15:39:21 +0100 Subject: [PATCH 10/15] electron; Build recipe for Electron 37.1.0 OS-19689 Added an electron build recipe and supplementary files. Electron recipes include Chromium recipes and override the necessary parts like do_compile, do_configure. meta-chromium-test is updated to test electron, as well as chromium. Added an electron workflow to use that infrastructure. --- .github/workflows/chromium.yml | 11 +- .github/workflows/electron.yml | 61 +++++ .../recipes-browser/chromium/electron-gn.inc | 75 ++++++ .../chromium/electron-ozone-nexus.bb | 37 +++ .../chromium/electron-ozone-wayland.bb | 46 ++++ .../chromium/electron-ozone-x11.bb | 50 ++++ .../recipes-browser/chromium/electron.inc | 43 ++++ ...c-toolchain-for-Yocto-cross-compilat.patch | 37 +++ .../0002-fix-Remove-X11-dependencies.patch | 74 ++++++ ...03-fix-Electron-yocto-build-problems.patch | 225 ++++++++++++++++++ .../chromium/files/remove_dri.patch | 22 ++ 11 files changed, 677 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/electron.yml create mode 100644 meta-chromium/recipes-browser/chromium/electron-gn.inc create mode 100644 meta-chromium/recipes-browser/chromium/electron-ozone-nexus.bb create mode 100644 meta-chromium/recipes-browser/chromium/electron-ozone-wayland.bb create mode 100644 meta-chromium/recipes-browser/chromium/electron-ozone-x11.bb create mode 100644 meta-chromium/recipes-browser/chromium/electron.inc create mode 100644 meta-chromium/recipes-browser/chromium/files/0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/0002-fix-Remove-X11-dependencies.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/0003-fix-Electron-yocto-build-problems.patch create mode 100644 meta-chromium/recipes-browser/chromium/files/remove_dri.patch diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 2add96351..4f5d7899b 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -15,7 +15,9 @@ on: branches: - master paths: - - 'meta-chromium/**' + - 'meta-chromium/recipes-browser/chromium/files/**' + - 'meta-chromium/recipes-browser/chromium/chromium*' + - 'meta-chromium/recipes-browser/chromium/gn*' - '.github/workflows/chromium.yml' permissions: @@ -29,7 +31,8 @@ jobs: strategy: matrix: yocto_version: [master] - chromium_version: [ozone-wayland, x11] + browser_version: [ozone-wayland, x11] + browser: [chromium] libc_flavour: [glibc] arch: [arm, aarch64, x86-64] runs-on: [self-hosted, chromium] @@ -54,5 +57,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone -b main https://github.com/brightsign/meta-chromium-test.git - ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.chromium_version }} ${{ matrix.libc_flavour}} + git clone -b electron-master https://github.com/brightsign/meta-chromium-test.git + ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml new file mode 100644 index 000000000..ae174c85b --- /dev/null +++ b/.github/workflows/electron.yml @@ -0,0 +1,61 @@ +name: Electron build- and smoke-test + +on: + workflow_dispatch: + inputs: + repository: + description: 'Repository to clone for the workflow' + required: true + default: 'brightsign' + branch: + description: 'Branch to checkout for the workflow' + required: true + default: 'master' + pull_request: + branches: + - master + paths: + - 'meta-chromium/recipes-browser/chromium/files/**' + - 'meta-chromium/recipes-browser/chromium/electron*' + - 'meta-chromium/recipes-browser/chromium/gn*' + - '.github/workflows/electron.yml' + +permissions: + contents: read + actions: read + checks: write + +jobs: + build: + if: ${{ github.repository_owner == 'brightsign' }} + strategy: + matrix: + yocto_version: [master] + browser_version: [ozone-wayland, ozone-x11] + browser: [electron] + libc_flavour: [glibc] + arch: [arm, aarch64, x86-64] + runs-on: [self-hosted, chromium] + container: + image: skandigraun/yocto:latest + volumes: + - yocto:/yocto + steps: + - run: | + mkdir -p /yocto/${{ matrix.yocto_version }} + cd /yocto/${{ matrix.yocto_version }} + rm -rf meta-browser meta-chromium-test + # Clean stale pseudo state from any previous interrupted builds + rm -rf build/tmp/work/*/*/*/pseudo build/tmp/sysroots-components/*/pseudo 2>/dev/null || true + if [ "${{ github.event_name }}" = "pull_request" ]; then + GH_URL="$GITHUB_SERVER_URL/${{ github.event.pull_request.head.repo.full_name }}" + GH_REV="$GITHUB_HEAD_REF" + else + GH_URL="$GITHUB_SERVER_URL/${{ github.event.inputs.repository }}/meta-browser" + GH_REV="${{ github.event.inputs.branch }}" + fi + git clone $GH_URL + git -C meta-browser checkout $GH_REV + # clone the test repo + git clone -b electron-master https://github.com/brightsign/meta-chromium-test.git + ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} diff --git a/meta-chromium/recipes-browser/chromium/electron-gn.inc b/meta-chromium/recipes-browser/chromium/electron-gn.inc new file mode 100644 index 000000000..caeec472e --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/electron-gn.inc @@ -0,0 +1,75 @@ +require electron.inc + +# Override the compile target for Electron +do_compile() { + ninja -v ${PARALLEL_MAKE} electron +} +do_compile[progress] = "outof:^\[(\d+)/(\d+)\]\s+" + +# Override the install task for Electron-specific installation +do_install() { + install -d ${D}${bindir} + install -d ${D}${libdir}/electron + install -d ${D}${libdir}/electron/locales + install -d ${D}${libdir}/electron/resources + + install -m 0755 electron ${D}${libdir}/electron/electron + ln -s ${libdir}/electron/electron ${D}${bindir}/electron + install -m 0644 resources/default_app.asar ${D}${libdir}/electron/resources/default_app.asar + install -m 0644 *.bin ${D}${libdir}/electron/ + install -m 0644 icudtl.dat ${D}${libdir}/electron/icudtl.dat + + # Chromium *.pak files + install -m 0644 chrome_*.pak ${D}${libdir}/electron/ + install -m 0644 resources.pak ${D}${libdir}/electron/resources.pak + + # Locales. + install -m 0644 locales/*.pak ${D}${libdir}/electron/locales/ + + install -m 0755 libffmpeg.so ${D}${libdir}/electron/ + + # ANGLE. + if [ -e libEGL.so ]; then + install -m 0644 libEGL.so ${D}${libdir}/electron/ + install -m 0644 libGLESv2.so ${D}${libdir}/electron/ + fi + + # libvulkan (for ANGLE). + if [ -e libvulkan.so.1 ]; then + install -m 0644 libvulkan.so.1 ${D}${libdir}/electron/ + fi + + # Swiftshader VK. + if [ -e libvk_swiftshader.so ]; then + install -m 0644 libvk_swiftshader.so ${D}${libdir}/electron/ + # This is needed for ANGLE to find libvk_swiftshader.so. + install -m 0644 vk_swiftshader_icd.json ${D}${libdir}/electron/ + fi + + if [ -n "${@bb.utils.contains('PACKAGECONFIG', 'component-build', 'component-build', '', d)}" ]; then + install -m 0755 *.so ${D}${libdir}/electron/ + fi + + # When building chromium with use_system_minigbm=false, + # libminigbm.so does not seem to get linked in statically. + # So we simply check whether it exists in all cases and ship it. + if [ -e libminigbm.so ]; then + install -m 0755 libminigbm.so ${D}${libdir}/electron/ + fi +} + +# Electron-specific package files +FILES:${PN} = " \ + ${bindir}/electron \ + ${libdir}/electron/* \ +" + +PACKAGE_DEBUG_SPLIT_STYLE = "debug-without-src" + +# There is no need to ship empty -dev packages. +ALLOW_EMPTY:${PN}-dev = "0" + +# ERROR: QA Issue: lib32-chromium-ozone-wayland: ELF binary /usr/bin/chromium has relocations in .text [textrel] +INSANE_SKIP:${PN}:append:x86 = "textrel" + +PACKAGECONFIG:append = " proprietary-codecs" diff --git a/meta-chromium/recipes-browser/chromium/electron-ozone-nexus.bb b/meta-chromium/recipes-browser/chromium/electron-ozone-nexus.bb new file mode 100644 index 000000000..4d5855167 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/electron-ozone-nexus.bb @@ -0,0 +1,37 @@ +require electron-gn.inc + +# Only Broadcom STB chips have have Nexus and Bitbake will complain +# when building for anything else without something like this. +COMPATIBLE_MACHINE = "^brcmstb$" + +RPROVIDES:${PN} = "electron" + +SRC_URI += "\ + file://remove_dri.patch \ +" + +DEPENDS += "\ + libnotify \ + at-spi2-atk \ + libffi \ + nexus \ +" + +GN_ARGS += "\ + ${PACKAGECONFIG_CONFARGS} \ + use_ozone=true \ + ozone_auto_platforms=false \ + ozone_platform_nexus=true \ + use_xkbcommon=true \ + use_system_minigbm=true \ + use_system_libffi=true \ + use_gtk=false \ + use_gio=false \ + use_lttng=true \ + use_bundled_fontconfig=false \ + override_electron_version="${PV}" \ + import("//electron/build/args/release.gn") \ +" + +# The electron binary must always be started with those arguments. +CHROMIUM_EXTRA_ARGS:append = " --ozone-platform=nexus" diff --git a/meta-chromium/recipes-browser/chromium/electron-ozone-wayland.bb b/meta-chromium/recipes-browser/chromium/electron-ozone-wayland.bb new file mode 100644 index 000000000..ecb2e521d --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/electron-ozone-wayland.bb @@ -0,0 +1,46 @@ +require electron-gn.inc + +RPROVIDES:${PN} = "electron" + +REQUIRED_DISTRO_FEATURES = "wayland" + +DEPENDS += "\ + libnotify \ + at-spi2-atk \ + virtual/egl \ + wayland \ + wayland-native \ + libffi \ + gtk+3 \ +" + +GN_ARGS += "\ + ${PACKAGECONFIG_CONFARGS} \ + use_ozone=true \ + ozone_auto_platforms=false \ + ozone_platform_headless=true \ + ozone_platform_wayland=true \ + use_system_wayland_scanner=false \ + use_xkbcommon=true \ + use_system_libwayland=false \ + use_system_minigbm=true \ + use_system_libffi=true \ + use_bundled_fontconfig=false \ + override_electron_version="${PV}" \ + import("//electron/build/args/release.gn") \ +" + +# The electron binary must always be started with those arguments. +CHROMIUM_EXTRA_ARGS:append = " --ozone-platform=wayland" + +# Angle is not used by Wayland yet, but it was still built. It wasn't a problem +# until wayland-protocols were updated to 1.20 in newest yocto releases, which +# changed the implementation of the wayland-scanner tool. That tool uses a new +# API, which is not part of older wayland-protocols. And given ANGLE includes +# libwayland headers from //third_party/wayland instead of relaying on the +# system ones, that results in undeclared methods. +# TODO(msisov): remove this once Chromium's //third_party/wayland is updated +# to >=1.20. This tracks https://crbug.com/1359189 +GN_ARGS += "\ + angle_use_wayland=false \ +" diff --git a/meta-chromium/recipes-browser/chromium/electron-ozone-x11.bb b/meta-chromium/recipes-browser/chromium/electron-ozone-x11.bb new file mode 100644 index 000000000..94bcb2dc2 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/electron-ozone-x11.bb @@ -0,0 +1,50 @@ +require electron-gn.inc + +RPROVIDES:${PN} = "electron" + +REQUIRED_DISTRO_FEATURES = "x11" + +DEPENDS += "\ + libnotify \ + libx11 \ + libxcomposite \ + libxcursor \ + libxdamage \ + libxext \ + libxfixes \ + libxi \ + libxrandr \ + libxrender \ + libxscrnsaver \ + libxtst \ + at-spi2-atk \ + virtual/egl \ + libffi \ + gtk+3 \ +" + +# Loaded at runtime. +# https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/x/xlib_support.cc +RDEPENDS:${PN} += "libx11-xcb" + +GN_ARGS += "\ + ${PACKAGECONFIG_CONFARGS} \ + use_ozone=true \ + ozone_auto_platforms=false \ + ozone_platform_headless=true \ + ozone_platform_wayland=false \ + ozone_platform_x11=true \ + use_xkbcommon=true \ + use_system_minigbm=true \ + use_system_libffi=true \ + use_bundled_fontconfig=false \ + override_electron_version="${PV}" \ + import("//electron/build/args/release.gn") \ +" + +# The electron binary must always be started with those arguments. +CHROMIUM_EXTRA_ARGS:append = " --ozone-platform=x11" + +# Compatibility glue while we have both electron-ozone-x11 and +# electron-ozone-wayland recipes. +PROVIDES = "electron" diff --git a/meta-chromium/recipes-browser/chromium/electron.inc b/meta-chromium/recipes-browser/chromium/electron.inc new file mode 100644 index 000000000..56da87f63 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/electron.inc @@ -0,0 +1,43 @@ +# Inherit from chromium-gn.inc to get all shared configuration +require chromium-gn.inc + +# Electron-specific metadata +DESCRIPTION = "Electron is an open-source project that aims to enable building cross-platform desktop apps with JavaScript, HTML, and CSS" +HOMEPAGE = "https://www.electronjs.org/" + +# Override CVE product to include Electron and Node.js +CVE_PRODUCT = "chromium:chromium google:chrome electronjs:electron nodejs:node.js" + +# Electron version - override the Chromium version +PV = "37.1.0-bs.alpha2" + +# Electron-specific source - replace Chromium source with Electron source +SRC_URI = "https://electron-ci-public-artifact-bucket.s3.amazonaws.com/v${PV}/v${PV}.tar.xz" +SRC_URI[sha256sum] = "4921ad086f4c7be4374cd6b1e18a22c0f90a58c6b2253b6e90f16ae88c9cfb12" +S = "${UNPACKDIR}/src" + +SRC_URI += " \ + file://0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch \ + file://0002-fix-Remove-X11-dependencies.patch \ + file://0003-fix-Electron-yocto-build-problems.patch \ +" + +# Non-specific patches. +SRC_URI += "\ + file://0001-Drop-GN-compiler-settings-conflicting-with-OE.patch \ + file://0002-v8-qemu-wrapper.patch \ + file://0003-wrapper-extra-flags.patch \ + file://0004-Delete-compiler-options-not-available-in-release-ver.patch \ + file://0005-avoid-link-latomic-failure-on-CentOS-8-host.patch \ + file://0006-Don-t-pass-unknown-LLVM-options.patch \ + file://0007-Fix-constexpr-variable-must-be-initialized-by-a-cons.patch \ + file://0008-Use-the-correct-path-to-libclang_rt.builtins.a.patch \ + file://0009-Adjust-the-Rust-build-to-our-needs.patch \ + file://0010-Don-t-require-profiler_builtins.rlib.patch \ + file://0011-fix-check_version-Only-compare-node.js-major-version.patch \ + file://0012-chromium-fix-v4l2-compiler-error-on-arm.patch \ +" + +# ARM/AArch64-specific patches. +# Note: 0001-Fix-AES-crypto-SIGILL-on-rpi4-64.patch is already included in Electron source +# SRC_URI:append:aarch64 = "${@bb.utils.contains('TUNE_FEATURES', 'crypto', '', ' file://arm/0001-Fix-AES-crypto-SIGILL-on-rpi4-64.patch', d)}" diff --git a/meta-chromium/recipes-browser/chromium/files/0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch b/meta-chromium/recipes-browser/chromium/files/0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch new file mode 100644 index 000000000..11a7b99a7 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch @@ -0,0 +1,37 @@ +From eb0e4dad4045362cc2cb2b16296f8df79a89c1e9 Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Tue, 29 Jul 2025 00:00:00 +0000 +Subject: [PATCH] electron: fix js2c toolchain for Yocto cross-compilation + +The electron js2c toolchain selection logic doesn't properly handle +Yocto cross-compilation environments where host tools need to be built +with the host toolchain rather than the target toolchain. + +This patch forces the electron_js2c_toolchain to use host_toolchain +in cross-compilation scenarios to ensure node_js2c can execute on +the build machine. + +Upstream-Status: Inappropriate [OE-specific] +--- + electron/build/js2c_toolchain.gni | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/electron/build/js2c_toolchain.gni b/electron/build/js2c_toolchain.gni +index b56590857c..b754143aba 100644 +--- a/electron/build/js2c_toolchain.gni ++++ b/electron/build/js2c_toolchain.gni +@@ -6,6 +6,11 @@ declare_args() { + electron_js2c_toolchain = "" + } + ++# Force host toolchain for Yocto cross-compilation ++if (electron_js2c_toolchain == "" && defined(host_toolchain)) { ++ electron_js2c_toolchain = host_toolchain ++} ++ + if (electron_js2c_toolchain == "") { + if (current_os == host_os && current_cpu == host_cpu) { + # This is not a cross-compile, so build the snapshot with the current +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0002-fix-Remove-X11-dependencies.patch b/meta-chromium/recipes-browser/chromium/files/0002-fix-Remove-X11-dependencies.patch new file mode 100644 index 000000000..2f1f86b7e --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0002-fix-Remove-X11-dependencies.patch @@ -0,0 +1,74 @@ +From a1b544e401fee3aff490de23c0c4749d797d4448 Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Tue, 29 Jul 2025 13:35:16 +0100 +Subject: [PATCH] fix: Remove X11 dependencies + +Electron introduces X11 dependencies in wayland ozone platform backend. +This is undesired behaviour. + +Upstream-Status: Inappropriate +--- + electron/BUILD.gn | 8 +++++--- + .../shell/browser/api/electron_api_desktop_capturer.cc | 2 ++ + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/electron/BUILD.gn b/electron/BUILD.gn +index fe4fb706f5..3308a5bb92 100644 +--- a/electron/BUILD.gn ++++ b/electron/BUILD.gn +@@ -637,7 +637,6 @@ source_set("electron_lib") { + } + } + if (is_linux) { +- libs = [ "xshmfence" ] + deps += [ + ":electron_gtk_stubs", + ":libnotify_loader", +@@ -647,19 +646,22 @@ source_set("electron_lib") { + "//device/bluetooth", + "//third_party/crashpad/crashpad/client", + "//ui/base/ime/linux", +- "//ui/events/devices/x11", +- "//ui/events/platform/x11", + "//ui/gtk:gtk_config", + "//ui/linux:linux_ui", + "//ui/linux:linux_ui_factory", + "//ui/wm", + ] + if (ozone_platform_x11) { ++ libs = [ "xshmfence" ] + sources += filenames.lib_sources_linux_x11 + public_deps += [ + "//ui/base/x", + "//ui/ozone/platform/x11", + ] ++ deps += [ ++ "//ui/events/devices/x11", ++ "//ui/events/platform/x11", ++ ] + } + configs += [ ":gio_unix" ] + defines += [ +diff --git a/electron/shell/browser/api/electron_api_desktop_capturer.cc b/electron/shell/browser/api/electron_api_desktop_capturer.cc +index d0b3f40b5d..e43bd39199 100644 +--- a/electron/shell/browser/api/electron_api_desktop_capturer.cc ++++ b/electron/shell/browser/api/electron_api_desktop_capturer.cc +@@ -49,6 +49,7 @@ + + namespace { + #if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_OZONE_X11) + // Private function in ui/base/x/x11_display_util.cc + base::flat_map GetMonitors( + std::pair version, +@@ -143,6 +144,7 @@ base::flat_map MonitorAtomIdToDisplayId() { + return monitor_atom_to_display; + } + #endif ++#endif + + std::unique_ptr MakeWindowCapturer() { + #if BUILDFLAG(IS_MAC) +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/0003-fix-Electron-yocto-build-problems.patch b/meta-chromium/recipes-browser/chromium/files/0003-fix-Electron-yocto-build-problems.patch new file mode 100644 index 000000000..0cae9097a --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/0003-fix-Electron-yocto-build-problems.patch @@ -0,0 +1,225 @@ +From e1d41130d98f66b0a48e07bf8d76737caee3d65b Mon Sep 17 00:00:00 2001 +From: Caner Altinbasak +Date: Tue, 29 Jul 2025 17:21:47 +0100 +Subject: [PATCH] fix: Electron yocto build problems + +Upstream-Status: Inappropriate +--- + content/zygote/zygote_linux.cc | 1 + + electron/shell/browser/native_window_views.cc | 24 ++++++++++--------- + electron/shell/browser/native_window_views.h | 4 ++-- + .../browser/osr/osr_host_display_client.cc | 2 +- + .../browser/osr/osr_host_display_client.h | 2 +- + electron/shell/browser/ui/gtk/menu_util.cc | 7 ++++++ + 6 files changed, 25 insertions(+), 15 deletions(-) + +diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc +index 003169cf7d..07a5f855b1 100644 +--- a/content/zygote/zygote_linux.cc ++++ b/content/zygote/zygote_linux.cc +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + #include +diff --git a/electron/shell/browser/native_window_views.cc b/electron/shell/browser/native_window_views.cc +index 3b18f05042..25cdaa4027 100644 +--- a/electron/shell/browser/native_window_views.cc ++++ b/electron/shell/browser/native_window_views.cc +@@ -333,6 +333,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, + if (parent) + SetParentWindow(parent); + ++#if BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + // Before the window is mapped the SetWMSpecState can not work, so we have + // to manually set the _NET_WM_STATE. +@@ -365,6 +366,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, + window_type); + } + #endif ++#endif + + #if BUILDFLAG(IS_WIN) + if (!has_frame()) { +@@ -478,7 +480,7 @@ NativeWindowViews::~NativeWindowViews() { + } + + void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) { +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + const std::string color = use_dark_theme ? "dark" : "light"; + auto* connection = x11::Connection::Get(); +@@ -566,7 +568,7 @@ void NativeWindowViews::Show() { + + NotifyWindowShow(); + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (global_menu_bar_) + global_menu_bar_->OnWindowMapped(); + +@@ -582,7 +584,7 @@ void NativeWindowViews::ShowInactive() { + + NotifyWindowShow(); + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (global_menu_bar_) + global_menu_bar_->OnWindowMapped(); + +@@ -601,7 +603,7 @@ void NativeWindowViews::Hide() { + + NotifyWindowHide(); + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (global_menu_bar_) + global_menu_bar_->OnWindowUnmapped(); + #endif +@@ -632,7 +634,7 @@ bool NativeWindowViews::IsVisible() const { + bool NativeWindowViews::IsEnabled() const { + #if BUILDFLAG(IS_WIN) + return ::IsWindowEnabled(GetAcceleratedWidget()); +-#elif BUILDFLAG(IS_LINUX) ++#elif BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) + return !event_disabler_.get(); + NOTIMPLEMENTED(); +@@ -669,7 +671,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { + + #if BUILDFLAG(IS_WIN) + ::EnableWindow(GetAcceleratedWidget(), enable); +-#else ++#elif BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + views::DesktopWindowTreeHostPlatform* tree_host = + views::DesktopWindowTreeHostLinux::GetHostForWidget( +@@ -1001,7 +1003,7 @@ bool NativeWindowViews::MoveAbove(const std::string& sourceId) { + ::SetWindowPos(GetAcceleratedWidget(), GetWindow(otherWindow, GW_HWNDPREV), 0, + 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +-#else ++#elif BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + if (!IsWindowValid(static_cast(id.id))) + return false; +@@ -1023,7 +1025,7 @@ void NativeWindowViews::MoveTop() { + ::SetWindowPos(GetAcceleratedWidget(), HWND_TOP, pos.x(), pos.y(), + size.width(), size.height(), + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +-#else ++#elif BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) + electron::MoveWindowToForeground( + static_cast(GetAcceleratedWidget())); +@@ -1365,7 +1367,7 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { + } else { + SetForwardMouseMessages(forward); + } +-#else ++#elif BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + auto* connection = x11::Connection::Get(); + if (ignore) { +@@ -1431,7 +1433,7 @@ bool NativeWindowViews::IsFocusable() const { + } + + void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) { +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + // Remove global menu bar. + if (global_menu_bar_ && menu_model == nullptr) { + global_menu_bar_.reset(); +@@ -1487,7 +1489,7 @@ void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) { + void NativeWindowViews::SetParentWindow(NativeWindow* parent) { + NativeWindow::SetParentWindow(parent); + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + if (x11_util::IsX11()) { + auto* connection = x11::Connection::Get(); + connection->SetProperty( +diff --git a/electron/shell/browser/native_window_views.h b/electron/shell/browser/native_window_views.h +index 7e192ff817..042bb5ee25 100644 +--- a/electron/shell/browser/native_window_views.h ++++ b/electron/shell/browser/native_window_views.h +@@ -28,7 +28,7 @@ + + namespace electron { + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + class GlobalMenuBarX11; + #endif + +@@ -262,7 +262,7 @@ class NativeWindowViews : public NativeWindow, + // events from resizing the window. + extensions::SizeConstraints old_size_constraints_; + +-#if BUILDFLAG(IS_LINUX) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + std::unique_ptr global_menu_bar_; + #endif + +diff --git a/electron/shell/browser/osr/osr_host_display_client.cc b/electron/shell/browser/osr/osr_host_display_client.cc +index 8f316f0449..b037fa8bf8 100644 +--- a/electron/shell/browser/osr/osr_host_display_client.cc ++++ b/electron/shell/browser/osr/osr_host_display_client.cc +@@ -95,7 +95,7 @@ void OffScreenHostDisplayClient::CreateLayeredWindowUpdater( + layered_window_updater_->SetActive(active_); + } + +-#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + void OffScreenHostDisplayClient::DidCompleteSwapWithNewSize( + const gfx::Size& size) {} + #endif +diff --git a/electron/shell/browser/osr/osr_host_display_client.h b/electron/shell/browser/osr/osr_host_display_client.h +index e95eb43bd1..0f4e0386d3 100644 +--- a/electron/shell/browser/osr/osr_host_display_client.h ++++ b/electron/shell/browser/osr/osr_host_display_client.h +@@ -74,7 +74,7 @@ class OffScreenHostDisplayClient : public viz::HostDisplayClient { + mojo::PendingReceiver receiver) + override; + +-#if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) ++#if BUILDFLAG(IS_LINUX) && BUILDFLAG(IS_OZONE_X11) + void DidCompleteSwapWithNewSize(const gfx::Size& size) override; + #endif + +diff --git a/electron/shell/browser/ui/gtk/menu_util.cc b/electron/shell/browser/ui/gtk/menu_util.cc +index 76bc5862f6..28204fab5a 100644 +--- a/electron/shell/browser/ui/gtk/menu_util.cc ++++ b/electron/shell/browser/ui/gtk/menu_util.cc +@@ -18,8 +18,11 @@ + #include "ui/base/accelerators/menu_label_accelerator_util_linux.h" + #include "ui/base/models/image_model.h" + #include "ui/base/models/menu_model.h" ++#include "ui/base/ozone_buildflags.h" + #include "ui/events/event_constants.h" ++#if BUILDFLAG(IS_OZONE_X11) + #include "ui/events/keycodes/keyboard_code_conversion_x.h" ++#endif + #include "ui/ozone/public/ozone_platform.h" + + namespace electron::gtkui { +@@ -42,7 +45,11 @@ int EventFlagsFromGdkState(guint state) { + guint GetGdkKeyCodeForAccelerator(const ui::Accelerator& accelerator) { + // The second parameter is false because accelerator keys are expressed in + // terms of the non-shift-modified key. ++#if BUILDFLAG(IS_OZONE_X11) + return XKeysymForWindowsKeyCode(accelerator.key_code(), false); ++#else ++ return 0; ++#endif + } + + GdkModifierType GetGdkModifierForAccelerator( +-- +2.39.5 + diff --git a/meta-chromium/recipes-browser/chromium/files/remove_dri.patch b/meta-chromium/recipes-browser/chromium/files/remove_dri.patch new file mode 100644 index 000000000..e2b559fb2 --- /dev/null +++ b/meta-chromium/recipes-browser/chromium/files/remove_dri.patch @@ -0,0 +1,22 @@ +diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn +index 93559f6092c7a..f257fa50077e9 100644 +--- a/content/gpu/BUILD.gn ++++ b/content/gpu/BUILD.gn +@@ -141,6 +141,5 @@ target(link_target_type, "gpu_sources") { + # Use DRI on desktop Linux builds. + if (current_cpu != "s390x" && current_cpu != "ppc64" && is_linux && + !is_castos) { +- configs += [ "//build/config/linux/dri" ] + } + } +diff --git a/media/gpu/sandbox/BUILD.gn b/media/gpu/sandbox/BUILD.gn +index cfcb7fa80ef89..8d149450b2116 100644 +--- a/media/gpu/sandbox/BUILD.gn ++++ b/media/gpu/sandbox/BUILD.gn +@@ -33,6 +33,5 @@ source_set("sandbox") { + if (current_cpu != "s390x" && current_cpu != "ppc64" && is_linux && + !is_castos) { + # For DRI_DRIVER_DIR. +- configs += [ "//build/config/linux/dri" ] + } + } From fdb8f4b4e4f418f998579b021f93b59ed457abe3 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Mon, 1 Sep 2025 10:57:33 +0100 Subject: [PATCH 11/15] feat: GitHub actions for building/testing Electron37 on AWS OS-19493 This changeset introduces major improvements to the GitHub Actions CI/CD workflows for the meta-browser project. The changes add support for running builds and tests on AWS EC2 instances for both Chromium and Electron, enable matrix builds across multiple configurations, and provide a new workflow for cleaning up EFS cache storage. The updates make the CI system more scalable, configurable, and better suited for large builds. New EC2-based CI workflow and matrix build support: Added a new workflow file bs_meta_browser_ci_ec2.yml that provisions EC2 runners, tags resources, and orchestrates build/test jobs for meta-browser on AWS, including automatic runner startup and cleanup. Updated chromium.yml and electron.yml to support matrix builds on AWS EC2, with configurable instance types, build parameters, and the option to fall back to local runners for manual dispatch. This enables parallel builds across Yocto versions, platforms, architectures, and libc flavors. Infrastructure and configuration enhancements: Added OIDC authentication permissions (id-token: write) to workflows for secure AWS credential management. Improved EC2 runner tagging for better traceability and resource management, including passing build metadata as AWS resource tags. EFS cache management: Introduced a new workflow cleanup-efs-cache.yml to automate cleanup of Yocto downloads and sstate-cache directories on EFS using a temporary EC2 runner, with options for selective cleanup and disk usage reporting. Minor fixes and consistency: Updated test repo cloning in build scripts to remove hardcoded branch, ensuring consistency for both Chromium and Electron workflows. --- .../bs_meta_browser_build_and_test.yml | 526 ++++++++++++++++++ .github/workflows/bs_meta_browser_ci_ec2.yml | 156 ++++++ .github/workflows/chromium.yml | 47 +- .github/workflows/cleanup-efs-cache.yml | 169 ++++++ .github/workflows/electron.yml | 47 +- 5 files changed, 939 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/bs_meta_browser_build_and_test.yml create mode 100644 .github/workflows/bs_meta_browser_ci_ec2.yml create mode 100644 .github/workflows/cleanup-efs-cache.yml diff --git a/.github/workflows/bs_meta_browser_build_and_test.yml b/.github/workflows/bs_meta_browser_build_and_test.yml new file mode 100644 index 000000000..e539c55ad --- /dev/null +++ b/.github/workflows/bs_meta_browser_build_and_test.yml @@ -0,0 +1,526 @@ +name: 'BrightSign Build and Test Meta-Browser: Build and test workflow' +on: + workflow_call: + inputs: + runner_name: + description: 'Runner name' + required: true + type: string + + github_hosted_runner: + description: 'Whether to use GitHub-hosted runner' + required: false + type: boolean + default: false + + browser: + description: "Electron or Chromium" + required: true + type: string + + build_type: + description: 'Build Type' + required: true + type: string + + yocto_version: + description: 'Yocto version' + required: true + type: string + + chromium_version: + description: 'Display backend/Ozone platform (ozone-wayland or x11)' + required: true + type: string + + libc_flavour: + description: 'libc flavour' + required: true + type: string + + arch: + description: 'Architecture' + required: true + type: string + + aws_arn_role: + required: true + type: string + + aws_region: + required: true + type: string + +jobs: + build-and-test-meta-browser: + name: Build and Test Meta-Browser + runs-on: ${{ inputs.runner_name }} + timeout-minutes: 660 # 11 hours - increase for long Yocto builds + defaults: + run: + shell: bash + steps: + # Configure AWS credentials for accessing S3 cache buckets + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ inputs.aws_arn_role }} + role-session-name: mb-build-${{ github.run_id }} + role-duration-seconds: 43200 # 12 hours - maximum for most IAM roles + + # Download config file and set environment variables + - name: Download config file and set env vars from it + run: | + aws s3 cp s3://meta-browser-ci-config-bucket/config.json . + aws s3 cp s3://meta-browser-ci-config-bucket/set_github_env_vars.py . + python3 set_github_env_vars.py --file config.json + + # Quick system verification (using pre-configured custom AMI) + - name: Verify AMI configuration + run: | + echo "=== Verifying pre-configured AMI ===" + echo "AMI Info:" + cat /etc/ami-info || echo "No AMI info file found" + + echo "" + echo "Checking KAS installation:" + kas --version || echo "KAS not found" + + echo "" + echo "Checking locale:" + locale + + echo "" + echo "Checking AWS CLI:" + aws --version || echo "AWS CLI not found" + + echo "" + echo "Available disk space:" + df -h + + echo "" + echo "Memory info:" + free -h + + echo "✓ AMI verification complete - all dependencies pre-installed" + + # Setup large swap space on local NVMe SSD for memory-intensive Yocto builds + - name: Setup NVMe storage and swap + run: | + echo "=== Setting up NVMe storage and swap ===" + + # Get current user once for all operations + CURRENT_USER=$(whoami) + echo "Setting up NVMe storage for user: $CURRENT_USER" + + # Check for NVMe storage and set up build directory + if [ -e /dev/nvme1n1 ]; then + echo "NVMe storage detected, setting up build directory..." + + # Create filesystem if not already done + if ! mount | grep -q nvme1n1; then + sudo mkfs.ext4 -F /dev/nvme1n1 + sudo mkdir -p /mnt/nvme + sudo mount /dev/nvme1n1 /mnt/nvme + sudo chown $CURRENT_USER:$CURRENT_USER /mnt/nvme + fi + + # Create build directory on NVMe storage + echo "Setting up build workspace on NVMe storage..." + sudo mkdir -p /mnt/nvme/yocto-build + sudo chown $CURRENT_USER:$CURRENT_USER /mnt/nvme/yocto-build + + # Create symlink from /yocto to NVMe storage + sudo rm -rf /yocto 2>/dev/null || true + sudo ln -sf /mnt/nvme/yocto-build /yocto + sudo chown -h $CURRENT_USER:$CURRENT_USER /yocto + + # Set up swap on NVMe (256GB) + echo "Creating 256GB swap file on NVMe SSD..." + sudo fallocate -l 256G /mnt/nvme/swapfile + sudo chmod 600 /mnt/nvme/swapfile + sudo mkswap /mnt/nvme/swapfile + sudo swapon /mnt/nvme/swapfile + + echo "✓ Build directory and swap configured on NVMe storage" + else + echo "ERROR: No NVMe device found! Instance should have NVMe storage for optimal build performance." + echo "Available block devices:" + lsblk + exit 1 + fi + + # Verify swap is active + echo "=== Memory and swap status ===" + free -h + swapon --show + echo "Available disk space:" + df -h + + # Mount EFS file systems and setup cache directories + - name: Setup EFS mounts and cache directories + run: | + # Get current user for consistent permission management + CURRENT_USER=$(whoami) + echo "Setting up EFS mounts and cache directories for user: $CURRENT_USER" + + # Create mount points and cache directories + sudo mkdir -p /mnt/shared-cache/sstate-cache /mnt/shared-cache/downloads /mnt/git-mirror + + # Debug DNS resolution + echo "=== DNS Troubleshooting ===" + echo "Checking DNS resolution for EFS endpoints..." + nslookup ${{ env.SHARED_CACHE_EFS_ID }}.efs.${{ env.AWS_REGION }}.amazonaws.com || echo "DNS lookup failed" + nslookup ${{ env.GIT_MIRROR_EFS_ID }}.efs.${{ env.AWS_REGION }}.amazonaws.com || echo "DNS lookup failed" + + # Check AWS metadata service and VPC DNS + echo "=== VPC DNS Settings ===" + curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/ | head -1 | xargs -I {} curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/{}/vpc-id || echo "VPC metadata failed" + + # Check if we can reach AWS services + echo "=== AWS Connectivity Check ===" + if curl -s --connect-timeout 10 https://s3.amazonaws.com; then + echo "✓ S3 reachable" + else + echo "✗ S3 not reachable - this will cause build artifact upload to fail" + echo "Network connectivity issue detected" + exit 1 + fi + + # Alternative: Use mount target IP if DNS fails + echo "=== Alternative EFS Mount Strategy ===" + # Try to resolve mount target IPs through AWS CLI if available + if command -v aws &> /dev/null; then + echo "Attempting to get EFS mount targets..." + aws efs describe-mount-targets --file-system-id ${{ env.SHARED_CACHE_EFS_ID }} --region ${{ env.AWS_REGION }} || echo "AWS CLI describe failed" + fi + + # Try using regional EFS mount target instead + echo "=== Mounting EFS file systems ===" + echo "Shared cache EFS: ${{ env.SHARED_CACHE_EFS_ID }}" + echo "Git mirror EFS: ${{ env.GIT_MIRROR_EFS_ID }}" + echo "AWS Region: ${{ env.AWS_REGION }}" + + # Get EFS mount target IPs for fallback + SHARED_CACHE_IP=$(aws efs describe-mount-targets --file-system-id ${{ env.SHARED_CACHE_EFS_ID }} --region ${{ env.AWS_REGION }} --query 'MountTargets[0].IpAddress' --output text 2>/dev/null || echo "") + GIT_MIRROR_IP=$(aws efs describe-mount-targets --file-system-id ${{ env.GIT_MIRROR_EFS_ID }} --region ${{ env.AWS_REGION }} --query 'MountTargets[0].IpAddress' --output text 2>/dev/null || echo "") + echo "Shared cache EFS IP: $SHARED_CACHE_IP" + echo "Git mirror EFS IP: $GIT_MIRROR_IP" + + # Mount shared cache EFS (sstate, downloads) with DNS and IP fallback + SHARED_MOUNTED=false + for i in {1..2}; do + echo "Attempt $i: Mounting shared cache EFS..." + # Try DNS first + if sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,intr,timeo=600 \ + ${{ env.SHARED_CACHE_EFS_ID }}.efs.${{ env.AWS_REGION }}.amazonaws.com:/ /mnt/shared-cache 2>/dev/null; then + echo "✓ Shared cache EFS mounted successfully via DNS" + SHARED_MOUNTED=true + break + elif [ -n "$SHARED_CACHE_IP" ]; then + echo "DNS failed, trying IP address: $SHARED_CACHE_IP" + if sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,intr,timeo=600 \ + $SHARED_CACHE_IP:/ /mnt/shared-cache; then + echo "✓ Shared cache EFS mounted successfully via IP" + SHARED_MOUNTED=true + break + fi + fi + echo "✗ Mount attempt $i failed, retrying..." + sleep 5 + done + + # Mount git mirror EFS with DNS and IP fallback + GIT_MOUNTED=false + for i in {1..2}; do + echo "Attempt $i: Mounting git mirror EFS..." + # Try DNS first + if sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,intr,timeo=600 \ + ${{ env.GIT_MIRROR_EFS_ID }}.efs.${{ env.AWS_REGION }}.amazonaws.com:/ /mnt/git-mirror 2>/dev/null; then + echo "✓ Git mirror EFS mounted successfully via DNS" + GIT_MOUNTED=true + break + elif [ -n "$GIT_MIRROR_IP" ]; then + echo "DNS failed, trying IP address: $GIT_MIRROR_IP" + if sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,intr,timeo=600 \ + $GIT_MIRROR_IP:/ /mnt/git-mirror; then + echo "✓ Git mirror EFS mounted successfully via IP" + GIT_MOUNTED=true + break + fi + fi + echo "✗ Mount attempt $i failed, retrying..." + sleep 5 + done + + # Report mount results + if [ "$SHARED_MOUNTED" = "false" ]; then + echo "⚠️ WARNING: Failed to mount shared cache EFS - builds will be slower without cache" + fi + if [ "$GIT_MOUNTED" = "false" ]; then + echo "⚠️ WARNING: Failed to mount git mirror EFS - will clone repositories directly" + fi + + if [ "$SHARED_MOUNTED" = "true" ] && [ "$GIT_MOUNTED" = "true" ]; then + echo "🎉 All EFS file systems mounted successfully!" + fi + + # Verify mounts + echo "=== Mount Verification ===" + mount | grep mnt || echo "No /mnt filesystems found" + df -h | grep mnt || echo "No /mnt filesystems in df" + + # Set permissions on mount points only (not recursive to avoid processing massive cache data) + sudo chown $CURRENT_USER:$CURRENT_USER /mnt/shared-cache /mnt/git-mirror + sudo chmod 755 /mnt/shared-cache /mnt/git-mirror + echo "✓ Mount point permissions set (skipped recursive operation to avoid processing 100GB+ of cache data)" + + # Set up build directories and environment + - name: Setup build environment + run: | + # Set environment variables and get current user + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + CURRENT_USER=$(whoami) + echo "Setting up build environment for user: $CURRENT_USER" + + # Setup environment variables for all shells + echo "export PATH=\$PATH:\$HOME/.local/bin" >> ~/.bashrc + echo "export LANG=en_US.UTF-8" >> ~/.bashrc + echo "export LC_ALL=en_US.UTF-8" >> ~/.bashrc + + # Create and setup the build directory structure + sudo mkdir -p /yocto/${{ inputs.yocto_version }} /yocto/yocto_ccache /yocto/test-images + sudo chown -R $CURRENT_USER:$CURRENT_USER /yocto + sudo chmod -R 755 /yocto/${{ inputs.yocto_version }} /yocto/yocto_ccache /yocto/test-images + + # Clean any previous builds in the NVMe-backed directory + cd /yocto/${{ inputs.yocto_version }} + rm -rf meta-browser meta-chromium-test build/tmp/work/*/*/*/pseudo build/tmp/sysroots-components/*/pseudo 2>/dev/null || true + + # Ensure current directory has proper permissions for symlink creation + sudo chown $CURRENT_USER:$CURRENT_USER . + sudo chmod 755 . + + # Create symlinks to EFS mounts in both build directory and /yocto root + ln -sf /mnt/shared-cache/sstate-cache ./sstate-cache + ln -sf /mnt/shared-cache/downloads ./downloads + ln -sf /mnt/shared-cache/sstate-cache /yocto/yocto_sstate_chromium + ln -sf /mnt/shared-cache/sstate-cache /yocto/yocto_sstate_electron + ln -sf /mnt/shared-cache/downloads /yocto/yocto_dl + + # Clone repositories (check if PR or manual trigger) + - name: Clone repositories + run: | + # Clone directly into the NVMe-backed /yocto directory + cd /yocto/${{ inputs.yocto_version }} + + if [ "${{ github.event_name }}" = "pull_request" ]; then + GH_URL="$GITHUB_SERVER_URL/${{ github.event.pull_request.head.repo.full_name }}" + GH_REV="$GITHUB_HEAD_REF" + else + GH_URL="$GITHUB_SERVER_URL/brightsign/meta-browser" + GH_REV="master" + fi + + echo "Cloning from $GH_URL, branch/ref: $GH_REV" + git clone $GH_URL + git -C meta-browser checkout $GH_REV + + # Clone the test repo + echo "Cloning meta-chromium-test electron-master branch" + git clone https://github.com/brightsign/meta-chromium-test.git + + # Run the build + - name: Build Browser + timeout-minutes: 660 # 11 hours for Yocto build + run: | + # Set environment variables that the Docker container would have + export LANG=en_US.UTF-8 + export LC_ALL=en_US.UTF-8 + export PATH=$PATH:$HOME/.local/bin + + # Debug environment and verify setup + echo "=== Environment Debug ===" + echo "PATH: $PATH" + echo "User: $(whoami)" + echo "Current directory: $(pwd)" + which kas || echo "kas not found in PATH" + + echo "Building ${{ inputs.browser }} with ${{ inputs.chromium_version }}" + + # The build script expects to run from /yocto/master (repositories already cloned here) + cd /yocto/${{ inputs.yocto_version }} + + # Verify repositories are present + if [ -d "meta-browser" ] && [ -d "meta-chromium-test" ]; then + echo "✓ Repositories successfully cloned to /yocto/${{ inputs.yocto_version }}" + ls -la meta-browser/ meta-chromium-test/ + else + echo "✗ ERROR: Repositories not found in /yocto/${{ inputs.yocto_version }}" + ls -la + exit 1 + fi + + # Display cache status (cache directories already set up in previous step) + echo "=== Cache Status ===" + if [ -w /mnt/shared-cache/sstate-cache ]; then + SSTATE_COUNT=$(find /mnt/shared-cache/sstate-cache -name "*.tar.*" | wc -l) + echo "✓ sstate-cache directory accessible, existing files: $SSTATE_COUNT" + else + echo "✗ sstate-cache directory not accessible" + fi + + if [ -w /mnt/shared-cache/downloads ]; then + DOWNLOADS_COUNT=$(find /mnt/shared-cache/downloads -type f | wc -l) + echo "✓ downloads directory accessible, existing files: $DOWNLOADS_COUNT" + else + echo "✗ downloads directory not accessible" + fi + + # Show cache directory sizes + echo "Cache directory sizes:" + du -sh /mnt/shared-cache/sstate-cache 2>/dev/null || echo " sstate-cache: not accessible" + du -sh /mnt/shared-cache/downloads 2>/dev/null || echo " downloads: not accessible" + + echo "Building ${{ inputs.browser }} with ${{ inputs.chromium_version }}" + echo "Current directory: $(pwd)" + echo "Directory contents: $(ls -la)" + echo "Yocto directories:" + ls -la /yocto/ 2>/dev/null || echo "No /yocto directory" + + # Verify the build script exists and is executable + if [ -f "./meta-chromium-test/scripts/build.sh" ]; then + echo "✓ Build script found and ready" + chmod +x ./meta-chromium-test/scripts/build.sh + else + echo "✗ Build script not found!" + find . -name "build.sh" 2>/dev/null || echo "No build.sh found anywhere" + exit 1 + fi + + # Verify cache symlinks (already created in build environment setup) + echo "=== Cache Directory Verification ===" + ls -la /yocto/ | grep -E "(yocto_dl|yocto_sstate)" || echo "Cache symlinks not found" + echo "✓ ccache disabled in kas configuration - relying on sstate cache" + + # Fix test-images directory permissions (build script needs to write here) + echo "=== Fixing test-images directory permissions ===" + CURRENT_USER=$(whoami) + sudo chown -R $CURRENT_USER:$CURRENT_USER /yocto/test-images + sudo chmod 755 /yocto/test-images + echo "test-images directory ownership:" + ls -la /yocto/ | grep test-images + + # Run the build script + echo "=== Starting build on NVMe storage ===" + echo "Available disk space:" + df -h + + ./meta-chromium-test/scripts/build.sh ${{ inputs.yocto_version }} ${{ inputs.arch }} ${{ inputs.chromium_version }} ${{ inputs.browser }} ${{ inputs.libc_flavour }} + + # Post-build cache analysis + echo "=== Post-Build Cache Analysis ===" + DOWNLOADS_COUNT_AFTER=$(find /mnt/shared-cache/downloads -type f 2>/dev/null | wc -l) + SSTATE_COUNT_AFTER=$(find /mnt/shared-cache/sstate-cache -type f 2>/dev/null | wc -l) + echo "Downloads files after build: $DOWNLOADS_COUNT_AFTER" + echo "Sstate cache files after build: $SSTATE_COUNT_AFTER" + echo "Downloads size after build:" + du -sh /mnt/shared-cache/downloads 2>/dev/null || echo "0 bytes" + echo "Sstate cache size after build:" + du -sh /mnt/shared-cache/sstate-cache 2>/dev/null || echo "0 bytes" + + # Analyze BitBake log for cache utilization + echo "=== BitBake Cache Analysis ===" + BUILD_LOG=$(find build/tmp/log -name "*.log" -type f | head -1) + if [ -n "$BUILD_LOG" ] && [ -f "$BUILD_LOG" ]; then + echo "Analyzing build log: $BUILD_LOG" + echo "Sstate cache hits:" + grep -c "sstate.*Found.*valid" "$BUILD_LOG" 2>/dev/null || echo "0" + echo "Sstate cache misses:" + grep -c "sstate.*not.*found" "$BUILD_LOG" 2>/dev/null || echo "0" + echo "Cache restore operations:" + grep -c "restoring.*from sstate" "$BUILD_LOG" 2>/dev/null || echo "0" + else + echo "No build log found for analysis" + fi + + # Show recent sstate cache activity + echo "Most recent sstate cache files (last 10 created):" + find /mnt/shared-cache/sstate-cache -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -10 | while read timestamp file; do + echo "$(date -d @${timestamp%.*} '+%Y-%m-%d %H:%M:%S') $file" + done || echo "No sstate files found" + + # Refresh AWS credentials before upload (they may have expired during long build) + - name: Refresh AWS credentials for artifact upload + if: success() + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ env.AWS_REGION }} + role-to-assume: ${{ inputs.aws_arn_role }} + role-session-name: mb-upload-${{ github.run_id }} + role-duration-seconds: 3600 # 1 hour for upload + + # Upload build artifacts to S3 (if successful) + - name: Upload artifacts + if: success() + run: | + cd /yocto/${{ inputs.yocto_version }} + + # Find and upload build artifacts + DEPLOY_DIR=$(find build -name "deploy" -type d | head -1) + if [ -n "$DEPLOY_DIR" ] && [ -d "$DEPLOY_DIR" ]; then + echo "=== Artifact Upload ===" + echo "Deploy directory: $DEPLOY_DIR" + echo "Checking directory contents:" + ls -la "$DEPLOY_DIR" || echo "Failed to list deploy directory" + + # Check available disk space + echo "Available disk space:" + df -h + + # Calculate upload size + UPLOAD_SIZE=$(du -sh "$DEPLOY_DIR" | cut -f1) + echo "Upload size: $UPLOAD_SIZE" + + # S3 destination path + S3_PATH="s3://${ARTIFACT_BUCKET_NAME}/meta-browser/${{ inputs.yocto_version }}/${{ inputs.arch }}/${{ inputs.chromium_version }}/${{ github.run_id }}/" + echo "S3 destination: $S3_PATH" + echo "Bucket name from env: ${ARTIFACT_BUCKET_NAME}" + + # Verify AWS credentials are working + echo "Verifying AWS credentials..." + if aws sts get-caller-identity; then + echo "✓ AWS credentials verified successfully" + else + echo "✗ AWS credentials verification failed" + exit 1 + fi + + echo "Starting S3 upload..." + # Use quiet mode to avoid excessive logging for large uploads + if aws s3 sync "$DEPLOY_DIR/" "$S3_PATH" --cli-read-timeout 0 --cli-connect-timeout 60 --quiet; then + echo "✓ Artifacts uploaded successfully to $S3_PATH" + + # Verify upload + echo "Verifying upload..." + aws s3 ls "$S3_PATH" --recursive --human-readable --summarize + else + echo "✗ S3 upload failed" + exit 1 + fi + else + echo "No deploy directory found" + echo "Available directories:" + find . -name "deploy" -type d 2>/dev/null || echo "No deploy directories found" + echo "Build directory structure:" + find build -type d -maxdepth 3 2>/dev/null || echo "No build directory found" + fi + + # Unmount EFS file systems + - name: Cleanup EFS mounts + if: always() + run: | + sudo umount /mnt/shared-cache || true + sudo umount /mnt/git-mirror || true diff --git a/.github/workflows/bs_meta_browser_ci_ec2.yml b/.github/workflows/bs_meta_browser_ci_ec2.yml new file mode 100644 index 000000000..33392929d --- /dev/null +++ b/.github/workflows/bs_meta_browser_ci_ec2.yml @@ -0,0 +1,156 @@ +name: 'BrightSign Build and Test Meta-Browser: EC2 controller' +on: + workflow_call: + inputs: + build_type: + description: 'Build Type' + required: true + type: string + + browser: + description: 'Chromium or Electron' + required: true + type: string + + yocto_version: + description: 'Yocto version' + required: true + type: string + + chromium_version: + description: 'Display backend/Ozone platform (ozone-wayland or x11)' + required: true + type: string + + libc_flavour: + description: 'libc flavour' + required: true + type: string + + arch: + description: 'Architecture' + required: true + type: string + + instance_type: + description: 'EC2 instance type' + required: false + type: string + default: c6a.4xlarge + + leave_ec2_instance_running: + description: 'Leave EC2 instance running after use' + type: boolean + default: false + + instance_name_postfix: + description: 'Name to add as postfix to the EC2 machine' + type: string + default: auto-triggered + + aws_arn_role: + required: true + type: string + + aws_region: + required: true + type: string + +jobs: + start-runner: + name: Start self-hosted EC2 runner + runs-on: ubuntu-latest + outputs: + label: ${{ steps.start-ec2-runner.outputs.label }} + ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ inputs.aws_arn_role }} + role-session-name: mb-ci-${{ github.run_id }} + role-duration-seconds: 43200 # 12 hours for long builds + + - name: Download config file and set env vars from it + run: | + aws s3 cp s3://meta-browser-ci-config-bucket/config.json . + aws s3 cp s3://meta-browser-ci-config-bucket/set_github_env_vars.py . + python3 set_github_env_vars.py --file config.json + + - name: Clean up any leftover runners + run: | + echo "Checking for any leftover runners from previous runs..." + # This is informational only - the action will handle unique labels + echo "Current GitHub run context:" + echo "Run ID: ${{ github.run_id }}" + echo "Run attempt: ${{ github.run_attempt }}" + echo "Run number: ${{ github.run_number }}" + + - name: Start EC2 runner with retry + id: start-ec2-runner + uses: brightsign/ec2-github-runner@0fa8b183dd4124fd191ccdbc48b68f0ea46a9634 + timeout-minutes: 15 # Allow more time for registration + with: + mode: start + github-app-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + github-app-id: 287690 + ec2-image-id: ami-08a4255385679596c # Custom AMI with Yocto build dependencies pre-installed + ec2-instance-type: ${{ inputs.instance_type }} + subnet-id: ${{ env.VPC_SUBNET_ID }} + security-group-id: ${{ env.VPC_SG_ID }} + run-as-service-with-user: ubuntu + label: "mb-${{ github.run_id }}-${{ inputs.browser }}-${{ inputs.arch }}-${{ inputs.chromium_version }}" # Unique per matrix job + aws-resource-tags: > # optional, requires additional permissions + [ + {"Key": "Name", "Value": "github-runner-meta-browser-${{ inputs.instance_name_postfix }}-${{ github.run_id }}"}, + {"Key": "GitHubRepository", "Value": "${{ github.repository }}"}, + {"Key": "YoctoVersion", "Value": "${{ inputs.yocto_version }}"}, + {"Key": "ChromiumVersion", "Value": "${{ inputs.chromium_version }}"}, + {"Key": "Architecture", "Value": "${{ inputs.arch }}"}, + {"Key": "RunId", "Value": "${{ github.run_id }}"}, + {"Key": "RunAttempt", "Value": "${{ github.run_attempt }}"}, + {"Key": "Browser", "Value": "${{ inputs.browser }}"} + ] + + build-and-test-meta-browser: + name: Build and Test Meta-Browser + needs: start-runner # required to start the main job when the runner is ready + uses: ./.github/workflows/bs_meta_browser_build_and_test.yml + secrets: inherit + with: + runner_name: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runner + github_hosted_runner: false + browser: ${{ inputs.browser }} + build_type: ${{ inputs.build_type }} + yocto_version: ${{ inputs.yocto_version }} + chromium_version: ${{ inputs.chromium_version }} + libc_flavour: ${{ inputs.libc_flavour }} + arch: ${{ inputs.arch }} + aws_arn_role: ${{ inputs.aws_arn_role }} + aws_region: ${{ inputs.aws_region }} + + stop-runner: + name: Stop self-hosted EC2 runner + needs: + - start-runner # required to get output from the start-runner job + - build-and-test-meta-browser # required to wait when the main job is done + runs-on: ubuntu-latest + if: ${{ always() }} # required to stop the runner even if the error happened in the previous jobs + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ inputs.aws_arn_role }} + aws-region: ${{ inputs.aws_region }} + role-session-name: mb-cleanup-${{ github.run_id }} + + - name: Stop EC2 runner + uses: brightsign/ec2-github-runner@0fa8b183dd4124fd191ccdbc48b68f0ea46a9634 + with: + mode: stop + github-app-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + github-app-id: 287690 + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} + leave-ec2-instance-running: ${{ inputs.leave_ec2_instance_running }} diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 4f5d7899b..9e7436330 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -11,6 +11,21 @@ on: description: 'Branch to checkout for the workflow' required: true default: 'master' + use_aws: + description: 'Use AWS EC2 instances instead of local runner' + required: false + type: boolean + default: true + instance_type: + description: 'EC2 instance type for build (more cores = faster builds)' + required: false + type: choice + options: + - 'c6id.4xlarge' # 16 vCPUs, 32 GB RAM, 1x 950 GB NVMe + - 'c6id.8xlarge' # 32 vCPUs, 64 GB RAM, 1x 1900 GB NVMe + - 'c6id.12xlarge' # 48 vCPUs, 96 GB RAM, 2x 1425 GB NVMe + - 'c6id.16xlarge' # 64 vCPUs, 128 GB RAM, 2x 1900 GB NVMe + default: 'c6id.8xlarge' pull_request: branches: - master @@ -24,11 +39,37 @@ permissions: contents: read actions: read checks: write + id-token: write # Required for OIDC authentication jobs: - build: - if: ${{ github.repository_owner == 'brightsign' }} + # AWS-based builds (always for PR, default for manual dispatch) + aws-matrix-build: + if: ${{ github.repository_owner == 'brightsign' && (github.event_name == 'pull_request' || inputs.use_aws == true || inputs.use_aws == null) }} strategy: + fail-fast: false # Continue other matrix jobs even if one fails + matrix: + yocto_version: [master] + ozone_platform: [ozone-wayland, x11] + libc_flavour: [glibc] + arch: [arm, aarch64, x86-64] + uses: ./.github/workflows/bs_meta_browser_ci_ec2.yml + secrets: inherit + with: + build_type: "release" + browser: "chromium" + yocto_version: ${{ matrix.yocto_version }} + chromium_version: ${{ matrix.ozone_platform }} + libc_flavour: ${{ matrix.libc_flavour }} + arch: ${{ matrix.arch }} + aws_arn_role: "arn:aws:iam::195607249165:role/github-actions-meta-browser-repo" + aws_region: "us-east-1" + instance_type: ${{ inputs.instance_type || 'c6id.8xlarge' }} # Default for PR builds, user choice for manual + + # Local runner (manual dispatch only, when explicitly disabled AWS) + local-build: + if: ${{ github.repository_owner == 'brightsign' && github.event_name == 'workflow_dispatch' && inputs.use_aws == false }} + strategy: + fail-fast: false # Continue other matrix jobs even if one fails matrix: yocto_version: [master] browser_version: [ozone-wayland, x11] @@ -57,5 +98,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone -b electron-master https://github.com/brightsign/meta-chromium-test.git + git clone https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} diff --git a/.github/workflows/cleanup-efs-cache.yml b/.github/workflows/cleanup-efs-cache.yml new file mode 100644 index 000000000..dbcacc891 --- /dev/null +++ b/.github/workflows/cleanup-efs-cache.yml @@ -0,0 +1,169 @@ +name: 'EFS Cache Cleanup' + +on: + workflow_dispatch: + inputs: + cleanup_downloads: + description: 'Clean downloads directory on EFS' + required: true + type: boolean + default: false + + cleanup_sstate: + description: 'Clean sstate-cache directory on EFS' + required: true + type: boolean + default: false + + aws_region: + description: 'AWS Region' + required: true + type: string + default: 'us-east-1' + + instance_type: + description: 'EC2 instance type for cleanup' + required: false + type: string + default: 't3.medium' + +permissions: + contents: read + actions: read + id-token: write + +jobs: + start-runner: + name: Start EC2 Runner + runs-on: ubuntu-latest + outputs: + label: ${{ steps.start-runner.outputs.label }} + ec2-instance-id: ${{ steps.start-runner.outputs.ec2-instance-id }} + steps: + # Configure AWS credentials + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ secrets.AWS_ARN_ROLE }} + + # Start EC2 runner for cleanup + - name: Start EC2 runner + id: start-runner + uses: machulav/ec2-github-runner@v2 + with: + mode: start + github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} + ec2-image-id: ami-0ba9883b710398fbb + ec2-instance-type: ${{ inputs.instance_type }} + subnet-id: ${{ env.VPC_SUBNET_ID }} + security-group-id: ${{ env.VPC_SG_ID }} + iam-role-name: yocto-github-actions-role + aws-resource-tags: > + [ + {"Key": "Name", "Value": "gh-runner-meta-browser-cleanup"}, + {"Key": "GitHubRepository", "Value": "${{ github.repository }}"} + ] + + cleanup-efs: + name: Cleanup EFS Cache + needs: start-runner + runs-on: ${{ needs.start-runner.outputs.label }} + steps: + # Configure AWS credentials on the EC2 runner + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ secrets.AWS_ARN_ROLE }} + + # Download config and get EFS ID + - name: Download config file and set env vars from it + run: | + aws s3 cp s3://meta-browser-ci-config-bucket/config.json . + aws s3 cp s3://meta-browser-ci-config-bucket/set_github_env_vars.py . + python3 set_github_env_vars.py --file config.json + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y nfs-common + + - name: Mount EFS and perform cleanup + run: | + # Create mount point + sudo mkdir -p /mnt/shared-cache + + # Mount EFS using environment variable from config + echo "Mounting EFS: ${{ env.SHARED_CACHE_EFS_ID }}" + sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,intr,timeo=600 \ + ${{ env.SHARED_CACHE_EFS_ID }}.efs.${{ inputs.aws_region }}.amazonaws.com:/ /mnt/shared-cache + + # Show current disk usage + echo "=== Current EFS disk usage ===" + df -h /mnt/shared-cache + echo "" + echo "=== Directory sizes before cleanup ===" + sudo du -sh /mnt/shared-cache/* 2>/dev/null || echo "No directories found" + echo "" + + # Cleanup downloads if requested + if [ "${{ inputs.cleanup_downloads }}" = "true" ]; then + echo "=== Cleaning yocto_downloads ===" + if [ -d "/mnt/shared-cache/downloads" ]; then + echo "Removing downloads directory..." + sudo rm -rf /mnt/shared-cache/downloads/* + echo "Downloads directory cleaned" + else + echo "Downloads directory not found" + fi + else + echo "Skipping downloads cleanup" + fi + + # Cleanup sstate-cache if requested + if [ "${{ inputs.cleanup_sstate }}" = "true" ]; then + echo "=== Cleaning sstate-cache ===" + if [ -d "/mnt/shared-cache/sstate-cache" ]; then + echo "Removing sstate-cache directory..." + sudo rm -rf /mnt/shared-cache/sstate-cache/* + echo "sstate-cache directory cleaned" + else + echo "sstate-cache directory not found" + fi + else + echo "Skipping sstate-cache cleanup" + fi + + # Show final disk usage + echo "" + echo "=== Directory sizes after cleanup ===" + sudo du -sh /mnt/shared-cache/* 2>/dev/null || echo "No directories found" + echo "" + echo "=== Final EFS disk usage ===" + df -h /mnt/shared-cache + + # Unmount EFS + sudo umount /mnt/shared-cache + + stop-runner: + name: Stop EC2 Runner + needs: + - start-runner + - cleanup-efs + runs-on: ubuntu-latest + if: always() + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ secrets.AWS_ARN_ROLE }} + + - name: Stop EC2 runner + uses: machulav/ec2-github-runner@v2 + with: + mode: stop + github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index ae174c85b..90f8c8311 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -11,6 +11,21 @@ on: description: 'Branch to checkout for the workflow' required: true default: 'master' + use_aws: + description: 'Use AWS EC2 instances instead of local runner' + required: false + type: boolean + default: true + instance_type: + description: 'EC2 instance type for build (more cores = faster builds)' + required: false + type: choice + options: + - 'c6id.4xlarge' # 16 vCPUs, 32 GB RAM, 1x 950 GB NVMe + - 'c6id.8xlarge' # 32 vCPUs, 64 GB RAM, 1x 1900 GB NVMe + - 'c6id.12xlarge' # 48 vCPUs, 96 GB RAM, 2x 1425 GB NVMe + - 'c6id.16xlarge' # 64 vCPUs, 128 GB RAM, 2x 1900 GB NVMe + default: 'c6id.8xlarge' pull_request: branches: - master @@ -24,11 +39,37 @@ permissions: contents: read actions: read checks: write + id-token: write # Required for OIDC authentication jobs: - build: - if: ${{ github.repository_owner == 'brightsign' }} + # AWS-based builds (always for PR, default for manual dispatch) + aws-matrix-build: + if: ${{ github.repository_owner == 'brightsign' && (github.event_name == 'pull_request' || inputs.use_aws == true || inputs.use_aws == null) }} strategy: + fail-fast: false # Continue other matrix jobs even if one fails + matrix: + yocto_version: [master] + ozone_platform: [ozone-wayland, ozone-x11] + libc_flavour: [glibc] + arch: [arm, aarch64, x86-64] + uses: ./.github/workflows/bs_meta_browser_ci_ec2.yml + secrets: inherit + with: + build_type: "release" + browser: "electron" + yocto_version: ${{ matrix.yocto_version }} + chromium_version: ${{ matrix.ozone_platform }} + libc_flavour: ${{ matrix.libc_flavour }} + arch: ${{ matrix.arch }} + aws_arn_role: "arn:aws:iam::195607249165:role/github-actions-meta-browser-repo" + aws_region: "us-east-1" + instance_type: ${{ github.event.inputs.instance_type || 'c6id.8xlarge' }} + + # Local runner (manual dispatch only, when explicitly disabled AWS) + local-build: + if: ${{ github.repository_owner == 'brightsign' && github.event_name == 'workflow_dispatch' && inputs.use_aws == false }} + strategy: + fail-fast: false # Continue other matrix jobs even if one fails matrix: yocto_version: [master] browser_version: [ozone-wayland, ozone-x11] @@ -57,5 +98,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone -b electron-master https://github.com/brightsign/meta-chromium-test.git + git clone https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} From 0e6d4e374b753b74dd821a281196deb9a2a0bd80 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 2 Sep 2025 18:01:51 +0100 Subject: [PATCH 12/15] fix: Replace UNPACKDIR with WORKDIR scarthgap doesn't define UNPACKDIR, use WORKDIR instead. --- meta-chromium/recipes-browser/chromium/chromium.inc | 2 +- meta-chromium/recipes-browser/chromium/electron.inc | 2 +- .../recipes-browser/chromium/gn-native_138.0.7204.157.bb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meta-chromium/recipes-browser/chromium/chromium.inc b/meta-chromium/recipes-browser/chromium/chromium.inc index db9e29d1d..db79f56cb 100644 --- a/meta-chromium/recipes-browser/chromium/chromium.inc +++ b/meta-chromium/recipes-browser/chromium/chromium.inc @@ -5,7 +5,7 @@ CVE_PRODUCT = "chromium:chromium google:chrome" SRC_URI = "https://commondatastorage.googleapis.com/chromium-browser-official/chromium-${PV}.tar.xz" SRC_URI[sha256sum] = "835dfd3228f6adb2a8c78e296c7d4981aee35daee401a2fe1493b4657d736d25" -S = "${UNPACKDIR}/chromium-${PV}" +S = "${WORKDIR}/chromium-${PV}" # GCC is not tested or officially supported upstream, and supporting it here # requires an ever-growing amount of backports and custom patches, without any diff --git a/meta-chromium/recipes-browser/chromium/electron.inc b/meta-chromium/recipes-browser/chromium/electron.inc index 56da87f63..4bbc3d546 100644 --- a/meta-chromium/recipes-browser/chromium/electron.inc +++ b/meta-chromium/recipes-browser/chromium/electron.inc @@ -14,7 +14,7 @@ PV = "37.1.0-bs.alpha2" # Electron-specific source - replace Chromium source with Electron source SRC_URI = "https://electron-ci-public-artifact-bucket.s3.amazonaws.com/v${PV}/v${PV}.tar.xz" SRC_URI[sha256sum] = "4921ad086f4c7be4374cd6b1e18a22c0f90a58c6b2253b6e90f16ae88c9cfb12" -S = "${UNPACKDIR}/src" +S = "${WORKDIR}/src" SRC_URI += " \ file://0001-electron-fix-js2c-toolchain-for-Yocto-cross-compilat.patch \ diff --git a/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb b/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb index 674283371..5a3225be9 100644 --- a/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb +++ b/meta-chromium/recipes-browser/chromium/gn-native_138.0.7204.157.bb @@ -6,7 +6,7 @@ require chromium.inc inherit native -S = "${UNPACKDIR}/chromium-${PV}" +S = "${WORKDIR}/chromium-${PV}" # bootstrap.py --no_clean hardcodes the build location to out_bootstrap. # Omitting --no_clean causes the script to create a temporary directory with a From 198ad41cec0c8bc7ab673c11e10cf6e5b1772b9f Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 2 Sep 2025 18:03:55 +0100 Subject: [PATCH 13/15] fix: Add recipe for nodejs v22.11.0 Chromium138/Electron37 requires Node.js v22.11.0. Add a new recipe to use updated version. --- .../nodejs/nodejs-oe-cache-22.11/oe-npm-cache | 77 +++++++ .../nodejs/nodejs-oe-cache-native_22.11.bb | 24 ++ ...e-running-gyp-files-for-bundled-deps.patch | 47 ++++ .../nodejs/0001-Using-native-binaries.patch | 94 ++++++++ .../nodejs/nodejs/0001-custom-env.patch | 18 ++ ...ps-disable-io_uring-support-in-libuv.patch | 52 +++++ ...-liftoff-Correct-function-signatures.patch | 69 ++++++ .../nodejs/nodejs/0001-positional-args.patch | 19 ++ ...4-Do-not-use-mminimal-toc-with-clang.patch | 25 ++ .../0004-v8-don-t-override-ARM-CFLAGS.patch | 99 ++++++++ .../nodejs/nodejs/libatomic.patch | 77 +++++++ .../recipes-devtools/nodejs/nodejs/run-ptest | 8 + .../nodejs/nodejs/system-c-ares.patch | 32 +++ .../recipes-devtools/nodejs/nodejs_22.11.0.bb | 214 ++++++++++++++++++ 14 files changed, 855 insertions(+) create mode 100755 meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-22.11/oe-npm-cache create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-native_22.11.bb create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-Disable-running-gyp-files-for-bundled-deps.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-Using-native-binaries.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-custom-env.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-liftoff-Correct-function-signatures.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-positional-args.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0001-ppc64-Do-not-use-mminimal-toc-with-clang.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/0004-v8-don-t-override-ARM-CFLAGS.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/libatomic.patch create mode 100755 meta-chromium/recipes-devtools/nodejs/nodejs/run-ptest create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs/system-c-ares.patch create mode 100644 meta-chromium/recipes-devtools/nodejs/nodejs_22.11.0.bb diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-22.11/oe-npm-cache b/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-22.11/oe-npm-cache new file mode 100755 index 000000000..eb0f143ea --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-22.11/oe-npm-cache @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +/// Usage: oe-npm-cache +/// ... meta - metainformation about package +/// tgz - tarball + +const process = require("node:process"); + +module.paths.unshift("@@libdir@@/node_modules/npm/node_modules"); + +const cacache = require('cacache') +const fs = require('fs') + +// argv[0] is 'node', argv[1] is this script +const cache_dir = process.argv[2] +const type = process.argv[3] +const key = process.argv[4] +const file = process.argv[5] + +const data = fs.readFileSync(file) + +// metadata content is highly nodejs dependent; when cache entries are not +// found, place debug statements in 'make-fetch-happen/lib/cache/policy.js' +// (CachePolicy::satisfies()) +const xlate = { + 'meta': { + 'key_prefix': 'make-fetch-happen:request-cache:', + 'metadata': function() { + return { + time: Date.now(), + url: key, + reqHeaders: { + 'accept': 'application/json', + }, + resHeaders: { + "content-type": "application/json", + "status": 200, + }, + options: { + compress: true, + } + }; + }, + }, + + 'tgz': { + 'key_prefix': 'make-fetch-happen:request-cache:', + 'metadata': function() { + return { + time: Date.now(), + url: key, + reqHeaders: { + 'accept': '*/*', + }, + resHeaders: { + "content-type": "application/octet-stream", + "status": 200, + }, + options: { + compress: true, + }, + }; + }, + }, +}; + +const info = xlate[type]; +let opts = {} + +if (info.metadata) { + opts['metadata'] = info.metadata(); +} + +cacache.put(cache_dir, info.key_prefix + key, data, opts) + .then(integrity => { + console.log(`Saved content of ${key} (${file}).`); +}) diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-native_22.11.bb b/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-native_22.11.bb new file mode 100644 index 000000000..d4b818f96 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs-oe-cache-native_22.11.bb @@ -0,0 +1,24 @@ +DESCRIPTION = "OE helper for manipulating npm cache" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10" + +SRC_URI = "\ + file://oe-npm-cache \ +" + +inherit native + +S = "${WORKDIR}/sources" +UNPACKDIR = "${S}" + +B = "${WORKDIR}/build" + +do_configure() { + sed -e 's!@@libdir@@!${libdir}!g' < '${UNPACKDIR}/oe-npm-cache' > '${B}/oe-npm-cache' +} + +do_install() { + install -D -p -m 0755 ${B}/oe-npm-cache ${D}${bindir}/oe-npm-cache +} + +RDEPENDS:${PN} = "nodejs-native" diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Disable-running-gyp-files-for-bundled-deps.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Disable-running-gyp-files-for-bundled-deps.patch new file mode 100644 index 000000000..f692eedd4 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Disable-running-gyp-files-for-bundled-deps.patch @@ -0,0 +1,47 @@ +From 689e098cbde130ecde523ae39df3567456271fda Mon Sep 17 00:00:00 2001 +From: Zuzana Svetlikova +Date: Thu, 27 Apr 2017 14:25:42 +0200 +Subject: [PATCH] Disable running gyp on shared deps + +Upstream-Status: Inappropriate [embedded specific] + +Probably imported from: +https://src.fedoraproject.org/rpms/nodejs/c/41af04f2a3c050fb44628e91ac65fd225b927acb?branch=22609d8c1bfeaa21fe0057645af20b3a2ccc7f53 +which is probably based on dont-run-gyp-files-for-bundled-deps.patch added in: +https://github.com/alpinelinux/aports/commit/6662eb3199902e8451fb20dce82554ad96f796bb + +We also explicitly prune some dependencies from source in the bitbake recipe: + +python prune_sources() { + import shutil + + shutil.rmtree(d.getVar('S') + '/deps/openssl') + if 'ares' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/cares') + if 'brotli' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/brotli') + if 'libuv' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/uv') + if 'nghttp2' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/nghttp2') + if 'zlib' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/zlib') +} +do_unpack[postfuncs] += "prune_sources" +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index dba16e5e..da4faffc 100644 +--- a/Makefile ++++ b/Makefile +@@ -173,7 +173,7 @@ with-code-cache test-code-cache: + $(warning '$@' target is a noop) + + out/Makefile: config.gypi common.gypi common_node.gypi node.gyp \ +- deps/uv/uv.gyp deps/llhttp/llhttp.gyp deps/zlib/zlib.gyp \ ++ deps/llhttp/llhttp.gyp \ + deps/simdutf/simdutf.gyp deps/ada/ada.gyp deps/nbytes/nbytes.gyp \ + tools/v8_gypfiles/toolchain.gypi \ + tools/v8_gypfiles/features.gypi \ diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Using-native-binaries.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Using-native-binaries.patch new file mode 100644 index 000000000..0178cec77 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-Using-native-binaries.patch @@ -0,0 +1,94 @@ +From 6c3ac20477a4bac643088f24df3c042e627fafa9 Mon Sep 17 00:00:00 2001 +From: Guillaume Burel +Date: Fri, 3 Jan 2020 11:25:54 +0100 +Subject: [PATCH] Using native binaries + +Upstream-Status: Inappropriate [embedded specific] + +Originally added in: +https://git.openembedded.org/meta-openembedded/commit/?id=1c8e4a679ae382f953b2e5c7a4966a4646314f3e +later extended and renamed in: +https://git.openembedded.org/meta-openembedded/commit/?id=feeb172d1a8bf010490d22b8df9448b20d9d2aed + +Signed-off-by: Archana Polampalli +--- + node.gyp | 3 + + tools/v8_gypfiles/v8.gyp | 5 +++++ + 2 files changed, 6 insertions(+) + +diff --git a/node.gyp b/node.gyp +index e8e1d9f9..e60ccc10 100644 +--- a/node.gyp ++++ b/node.gyp +@@ -320,6 +320,7 @@ + 'action_name': 'node_mksnapshot', + 'process_outputs_as_sources': 1, + 'inputs': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(node_mksnapshot_exec)', + '<(node_snapshot_main)', + ], +@@ -935,6 +935,7 @@ + 'action_name': 'node_js2c', + 'process_outputs_as_sources': 1, + 'inputs': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(node_js2c_exec)', + '<@(library_files)', + '<@(deps_files)', +@@ -944,6 +945,7 @@ + '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc', + ], + 'action': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(node_js2c_exec)', + '<@(_outputs)', + 'lib', + +diff --git a/tools/v8_gypfiles/v8.gyp b/tools/v8_gypfiles/v8.gyp +index 42e26cd9..bc721991 100644 +--- a/tools/v8_gypfiles/v8.gyp ++++ b/tools/v8_gypfiles/v8.gyp +@@ -68,6 +68,7 @@ + { + 'action_name': 'run_torque_action', + 'inputs': [ # Order matters. ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)torque<(EXECUTABLE_SUFFIX)', + '<@(torque_files)', + ], +@@ -99,6 +100,7 @@ + '<@(torque_outputs_inc)', + ], + 'action': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)torque<(EXECUTABLE_SUFFIX)', + '-o', '<(SHARED_INTERMEDIATE_DIR)/torque-generated', + '-v8-root', '<(V8_ROOT)', +@@ -211,6 +213,7 @@ + { + 'action_name': 'generate_bytecode_builtins_list_action', + 'inputs': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)bytecode_builtins_list_generator<(EXECUTABLE_SUFFIX)', + ], + 'outputs': [ +@@ -400,6 +403,7 @@ + ], + }, + 'inputs': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(mksnapshot_exec)', + ], + 'outputs': [ +@@ -1539,6 +1543,7 @@ + { + 'action_name': 'run_gen-regexp-special-case_action', + 'inputs': [ ++ '<(PRODUCT_DIR)/v8-qemu-wrapper.sh', + '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)gen-regexp-special-case<(EXECUTABLE_SUFFIX)', + ], + 'outputs': [ +-- +2.34.1 + diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-custom-env.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-custom-env.patch new file mode 100644 index 000000000..532d3c5f5 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-custom-env.patch @@ -0,0 +1,18 @@ +From 9c1a31afdcf368f794b9f5378cb3fe759570f905 Mon Sep 17 00:00:00 2001 +From: Jason Schonberg +Date: Tue, 30 Apr 2024 21:48:33 -0400 +Subject: [PATCH] Update to nodejs 22.0.0 + +Upstream-Status: Inappropriate [embedded specific] +--- + test/fixtures/run-script/node_modules/.bin/custom-env | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/fixtures/run-script/node_modules/.bin/custom-env b/test/fixtures/run-script/node_modules/.bin/custom-env +index e6f291c6..1430f2e9 100755 +--- a/test/fixtures/run-script/node_modules/.bin/custom-env ++++ b/test/fixtures/run-script/node_modules/.bin/custom-env +@@ -1,2 +1,2 @@ +-#!/bin/bash ++#!/bin/sh + echo "$CUSTOM_ENV" diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch new file mode 100644 index 000000000..04398ac68 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch @@ -0,0 +1,52 @@ +From 2bb296f169f86dbb04ee47e9a0dc1e3ee13d4f73 Mon Sep 17 00:00:00 2001 +From: Jason Schonberg +Date: Thu, 7 Mar 2024 12:55:56 -0500 +Subject: [PATCH] Update to nodejs 20.11.1 + +Upstream-Status: Inappropriate [embedded specific] +--- + ...ps-disable-io_uring-support-in-libuv.patch | 35 +++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 meta-oe/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch + +diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch b/meta-oe/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch +new file mode 100644 +index 00000000..5ac711fb +--- /dev/null ++++ b/meta-oe/recipes-devtools/nodejs/nodejs/0001-deps-disable-io_uring-support-in-libuv.patch +@@ -0,0 +1,35 @@ ++From 9838be9c710ab4249df86726fa390232a3b6a6e7 Mon Sep 17 00:00:00 2001 ++From: Changqing Li ++Date: Fri, 1 Mar 2024 15:46:11 +0800 ++Subject: [PATCH] deps: disable io_uring support in libuv ++ ++Refer [1], Pseudo fails to intercept some of the syscalls when io_uring ++enabled. Refer [2], always disable io_uring support in libuv to fix ++issue in [1]. ++ ++[1] https://git.openembedded.org/meta-openembedded/commit/?id=d08453978c31ee41d28206c6ff198d7d9d701d88 ++[2] https://github.com/nodejs/node/commit/686da19abb ++ ++Upstream-Status: Inappropriate [oe-specific] ++ ++Signed-off-by: Changqing Li ++--- ++ deps/uv/src/unix/linux.c | 2 +- ++ 1 file changed, 1 insertion(+), 1 deletion(-) ++ ++diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c ++index 0c997185..7508409d 100644 ++--- a/deps/uv/src/unix/linux.c +++++ b/deps/uv/src/unix/linux.c ++@@ -433,7 +433,7 @@ static int uv__use_io_uring(void) { ++ if (use == 0) { ++ /* Disable io_uring by default due to CVE-2024-22017. */ ++ val = getenv("UV_USE_IO_URING"); ++- use = val != NULL && atoi(val) ? 1 : -1; +++ use = 0; ++ atomic_store_explicit(&use_io_uring, use, memory_order_relaxed); ++ } ++ ++-- ++2.25.1 ++ diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-liftoff-Correct-function-signatures.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-liftoff-Correct-function-signatures.patch new file mode 100644 index 000000000..5e617e655 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-liftoff-Correct-function-signatures.patch @@ -0,0 +1,69 @@ +From 33393507ba8209f0d6b85b391c525b4c70807275 Mon Sep 17 00:00:00 2001 +From: Khem Raj +Date: Mon, 3 Jul 2023 12:33:16 +0000 +Subject: [PATCH] Correct function signatures + +Fixes builds on mips where clang reports an error +../deps/v8/src/wasm/baseline/mips/liftoff-assembler-mips.h:661:5: error: no matching member function for call to 'Move' + Move(tmp, src, type.value_type()); + ^~~~ + +Upstream-Status: Submitted [https://chromium-review.googlesource.com/c/v8/v8/+/3235674] + +Signed-off-by: Archana Polampalli +--- + deps/v8/src/wasm/baseline/liftoff-assembler.h | 6 +++--- + .../src/wasm/baseline/mips64/liftoff-assembler-mips64-inl.h | 2 +- + .../src/wasm/baseline/riscv/liftoff-assembler-riscv64-inl.h | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/deps/v8/src/wasm/baseline/liftoff-assembler.h b/deps/v8/src/wasm/baseline/liftoff-assembler.h +index 7cb2f500..713d16db 100644 +--- a/deps/v8/src/wasm/baseline/liftoff-assembler.h ++++ b/deps/v8/src/wasm/baseline/liftoff-assembler.h +@@ -681,7 +681,7 @@ class LiftoffAssembler : public MacroAssembler { + void FinishCall(const ValueKindSig*, compiler::CallDescriptor*); + + // Move {src} into {dst}. {src} and {dst} must be different. +- void Move(LiftoffRegister dst, LiftoffRegister src, ValueKind); ++ void Move(LiftoffRegister dst, LiftoffRegister src, ValueKind kind); + + // Parallel register move: For a list of tuples , move the + // {src} register of kind {kind} into {dst}. If {src} equals {dst}, ignore +@@ -851,8 +851,8 @@ class LiftoffAssembler : public MacroAssembler { + inline void MoveStackValue(uint32_t dst_offset, uint32_t src_offset, + ValueKind); + +- inline void Move(Register dst, Register src, ValueKind); +- inline void Move(DoubleRegister dst, DoubleRegister src, ValueKind); ++ inline void Move(Register dst, Register src, ValueKind kind); ++ inline void Move(DoubleRegister dst, DoubleRegister src, ValueKind kind); + + inline void Spill(int offset, LiftoffRegister, ValueKind); + inline void Spill(int offset, WasmValue); +diff --git a/deps/v8/src/wasm/baseline/mips64/liftoff-assembler-mips64-inl.h b/deps/v8/src/wasm/baseline/mips64/liftoff-assembler-mips64-inl.h +index bd59f162..56b4d70c 100644 +--- a/deps/v8/src/wasm/baseline/mips64/liftoff-assembler-mips64-inl.h ++++ b/deps/v8/src/wasm/baseline/mips64/liftoff-assembler-mips64-inl.h +@@ -672,7 +672,7 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, + pinned.set(dst_op.rm()); + LiftoffRegister tmp = kScratchReg2; + // Save original value. +- Move(tmp, src, type.value_type()); ++ Move(tmp, src, type.value_type().kind()); + + src = tmp; + pinned.set(tmp); +diff --git a/deps/v8/src/wasm/baseline/riscv/liftoff-assembler-riscv64-inl.h b/deps/v8/src/wasm/baseline/riscv/liftoff-assembler-riscv64-inl.h +index a3c94af0..456e5334 100644 +--- a/deps/v8/src/wasm/baseline/riscv/liftoff-assembler-riscv64-inl.h ++++ b/deps/v8/src/wasm/baseline/riscv/liftoff-assembler-riscv64-inl.h +@@ -452,7 +452,7 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, + pinned.set(dst_op.rm()); + LiftoffRegister tmp = GetUnusedRegister(src.reg_class(), pinned); + // Save original value. +- Move(tmp, src, type.value_type()); ++ Move(tmp, src, type.value_type().kind()); + + src = tmp; + pinned.set(tmp); diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-positional-args.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-positional-args.patch new file mode 100644 index 000000000..5fd6aee35 --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-positional-args.patch @@ -0,0 +1,19 @@ +From 07ee84863fa4a9e4d5f155632478587b0acbf71a Mon Sep 17 00:00:00 2001 +From: Jason Schonberg +Date: Tue, 30 Apr 2024 21:48:33 -0400 +Subject: [PATCH] Update to nodejs 22.0.0 + +Upstream-Status: Inappropriate [embedded specific] +--- + test/fixtures/run-script/node_modules/.bin/positional-args | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/fixtures/run-script/node_modules/.bin/positional-args b/test/fixtures/run-script/node_modules/.bin/positional-args +index 2d809237..3dc5314f 100755 +--- a/test/fixtures/run-script/node_modules/.bin/positional-args ++++ b/test/fixtures/run-script/node_modules/.bin/positional-args +@@ -1,3 +1,3 @@ +-#!/bin/bash ++#!/bin/sh + echo "Arguments: '$@'" + echo "The total number of arguments are: $#" diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0001-ppc64-Do-not-use-mminimal-toc-with-clang.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-ppc64-Do-not-use-mminimal-toc-with-clang.patch new file mode 100644 index 000000000..dd9c9015e --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0001-ppc64-Do-not-use-mminimal-toc-with-clang.patch @@ -0,0 +1,25 @@ +From 0976af0f3b328436ea44a74a406f311adb2ab211 Mon Sep 17 00:00:00 2001 +From: Khem Raj +Date: Tue, 15 Jun 2021 19:01:31 -0700 +Subject: [PATCH] ppc64: Do not use -mminimal-toc with clang + +clang does not support this option + +Signed-off-by: Khem Raj +--- +Upstream-Status: Pending + + common.gypi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/common.gypi ++++ b/common.gypi +@@ -417,7 +417,7 @@ + 'ldflags': [ '-m32' ], + }], + [ 'target_arch=="ppc64" and OS!="aix"', { +- 'cflags': [ '-m64', '-mminimal-toc' ], ++ 'cflags': [ '-m64' ], + 'ldflags': [ '-m64' ], + }], + [ 'target_arch=="s390x"', { diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/0004-v8-don-t-override-ARM-CFLAGS.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/0004-v8-don-t-override-ARM-CFLAGS.patch new file mode 100644 index 000000000..cc920118a --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/0004-v8-don-t-override-ARM-CFLAGS.patch @@ -0,0 +1,99 @@ +From afc085af7b6b935a5e14fc3f40db47df02ca3af2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Draszik?= +Date: Sat, 9 Nov 2019 14:45:30 +0000 +Subject: [PATCH] v8: don't override ARM CFLAGS +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This overrides yocto-provided build flags with its own, e.g we get + arm-poky-linux-musleabi-g++ -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7 \ + ... \ + -march=armv7-a -mfpu=neon -mfloat-abi=hard -marm + +Causing the latter to override the former, and compiler warnings: + cc1plus: warning: switch '-mcpu=cortex-a7' conflicts with '-march=armv7-a' switch + +Patch this out, so that yocto-provided flags take precedence. +Note that in reality the same should probably be done for all the other +supported architectures, too. + +Note that this also switches to Thumb(2) mode (in my case). No obvious +problems have been noted during compilation or runtime. + +Upstream-Status: Inappropriate [oe-specific] +Signed-off-by: André Draszik +--- + tools/v8_gypfiles/toolchain.gypi | 52 ++------------------------------ + 1 file changed, 2 insertions(+), 50 deletions(-) + +diff --git a/tools/v8_gypfiles/toolchain.gypi b/tools/v8_gypfiles/toolchain.gypi +index 9d1b0987..4df15e60 100644 +--- a/tools/v8_gypfiles/toolchain.gypi ++++ b/tools/v8_gypfiles/toolchain.gypi +@@ -203,31 +203,7 @@ + 'target_conditions': [ + ['_toolset=="host"', { + 'conditions': [ +- ['v8_target_arch==host_arch', { +- # Host built with an Arm CXX compiler. +- 'conditions': [ +- [ 'arm_version==7', { +- 'cflags': ['-march=armv7-a',], +- }], +- [ 'arm_version==7 or arm_version=="default"', { +- 'conditions': [ +- [ 'arm_fpu!="default"', { +- 'cflags': ['-mfpu=<(arm_fpu)',], +- }], +- ], +- }], +- [ 'arm_float_abi!="default"', { +- 'cflags': ['-mfloat-abi=<(arm_float_abi)',], +- }], +- [ 'arm_thumb==1', { +- 'cflags': ['-mthumb',], +- }], +- [ 'arm_thumb==0', { +- 'cflags': ['-marm',], +- }], +- ], +- }, { +- # 'v8_target_arch!=host_arch' ++ ['v8_target_arch!=host_arch', { + # Host not built with an Arm CXX compiler (simulator build). + 'conditions': [ + [ 'arm_float_abi=="hard"', { +@@ -246,31 +222,7 @@ + }], # _toolset=="host" + ['_toolset=="target"', { + 'conditions': [ +- ['v8_target_arch==target_arch', { +- # Target built with an Arm CXX compiler. +- 'conditions': [ +- [ 'arm_version==7', { +- 'cflags': ['-march=armv7-a',], +- }], +- [ 'arm_version==7 or arm_version=="default"', { +- 'conditions': [ +- [ 'arm_fpu!="default"', { +- 'cflags': ['-mfpu=<(arm_fpu)',], +- }], +- ], +- }], +- [ 'arm_float_abi!="default"', { +- 'cflags': ['-mfloat-abi=<(arm_float_abi)',], +- }], +- [ 'arm_thumb==1', { +- 'cflags': ['-mthumb',], +- }], +- [ 'arm_thumb==0', { +- 'cflags': ['-marm',], +- }], +- ], +- }, { +- # 'v8_target_arch!=target_arch' ++ ['v8_target_arch!=target_arch', { + # Target not built with an Arm CXX compiler (simulator build). + 'conditions': [ + [ 'arm_float_abi=="hard"', { diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs/libatomic.patch b/meta-chromium/recipes-devtools/nodejs/nodejs/libatomic.patch new file mode 100644 index 000000000..d987ac50b --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs/libatomic.patch @@ -0,0 +1,77 @@ +From 15e751e4b79475fb34e4b32a3ca54119b20c564a Mon Sep 17 00:00:00 2001 +From: Hongxu Jia +Date: Sat, 17 Aug 2024 21:33:18 +0800 +Subject: [PATCH] link libatomic for clang conditionally + +Clang emits atomic builtin, explicitly link libatomic conditionally: +- For target build, always link -latomic for clang as usual +- For host build, if host and target have same bit width, cross compiling + is enabled, and host toolchain is gcc which does not link -latomic; + if host and target have different bit width, no cross compiling, + host build is the same with target build that requires to link + -latomic; + +Fix: +|tmp-glibc/work/core2-64-wrs-linux/nodejs/20.13.0/node-v20.13.0/out/Release/node_js2c: error while loading shared libraries: libatomic.so.1: cannot open shared object file: No such file or directory + +Upstream-Status: Inappropriate [OE specific] + +Signed-off-by: Hongxu Jia +--- + node.gyp | 13 ++++++++++++- + tools/v8_gypfiles/v8.gyp | 15 ++++++++++++--- + 2 files changed, 24 insertions(+), 4 deletions(-) + +diff --git a/node.gyp b/node.gyp +index b425f443..f296f35c 100644 +--- a/node.gyp ++++ b/node.gyp +@@ -487,7 +487,18 @@ + ], + }], + ['OS=="linux" and clang==1', { +- 'libraries': ['-latomic'], ++ 'target_conditions': [ ++ ['_toolset=="host"', { ++ 'conditions': [ ++ ['" +Date: Mon, 4 Mar 2024 11:05:25 -0500 +Subject: [PATCH] keep nodejs compatible with c-ares 1.17.1 + +Upstream-Status: Inappropriate [c-ares specific] +Signed-off-by: Khem Raj +--- + src/cares_wrap.h | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/cares_wrap.h b/src/cares_wrap.h +index 021ef1c9..820c5d88 100644 +--- a/src/cares_wrap.h ++++ b/src/cares_wrap.h +@@ -23,7 +23,15 @@ + # include + #endif // __POSIX__ + +-# include ++#if defined(__ANDROID__) || \ ++ defined(__MINGW32__) || \ ++ defined(__OpenBSD__) || \ ++ defined(_MSC_VER) ++ ++# include ++#else ++# include ++#endif + + namespace node { + namespace cares_wrap { diff --git a/meta-chromium/recipes-devtools/nodejs/nodejs_22.11.0.bb b/meta-chromium/recipes-devtools/nodejs/nodejs_22.11.0.bb new file mode 100644 index 000000000..49a5f350d --- /dev/null +++ b/meta-chromium/recipes-devtools/nodejs/nodejs_22.11.0.bb @@ -0,0 +1,214 @@ +DESCRIPTION = "nodeJS Evented I/O for V8 JavaScript" +HOMEPAGE = "http://nodejs.org" +LICENSE = "MIT & ISC & BSD-2-Clause & BSD-3-Clause & Artistic-2.0 & Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=25e89142a2f4b075904a9986c45fbdb2" + +CVE_PRODUCT = "nodejs node.js" + +DEPENDS = "openssl openssl-native file-replacement-native python3-packaging-native" +DEPENDS:append:class-target = " qemu-native" +DEPENDS:append:class-native = " c-ares-native" + +inherit pkgconfig python3native qemu ptest siteinfo + +COMPATIBLE_MACHINE:armv4 = "(!.*armv4).*" +COMPATIBLE_MACHINE:armv5 = "(!.*armv5).*" +COMPATIBLE_MACHINE:mips64 = "(!.*mips64).*" + +COMPATIBLE_HOST:riscv64 = "null" +COMPATIBLE_HOST:riscv32 = "null" +COMPATIBLE_HOST:powerpc = "null" + +SRC_URI = "http://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz \ + file://0001-Disable-running-gyp-files-for-bundled-deps.patch \ + file://0004-v8-don-t-override-ARM-CFLAGS.patch \ + file://system-c-ares.patch \ + file://0001-liftoff-Correct-function-signatures.patch \ + file://libatomic.patch \ + file://0001-deps-disable-io_uring-support-in-libuv.patch \ + file://0001-positional-args.patch \ + file://0001-custom-env.patch \ + file://run-ptest \ + " +SRC_URI:append:class-target = " \ + file://0001-Using-native-binaries.patch \ + " +SRC_URI:append:toolchain-clang:powerpc64le = " \ + file://0001-ppc64-Do-not-use-mminimal-toc-with-clang.patch \ + " +SRC_URI[sha256sum] = "bbf0297761d53aefda9d7855c57c7d2c272b83a7b5bad4fea9cb29006d8e1d35" + +S = "${WORKDIR}/node-v${PV}" + +CVE_PRODUCT += "node.js" + +# v8 errors out if you have set CCACHE +CCACHE = "" + +def map_nodejs_arch(a, d): + import re + + if re.match('i.86$', a): return 'ia32' + elif re.match('x86_64$', a): return 'x64' + elif re.match('aarch64$', a): return 'arm64' + elif re.match('(powerpc64|powerpc64le|ppc64le)$', a): return 'ppc64' + elif re.match('powerpc$', a): return 'ppc' + return a + +ARCHFLAGS:arm = "${@bb.utils.contains('TUNE_FEATURES', 'callconvention-hard', '--with-arm-float-abi=hard', '--with-arm-float-abi=softfp', d)} \ + ${@bb.utils.contains('TUNE_FEATURES', 'neon', '--with-arm-fpu=neon', \ + bb.utils.contains('TUNE_FEATURES', 'vfpv3d16', '--with-arm-fpu=vfpv3-d16', \ + bb.utils.contains('TUNE_FEATURES', 'vfpv3', '--with-arm-fpu=vfpv3', \ + '--with-arm-fpu=vfp', d), d), d)}" +ARCHFLAGS:append:mips = " --v8-lite-mode" +ARCHFLAGS:append:mipsel = " --v8-lite-mode" +ARCHFLAGS ?= "" + +PACKAGECONFIG ??= "ares brotli icu zlib" + +PACKAGECONFIG[ares] = "--shared-cares,,c-ares c-ares-native" +PACKAGECONFIG[brotli] = "--shared-brotli,,brotli brotli-native" +PACKAGECONFIG[icu] = "--with-intl=system-icu,--without-intl,icu icu-native" +PACKAGECONFIG[libuv] = "--shared-libuv,,libuv" +PACKAGECONFIG[nghttp2] = "--shared-nghttp2,,nghttp2" +PACKAGECONFIG[shared] = "--shared" +PACKAGECONFIG[zlib] = "--shared-zlib,,zlib" + +EXTRANATIVEPATH += "file-native" + +python prune_sources() { + import shutil + + shutil.rmtree(d.getVar('S') + '/deps/openssl') + if 'ares' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/cares') + if 'brotli' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/brotli') + if 'libuv' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/uv') + if 'nghttp2' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/nghttp2') + if 'zlib' in d.getVar('PACKAGECONFIG'): + shutil.rmtree(d.getVar('S') + '/deps/zlib') +} +do_unpack[postfuncs] += "prune_sources" + +# V8's JIT infrastructure requires binaries such as mksnapshot and +# mkpeephole to be run in the host during the build. However, these +# binaries must have the same bit-width as the target (e.g. a x86_64 +# host targeting ARMv6 needs to produce a 32-bit binary). +# 1. If host and target have the different bit width, run those +# binaries for the target and run them on the host with QEMU. +# 2. If host and target have the same bit width, enable upstream +# cross compile support and no QEMU +python do_create_v8_qemu_wrapper () { + """Creates a small wrapper that invokes QEMU to run some target V8 binaries + on the host.""" + qemu_libdirs = [d.expand('${STAGING_DIR_HOST}${libdir}'), + d.expand('${STAGING_DIR_HOST}${base_libdir}')] + qemu_cmd = qemu_wrapper_cmdline(d, d.getVar('STAGING_DIR_HOST'), + qemu_libdirs) + + if d.getVar("HOST_AND_TARGET_SAME_WIDTH") == "1": + qemu_cmd = "" + + wrapper_path = d.expand('${B}/v8-qemu-wrapper.sh') + with open(wrapper_path, 'w') as wrapper_file: + wrapper_file.write("""#!/bin/sh + +# This file has been generated automatically. +# It invokes QEMU to run binaries built for the target in the host during the +# build process. + +%s "$@" +""" % qemu_cmd) + os.chmod(wrapper_path, 0o755) +} + +do_create_v8_qemu_wrapper[dirs] = "${B}" +addtask create_v8_qemu_wrapper after do_configure before do_compile + +LDFLAGS:append:x86 = " -latomic" + +export CC_host +export CFLAGS_host +export CXX_host +export CXXFLAGS_host +export LDFLAGS_host +export AR_host +export HOST_AND_TARGET_SAME_WIDTH + +CROSS_FLAGS = "--cross-compiling" +CROSS_FLAGS:class-native = "--no-cross-compiling" + +# Node is way too cool to use proper autotools, so we install two wrappers to forcefully inject proper arch cflags to workaround gypi +do_configure () { + GYP_DEFINES="${GYP_DEFINES}" export GYP_DEFINES + # $TARGET_ARCH settings don't match --dest-cpu settings + python3 configure.py --verbose --prefix=${prefix} \ + --shared-openssl \ + --dest-cpu="${@map_nodejs_arch(d.getVar('TARGET_ARCH'), d)}" \ + --dest-os=linux \ + --libdir=${baselib} \ + ${CROSS_FLAGS} \ + ${ARCHFLAGS} \ + ${PACKAGECONFIG_CONFARGS} +} + +do_compile () { + install -D ${RECIPE_SYSROOT_NATIVE}/etc/ssl/openssl.cnf ${B}/deps/openssl/nodejs-openssl.cnf + install -D ${B}/v8-qemu-wrapper.sh ${B}/out/Release/v8-qemu-wrapper.sh + oe_runmake BUILDTYPE=Release +} + +do_install () { + oe_runmake install DESTDIR=${D} +} + +do_install_ptest () { + cp -r ${B}/out/Release/cctest ${D}${PTEST_PATH}/ + cp -r ${B}/test ${D}${PTEST_PATH} + chown -R root:root ${D}${PTEST_PATH} +} + +PACKAGES =+ "${PN}-npm" +FILES:${PN}-npm = "${nonarch_libdir}/node_modules ${bindir}/npm ${bindir}/npx ${bindir}/corepack" +RDEPENDS:${PN}-npm = "bash python3-core python3-shell python3-datetime \ + python3-misc python3-multiprocessing" + +PACKAGES =+ "${PN}-systemtap" +FILES:${PN}-systemtap = "${datadir}/systemtap" + +do_configure[prefuncs] += "set_gyp_variables" +do_compile[prefuncs] += "set_gyp_variables" +do_install[prefuncs] += "set_gyp_variables" +python set_gyp_variables () { + if d.getVar("HOST_AND_TARGET_SAME_WIDTH") == "0": + # We don't want to cross-compile during target compile, + # and we need to use the right flags during host compile, + # too. + d.setVar("CC_host", d.getVar("CC") + " -pie -fPIE") + d.setVar("CFLAGS_host", d.getVar("CFLAGS")) + d.setVar("CXX_host", d.getVar("CXX") + " -pie -fPIE") + d.setVar("CXXFLAGS_host", d.getVar("CXXFLAGS")) + d.setVar("LDFLAGS_host", d.getVar("LDFLAGS")) + d.setVar("AR_host", d.getVar("AR")) + elif d.getVar("HOST_AND_TARGET_SAME_WIDTH") == "1": + # Enable upstream cross compile support + d.setVar("CC_host", d.getVar("BUILD_CC")) + d.setVar("CFLAGS_host", d.getVar("BUILD_CFLAGS")) + d.setVar("CXX_host", d.getVar("BUILD_CXX")) + d.setVar("CXXFLAGS_host", d.getVar("BUILD_CXXFLAGS")) + d.setVar("LDFLAGS_host", d.getVar("BUILD_LDFLAGS")) + d.setVar("AR_host", d.getVar("BUILD_AR")) +} + +python __anonymous () { + # 32 bit target and 64 bit host (x86-64 or aarch64) have different bit width + if d.getVar("SITEINFO_BITS") == "32" and "64" in d.getVar("BUILD_ARCH"): + d.setVar("HOST_AND_TARGET_SAME_WIDTH", "0") + else: + d.setVar("HOST_AND_TARGET_SAME_WIDTH", "1") +} + +BBCLASSEXTEND = "native" From 6e8ec57ac2c9ea9d2e7b9409a675d95e26203c7e Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 2 Sep 2025 18:08:43 +0100 Subject: [PATCH 14/15] Use scarthgap branch of meta-chromium-test meta-chromium-test has some modifications for scarthgap builds. Use that branch when building for scarthgap --- .github/workflows/bs_meta_browser_build_and_test.yml | 4 ++-- .github/workflows/chromium.yml | 2 +- .github/workflows/electron.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bs_meta_browser_build_and_test.yml b/.github/workflows/bs_meta_browser_build_and_test.yml index e539c55ad..6a205820a 100644 --- a/.github/workflows/bs_meta_browser_build_and_test.yml +++ b/.github/workflows/bs_meta_browser_build_and_test.yml @@ -329,8 +329,8 @@ jobs: git -C meta-browser checkout $GH_REV # Clone the test repo - echo "Cloning meta-chromium-test electron-master branch" - git clone https://github.com/brightsign/meta-chromium-test.git + echo "Cloning meta-chromium-test scarthgap branch" + git clone -b scarthgap https://github.com/brightsign/meta-chromium-test.git # Run the build - name: Build Browser diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 9e7436330..11f6c14f0 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -98,5 +98,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone https://github.com/brightsign/meta-chromium-test.git + git clone -b scarthgap https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 90f8c8311..9608e654d 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -98,5 +98,5 @@ jobs: git clone $GH_URL git -C meta-browser checkout $GH_REV # clone the test repo - git clone https://github.com/brightsign/meta-chromium-test.git + git clone -b scarthgap https://github.com/brightsign/meta-chromium-test.git ./meta-chromium-test/scripts/build.sh ${{ matrix.yocto_version}} ${{ matrix.arch }} ${{ matrix.browser_version }} ${{ matrix.browser }} ${{ matrix.libc_flavour}} From be5ed127f9ea57e6c636db3d8ab4152d4de625d3 Mon Sep 17 00:00:00 2001 From: Caner Altinbasak Date: Tue, 2 Sep 2025 18:11:08 +0100 Subject: [PATCH 15/15] Use scarthgap as yocto version --- .github/workflows/chromium.yml | 2 +- .github/workflows/electron.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/chromium.yml b/.github/workflows/chromium.yml index 11f6c14f0..e67c36f98 100644 --- a/.github/workflows/chromium.yml +++ b/.github/workflows/chromium.yml @@ -48,7 +48,7 @@ jobs: strategy: fail-fast: false # Continue other matrix jobs even if one fails matrix: - yocto_version: [master] + yocto_version: [scarthgap] ozone_platform: [ozone-wayland, x11] libc_flavour: [glibc] arch: [arm, aarch64, x86-64] diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 9608e654d..fe2c9524d 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -48,7 +48,7 @@ jobs: strategy: fail-fast: false # Continue other matrix jobs even if one fails matrix: - yocto_version: [master] + yocto_version: [scarthgap] ozone_platform: [ozone-wayland, ozone-x11] libc_flavour: [glibc] arch: [arm, aarch64, x86-64]