diff --git a/.config_gclient b/.config_gclient deleted file mode 100644 index 28cc0075519b4cb55e110b5f5f67553bbf55ec80..0000000000000000000000000000000000000000 --- a/.config_gclient +++ /dev/null @@ -1,75 +0,0 @@ -solutions = [{ - "url": "https://chromium.googlesource.com/chromium/src.git", - "managed": False, - "name": "src", - "custom_deps": { - "src/native_client": None, - "src/third_party/gvr-android-sdk/src": None, - "src/third_party/arcore-android-sdk/src": None - }, - "custom_hooks": [{ - "name": "gvr_static_shim_android_arm", - "condition": False - }, { - "name": "gvr_static_shim_android_arm_1", - "condition": False - }, { - "name": "gvr_static_shim_android_arm_ndk1", - "condition": False - }, { - "name": "gvr_static_shim_android_arm_Cr", - "condition": False - }, { - "name": "gvr_static_shim_android_arm64", - "condition": False - }, { - "name": "gvr_static_shim_android_arm64_1", - "condition": False - }, { - "name": "gvr_static_shim_android_arm64_ndk1", - "condition": False - }, { - "name": "gvr_static_shim_android_arm64_Cr", - "condition": False - }, { - "name": "gvr_static_shim_custom_libcxx_android_arm", - "condition": False - }, { - "name": "gvr_static_shim_custom_libcxx_android_arm64", - "condition": False - }, { - "name": "sdkextras", - "condition": False - }, { - "name": "sysroot_x86", - "condition": False - }, { - "name": "sysroot_x64", - "condition": False - }, { - "name": "vr_controller_test_api", - "condition": False - }, { - "name": "vr_test_apks", - "condition": False - }, { - "name": "ar_test_apks", - "condition": False - }, { - "name": "test_fonts", - "condition": False - }, { - "name": "zucchini_testdata", - "condition": False - }, { - "name": "Fetch Android RenderTest goldens", - "condition": False - }], - "custom_vars": { - "checkout_instrumented_libraries": False, - "checkout_nacl": False, - "checkout_traffic_annotation_tools": False, - } -}] - -target_os = ["android"] diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index dcc666f725094d1e10c2ddf623e1e30aaa75d8ed..0000000000000000000000000000000000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,4 +0,0 @@ -# These are supported funding model platforms - -liberapay: csagan5 -custom: https://www.bromite.org/#donate diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 782bf25a0806e9078a0edbf21d96d420f4dd9ad4..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -name: Bug report -about: Create a Bromite bug report - ---- - - - -### Bromite version - -Version: `89.0...` -Arch: `arm` or `arm64` or `x86` -Android version: (example: `9.0`) -Device model: (example: `SM-G960UZKABST`) - -### Flags - -These are the flags changed under `chrome://flags`: - - - -### Is this bug about the SystemWebView? - - - -Yes/No - -### Is the bug reproducible with latest version? - - - -Yes/No - -### Can the bug be reproduced with corresponding Chromium version? - - - -Yes/No - -### Is this bug happening in an incognito tab? -Yes/No - -### Allow ads for the site; is the bug caused by the adblocker? -Yes/No - -### Is the bug a crash? - -No - --- or -- - -Yes, this is the crash dump: -``` - -``` - -### Describe the bug - - - -### Steps to reproduce the bug - -Steps to reproduce the bug: -1. Go to '...' -2. Click on '...' -3. Scroll down to '...' -4. See error - -Do not write "any website", specify which URLs can be used to reproduce the issue. - -### Expected behavior - - - -### Screenshots - - diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 8540ae0a1c687150e052536d5a2a4ea814107ea6..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Feature request -about: Suggest a privacy-related idea for this project - ---- - - - - - -### Is your feature request related to privacy? - - - -### Is there a patch available for this feature somewhere? - - - -### Describe the solution you would like - - - -### Describe alternatives you have considered - - diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9293f69d6741f0468ab789f718761b81d3258ac4..0000000000000000000000000000000000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "bromite"] - path = bromite - url = https://github.com/bromite/bromite/ diff --git a/Dockerfile b/Dockerfile index b0652088961948d9d799ae628c7971f408e9c86b..476b21ded2bddd2e35e081f4e1bafb40b9562114 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,48 +1,22 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 ENV CHROMIUM_DIR "/srv/chromium" -ENV DEBIAN_FRONTEND noninteractive -# Enable foreign architectures support -RUN dpkg --print-architecture && \ - dpkg --print-foreign-architectures +RUN dpkg --add-architecture i386 -RUN dpkg --add-architecture i386 && \ - dpkg --print-foreign-architectures +RUN apt-get update &&\ + DEBIAN_FRONTEND=noninteractive \ + apt-get -f -y install sudo lsb-release cl-base64 bash wget apt-utils ccache jq python3 \ + python-is-python3 sed tzdata build-essential lib32gcc-9-dev g++-multilib dos2unix wiggle git curl -# Install required packages -COPY packages.txt /packages.txt - -RUN apt-get -qq update && \ - cat /packages.txt | xargs apt-get install -qqy - -RUN rm /packages.txt - -# Install and setup python 3.10 as default -RUN add-apt-repository -y ppa:deadsnakes/ppa - -RUN apt-get -qq update && \ - apt-get install -qqy --no-install-recommends \ - python3.8 lib32z1 lighttpd xvfb libncurses5:i386 libstdc++6:i386 zlib1g:i386 ccache - -RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2 -RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1 -RUN update-alternatives --set python3 /usr/bin/python3.8 +RUN mkdir ${CHROMIUM_DIR} -# Install chromium build dependencies RUN curl -s https://raw.githubusercontent.com/chromium/chromium/main/build/install-build-deps.py \ - | python3 - --no-syms --lib32 --no-arm --no-chromeos-fonts --no-nacl --no-prompt - -RUN apt-get -f install - -# Setup locale -RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 -ENV LANG en_US.UTF-8 + | python - --android --lib32 --no-chromeos-fonts --no-prompt RUN git config --global user.name "John Doe" RUN git config --global user.email "johndoe@example.com" - -RUN mkdir ${CHROMIUM_DIR} +RUN git config --global --add safe.directory "*" WORKDIR ${CHROMIUM_DIR} diff --git a/README.md b/README.md index 543c7ffb13976ffcbfe2420d81b98204505ce0ae..a5792864a06d344011184a4a51957a09d7570da5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Browser +# /e/OS Browser -Browser is an open-source fork of [Bromite](https://www.bromite.org/) which is based on [Chromium](https://www.chromium.org/Home) licensed and distributed under [The GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html). It has several additional changes to improve user experience, security, and privacy. It is shipped as the default browser on /e/ OS. +/e/OS Browser is an open-source fork of [Cromite](https://github.com/uazo/cromite) (Fork of bromite) which is based on [Chromium](https://www.chromium.org/Home) licensed and distributed under [The GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html). It has several additional changes to improve user experience, security, and privacy. It is shipped as the default browser on /e/OS. ## Enhancements Following are the enhancements offered by Browser over bromite: @@ -16,14 +16,14 @@ Following are the enhancements offered by Browser over bromite: - [DuckDuckGo Lite](https://lite.duckduckgo.com/lite) - [Qwant](https://www.qwant.com/) - [espot](https://spot.ecloud.global/) + - [Mojeek](https://www.mojeek.com/) - Remove **Google** and **Chrome** mentions from various strings wherever applicable feature/services were not reliant on the same ## Artifacts -Browser is built using the [GitLab's CI/CD](https://docs.gitlab.com/ee/ci/). You can access the pipelines for this repository [here](https://gitlab.e.foundation/e/apps/browser/-/pipelines). You can download the job artifacts from the pipelines by following instructions from this [page](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#download-job-artifacts). +Browser is built using the [GitLab's CI/CD](https://docs.gitlab.com/ee/ci/). You can access the pipelines for this repository [here](https://gitlab.e.foundation/e/os/browser/-/pipelines). You can download the job artifacts from the pipelines by following instructions from this [page](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#download-job-artifacts). ## Development -- Documentation regarding development can be found on this repository's [wiki](https://gitlab.e.foundation/e/apps/browser/-/wikis/home) +- Documentation regarding development can be found on this repository's [wiki](https://gitlab.e.foundation/e/os/browser/-/wikis/home) +- A the list of contributors can be viewed on this repository's [contributors graph](https://gitlab.e.foundation/e/os/browser/-/graphs/master). -- A the list of contributors can be viewed on this repository's [contributors graph](https://gitlab.e.foundation/e/apps/browser/-/graphs/master). - -In case you wish to contribute to the development of this project, feel free to open a [Merge Request](https://gitlab.e.foundation/e/apps/browser/-/merge_requests) or an [Issue](https://gitlab.e.foundation/e/backlog/-/issues/) for the same. Contributions are always welcome. +In case you wish to contribute to the development of this project, feel free to open a [Merge Request](https://gitlab.e.foundation/e/os/browser/-/merge_requests) or an [Issue](https://gitlab.e.foundation/e/backlog/-/issues/) for the same. Contributions are always welcome. diff --git a/bromite b/bromite deleted file mode 160000 index 6f40f8341ab3fbcab458c10fe7b6bbcb8f881404..0000000000000000000000000000000000000000 --- a/bromite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6f40f8341ab3fbcab458c10fe7b6bbcb8f881404 diff --git a/build.sh b/build.sh index 074907f29cf4b41b41399677e8fe081ca1a72a7d..8b8904488fff56d02756614807fee244497ef1be 100755 --- a/build.sh +++ b/build.sh @@ -7,8 +7,7 @@ root_dir=$(dirname "$(readlink -f "$0")") if [ ! -d "$chromium_dir" ]; then chromium_dir=$root_dir fi -date_time=$(date '+%Y%m%d%H%M%S') -chromium_version=$(head -n 1 "${root_dir}/bromite/CHANGELOG.md" | sed 's/# \(.*\)/\1/') +chromium_version="117.0.5938.153" chromium_code=$(echo "$chromium_version" | tr -d '.' | cut -c5-) clean=0 gsync=0 @@ -30,17 +29,10 @@ usage() { build() { echo ">> [$(date)] Head commit: $(git show -s --format=%s)" - apks="ChromePublic SystemWebView TrichromeChrome32 TrichromeLibrary32 TrichromeWebView32 TrichromeChrome6432 TrichromeLibrary6432 TrichromeWebView6432" + apks="ChromePublic SystemWebView TrichromeChrome32 TrichromeLibrary32 TrichromeWebView32 TrichromeChrome6432 TrichromeLibrary6432 TrichromeWebView6432 MonochromePublic" build_args="$(cat "${root_dir}"/build/browser.gn_args) target_cpu=\"${1}\" " - if [ $1 '==' "x86" ] || [ $1 '==' "x64" ]; then - build_args+=' is_cfi=false' - build_args+=' use_cfi_cast=false' - else - build_args+=' is_cfi=true' - build_args+=' use_cfi_cast=true' - fi - + apk_arch=${1} code=$chromium_code if [ $1 '==' "arm" ]; then code+=00 @@ -49,6 +41,7 @@ build() { elif [ $1 '==' "x86" ]; then code+=10 elif [ $1 '==' "x64" ]; then + apk_arch="x86_64" code+=60 fi build_args+=' android_default_version_name="'$chromium_version'"' @@ -62,40 +55,26 @@ build() { echo ">> [$(date)] Building chromium $chromium_version for $1" gn gen "out/$1" --args="$build_args" + build_targets="chrome_public_apk system_webview_apk monochrome_public_apk" if [ $1 '==' "x64" ] || [ $1 '==' "arm64" ]; then - ninja -C out/$1 trichrome_webview_32_apk trichrome_chrome_32_apk trichrome_library_32_apk \ - trichrome_webview_64_32_apk trichrome_chrome_64_32_apk trichrome_library_64_32_apk \ - chrome_public_apk system_webview_apk - elif [ $1 '==' "x86" ] || [ $1 '==' "arm" ]; then - ninja -C out/$1 chrome_public_apk system_webview_apk + build_targets="$build_targets trichrome_webview_32_apk trichrome_chrome_32_apk trichrome_library_32_apk \ + trichrome_webview_64_32_apk trichrome_chrome_64_32_apk trichrome_library_64_32_apk" fi + ninja -C out/$1 $build_targets for apk in $apks; do if [ -f "out/${1}/apks/$apk.apk" ]; then - echo ">> [$(date)] Moving $apk for ${1} to output folder" - if [[ "$apk.apk" == ChromePublic.apk ]]; then - if [ "$1" = "x64" ]; then - mv "out/${1}/apks/ChromePublic.apk" "${root_dir}/apks/$date_time/x86_64/ChromePublic.apk" - else - mv "out/${1}/apks/ChromePublic.apk" "${root_dir}/apks/$date_time/${1}/ChromePublic.apk" - fi - elif [[ "$apk.apk" == SystemWebView.apk ]]; then - if [ "$1" = "x64" ]; then - mv "out/${1}/apks/SystemWebView.apk" "${root_dir}/apks/$date_time/x86_64/SystemWebView.apk" - else - mv "out/${1}/apks/SystemWebView.apk" "${root_dir}/apks/$date_time/${1}/SystemWebView.apk" - fi - elif [[ "$apk.apk" == *6432.apk ]]; then - if [ "$1" = "x64" ]; then - mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/$date_time/x86_64/${apk::-4}.apk" - elif [ "$1" = "arm64" ]; then - mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/$date_time/arm64/${apk::-4}.apk" - fi + echo ">> [$(date)] Moving $apk for ${apk_arch} to output folder" + if [[ "$apk.apk" == *Public.apk ]] || [[ "$apk.apk" == SystemWebView.apk ]] || [[ "$apk.apk" == *6432.apk ]]; then + mkdir -p "${root_dir}/apks/${apk_arch}" + mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/${apk_arch}/$(echo "$apk" | sed 's/[0-9]*//g').apk" else if [ "$1" = "x64" ]; then - mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/$date_time/x86/${apk::-2}.apk" + mkdir -p "${root_dir}/apks/x86" + mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/x86/$(echo "$apk" | sed 's/[0-9]*//g').apk" elif [ "$1" = "arm64" ]; then - mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/$date_time/arm/${apk::-2}.apk" + mkdir -p "${root_dir}/apks/arm" + mv "out/${1}/apks/$apk.apk" "${root_dir}/apks/arm/$(echo "$apk" | sed 's/[0-9]*//g').apk" fi fi fi @@ -117,32 +96,71 @@ patch() { cd $chromium_dir/src echo ">> [$(date)] Applying bromite and /e/ patches" - bromite_patches_list=$(cat "${root_dir}/bromite/build/bromite_patches_list.txt") + bromite_patches_list=$(cat "${root_dir}/build/bromite_patches_list.txt") for file in $bromite_patches_list; do - git am -3 --ignore-whitespace "${root_dir}/bromite/build/patches/$file" + git am -3 --ignore-whitespace "${root_dir}/build/bromite_patches/$file" done e_patches_list=$(cat "${root_dir}/build/e_patches_list.txt") for file in $e_patches_list; do - git am -3 --ignore-whitespace "${root_dir}/build/patches/$file" + git am -3 --ignore-whitespace "${root_dir}/build/e_patches/$file" done # Rename Chrome to Browser - bash ${root_dir}/rename.sh + rename +} + +rename() { + cd $chromium_dir/src + + # Translation directories + dirs="components components/strings chrome/browser/ui/android/strings chrome/browser/ui/android/strings/translations" + + for dir in $dirs; do + grdp_files=$(ls $dir/*.grdp 2> /dev/null | wc -l) + if [ "$grdp_files" != "0" ]; then + sed -i 's/Chrome browser/Browser/g' $dir/*.grd* + sed -i 's/Chrome/Browser/g' $dir/*.grd* + fi + xtb_files=$(ls $dir/*.xtb 2> /dev/null | wc -l) + if [ "$xtb_files" != "0" ]; then + sed -i 's/Chrome browser/Browser/g' $dir/*.xtb + sed -i 's/Chrome/Browser/g' $dir/*.xtb + fi + done + + git add . + git commit -m "Browser: Replace Chrome with Browser" } sync() { echo ">> [$(date)] Syncing chromium $chromium_version" cd $chromium_dir - cp ${root_dir}/.config_gclient $chromium_dir/.gclient + gclient_config if [ $history -eq 1 ]; then - yes | gclient sync -D -R -r $chromium_version + gclient sync -D --nohooks -R -r $chromium_version else - yes | gclient sync --no-history -D -R -r $chromium_version + gclient sync --no-history -D --nohooks -R -r $chromium_version fi + gclient runhooks patch } +gclient_config() { + cat < "$chromium_dir/.gclient" +solutions = [{ + "url": "https://github.com/chromium/chromium", + "managed": False, + "name": "src", + "custom_deps": {}, + "custom_hooks": [], + "custom_vars": {} +}] + +target_os = ["android"] +EOF +} + init_repo(){ echo ">> [$(date)] Init chromium $chromium_version" cd $chromium_dir @@ -194,19 +212,23 @@ if [ ! -d "$chromium_dir/src" ]; then fi if [ $gsync -eq 1 ]; then - if [ -d "$chromium_dir/src/.git/rebase-apply" ]; then - rm -fr "$chromium_dir/src/.git/rebase-apply" + cd $chromium_dir/src + # Check if a rebase is in progress + if [ -d "$(git rev-parse --git-dir)/rebase-apply" ] || [ -d "$(git rev-parse --git-dir)/rebase-merge" ]; then + git am --skip 2>/dev/null || true + git rebase --abort 2>/dev/null || true + echo ">> [$(date)] Rebase aborted." fi - find "$chromium_dir/src/.git" -name "*.lock" -delete - if [ ! -z $(find $chromium_dir/src/.git -name "*.lock") ]; then + find ".git" -name "*.lock" -delete + if [ ! -z $(find .git -name "*.lock") ]; then + echo ">> [$(date)] Re-init chromium." + cd ${root_dir} rm -fr $chromium_dir/src init_repo fi sync fi -mkdir -p "${root_dir}/apks/$date_time/x86_64" "${root_dir}/apks/$date_time/arm64" "${root_dir}/apks/$date_time/x86" "${root_dir}/apks/$date_time/arm" - cd $chromium_dir/src . build/android/envsetup.sh setup_ccache @@ -219,7 +241,3 @@ else build x86 build x64 fi - -if [ -d "${root_dir}/apks" ]; then - find "${root_dir}/apks" -empty -type d -delete -fi diff --git a/build/bromite_patches/AImageReader-CFI-crash-mitigations.patch b/build/bromite_patches/AImageReader-CFI-crash-mitigations.patch new file mode 100644 index 0000000000000000000000000000000000000000..c7397602ad6cdf7c3d2ab1bd69f5a8c435e4c69e --- /dev/null +++ b/build/bromite_patches/AImageReader-CFI-crash-mitigations.patch @@ -0,0 +1,274 @@ +From 4f01cc05c38f2abb1fbbc71c679d03f02a06324f Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Tue, 5 May 2020 07:22:20 +0200 +Subject: [PATCH 106/192] AImageReader CFI crash mitigations + +Revert "gpu/android: Remove setup for disabling AImageReader." +This reverts commit dcd5a39518246eb999f1cc63bf1ec95d93fd5b2f. + +Revert "Remove flags to enable/disable AImageReader." +This reverts commit 463fa0f2e3b9e418bc26e2c8954463f0b0f76634. + +Restore GPU bug blacklist for AImageReader on ARM and Qualcomm CPUs + +Restore the AImageReader blacklist for ARM/Qualcomm chipsets which causes +crashes on Android 9 and 10 (at different code locations). + +See discussions at: +* https://github.com/bromite/bromite/issues/445 +* https://github.com/bromite/bromite/issues/814 +* https://github.com/bromite/bromite/issues/1005 + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + base/android/android_image_reader_compat.cc | 8 +++++++- + base/android/android_image_reader_compat.h | 4 ++++ + chrome/browser/flag-metadata.json | 6 +++--- + gpu/config/gpu_driver_bug_list.json | 16 ++++++++++++++++ + gpu/config/gpu_finch_features.cc | 5 +++++ + gpu/config/gpu_finch_features.h | 1 + + gpu/config/gpu_util.cc | 8 ++++++++ + gpu/config/gpu_workaround_list.txt | 1 + + gpu/ipc/service/gpu_init.cc | 7 +++++++ + gpu/ipc/service/stream_texture_android.cc | 11 ++++++++++- + media/base/media_switches.cc | 5 +++++ + media/base/media_switches.h | 1 + + 12 files changed, 68 insertions(+), 5 deletions(-) + +diff --git a/base/android/android_image_reader_compat.cc b/base/android/android_image_reader_compat.cc +index d26f7e8a19517..a20f33f114b91 100644 +--- a/base/android/android_image_reader_compat.cc ++++ b/base/android/android_image_reader_compat.cc +@@ -23,6 +23,8 @@ + namespace base { + namespace android { + ++bool AndroidImageReader::disable_support_ = false; ++ + AndroidImageReader& AndroidImageReader::GetInstance() { + // C++11 static local variable initialization is + // thread-safe. +@@ -30,8 +32,12 @@ AndroidImageReader& AndroidImageReader::GetInstance() { + return instance; + } + ++void AndroidImageReader::DisableSupport() { ++ disable_support_ = true; ++} ++ + bool AndroidImageReader::IsSupported() { +- return is_supported_; ++ return !disable_support_ && is_supported_; + } + + AndroidImageReader::AndroidImageReader() : is_supported_(LoadFunctions()) {} +diff --git a/base/android/android_image_reader_compat.h b/base/android/android_image_reader_compat.h +index bef2ae103235c..b44886946d565 100644 +--- a/base/android/android_image_reader_compat.h ++++ b/base/android/android_image_reader_compat.h +@@ -24,6 +24,9 @@ class BASE_EXPORT AndroidImageReader { + AndroidImageReader(const AndroidImageReader&) = delete; + AndroidImageReader& operator=(const AndroidImageReader&) = delete; + ++ // Disable image reader support. ++ static void DisableSupport(); ++ + // Check if the image reader usage is supported. This function returns TRUE + // if android version is >=OREO, image reader support is not disabled and all + // the required functions are loaded. +@@ -61,6 +64,7 @@ class BASE_EXPORT AndroidImageReader { + jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window); + + private: ++ static bool disable_support_; + friend class base::NoDestructor; + + AndroidImageReader(); +diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json +index fded6daa7c8e2..a6d5da2c7c3a6 100644 +--- a/chrome/browser/flag-metadata.json ++++ b/chrome/browser/flag-metadata.json +@@ -2928,9 +2928,9 @@ + "expiry_milestone": 125 + }, + { +- "name": "enable-image-reader", +- "owners": [ "vikassoni", "liberato" ], +- "expiry_milestone": 125 ++ "name": "enable-image-reader", // Bromite: do not expire ++ "owners": [ "vikassoni", "liberato" ], // flag ++ "expiry_milestone": -1 + }, + { + "name": "enable-immersive-fullscreen-toolbar", +diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json +index 71a4f139d781f..7144b22e13688 100644 +--- a/gpu/config/gpu_driver_bug_list.json ++++ b/gpu/config/gpu_driver_bug_list.json +@@ -3688,6 +3688,22 @@ + "no_downscaled_overlay_promotion" + ] + }, ++ { ++ "id":335, ++ "cr_bugs": [1051705], ++ "description": "Disable AImageReader on ARM GPUs", ++ "os": { ++ "type": "android", ++ "version": { ++ "op": "<", ++ "value": "10" ++ } ++ }, ++ "gl_vendor": "ARM.*|Qualcomm.*", ++ "features": [ ++ "disable_aimagereader" ++ ] ++ }, + { + "id": 381, + "cr_bugs": [ +diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc +index f0138acdeae0c..76c2d34ed046a 100644 +--- a/gpu/config/gpu_finch_features.cc ++++ b/gpu/config/gpu_finch_features.cc +@@ -65,6 +65,11 @@ BASE_FEATURE(kUseGles2ForOopR, + #endif + ); + ++ ++// Use android AImageReader when playing videos with MediaPlayer. ++const base::Feature kAImageReaderMediaPlayer{"AImageReaderMediaPlayer", ++ base::FEATURE_ENABLED_BY_DEFAULT}; ++ + #if BUILDFLAG(IS_ANDROID) + // Use android SurfaceControl API for managing display compositor's buffer queue + // and using overlays on Android. Also used by webview to disable surface +diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h +index c77113dda67c4..662e100792a16 100644 +--- a/gpu/config/gpu_finch_features.h ++++ b/gpu/config/gpu_finch_features.h +@@ -17,6 +17,7 @@ namespace features { + GPU_EXPORT BASE_DECLARE_FEATURE(kUseGles2ForOopR); + + // All features in alphabetical order. The features should be documented ++GPU_EXPORT extern const base::Feature kAImageReaderMediaPlayer; + // alongside the definition of their values in the .cc file. + #if BUILDFLAG(IS_ANDROID) + GPU_EXPORT BASE_DECLARE_FEATURE(kAndroidSurfaceControl); +diff --git a/gpu/config/gpu_util.cc b/gpu/config/gpu_util.cc +index 82528e1b97ab6..572ea0893e45e 100644 +--- a/gpu/config/gpu_util.cc ++++ b/gpu/config/gpu_util.cc +@@ -122,6 +122,9 @@ GpuFeatureStatus GetAndroidSurfaceControlFeatureStatus( + #if !BUILDFLAG(IS_ANDROID) + return kGpuFeatureStatusDisabled; + #else ++ if (blocklisted_features.count(GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL)) ++ return kGpuFeatureStatusBlocklisted; ++ + if (!gpu_preferences.enable_android_surface_control) + return kGpuFeatureStatusDisabled; + +@@ -351,6 +354,11 @@ void AdjustGpuFeatureStatusToWorkarounds(GpuFeatureInfo* gpu_feature_info) { + gpu_feature_info->status_values[GPU_FEATURE_TYPE_CANVAS_OOP_RASTERIZATION] = + kGpuFeatureStatusBlocklisted; + } ++ ++ if (gpu_feature_info->IsWorkaroundEnabled(DISABLE_AIMAGEREADER)) { ++ gpu_feature_info->status_values[GPU_FEATURE_TYPE_ANDROID_SURFACE_CONTROL] = ++ kGpuFeatureStatusBlocklisted; ++ } + } + + // Estimates roughly user total disk space by counting in the drives where +diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt +index 164df51c709af..f2a7ebad434c3 100644 +--- a/gpu/config/gpu_workaround_list.txt ++++ b/gpu/config/gpu_workaround_list.txt +@@ -16,6 +16,7 @@ decode_encode_srgb_for_generatemipmap + depth_stencil_renderbuffer_resize_emulation + disable_2d_canvas_auto_flush + disable_accelerated_av1_decode ++disable_aimagereader + disable_accelerated_av1_encode + disable_accelerated_h264_decode + disable_accelerated_h264_encode +diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc +index e8af3b3325636..ae69ecc8a7384 100644 +--- a/gpu/ipc/service/gpu_init.cc ++++ b/gpu/ipc/service/gpu_init.cc +@@ -628,6 +628,13 @@ bool GpuInit::InitializeAndStartSandbox(base::CommandLine* command_line, + } + #endif // BUILDFLAG(IS_WIN) + ++#if BUILDFLAG(IS_ANDROID) ++ // Disable AImageReader if the workaround is enabled. ++ if (gpu_feature_info_.IsWorkaroundEnabled(DISABLE_AIMAGEREADER)) { ++ base::android::AndroidImageReader::DisableSupport(); ++ } ++#endif ++ + if (gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] != + kGpuFeatureStatusEnabled || + !InitializeVulkan()) { +diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc +index 762b8dabc387c..8939050c6f817 100644 +--- a/gpu/ipc/service/stream_texture_android.cc ++++ b/gpu/ipc/service/stream_texture_android.cc +@@ -6,6 +6,7 @@ + + #include + ++#include "base/android/android_image_reader_compat.h" + #include "base/android/scoped_hardware_buffer_fence_sync.h" + #include "base/feature_list.h" + #include "base/functional/bind.h" +@@ -50,7 +51,15 @@ std::unique_ptr MakeCurrent( + } + + TextureOwner::Mode GetTextureOwnerMode() { +- return features::IsAImageReaderEnabled() ++ const bool a_image_reader_supported = ++ base::android::AndroidImageReader::GetInstance().IsSupported(); ++ ++ // TODO(vikassoni) : Currently we have 2 different flags to enable/disable ++ // AImageReader - one for MCVD and other for MediaPlayer here. Merge those 2 ++ // flags into a single flag. Keeping the 2 flags separate for now since finch ++ // experiment using this flag is in progress. ++ return a_image_reader_supported && features::IsAImageReaderEnabled() && ++ base::FeatureList::IsEnabled(features::kAImageReaderMediaPlayer) + ? TextureOwner::Mode::kAImageReaderInsecure + : TextureOwner::Mode::kSurfaceTextureInsecure; + } +diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc +index 2c8a257f5ea4a..c1c11e4a8b615 100644 +--- a/media/base/media_switches.cc ++++ b/media/base/media_switches.cc +@@ -950,6 +950,11 @@ BASE_FEATURE(kHardwareSecureDecryptionExperiment, + // Allows automatically disabling hardware secure Content Decryption Module + // (CDM) after failures or crashes to fallback to software secure CDMs. If this + // feature is disabled, the fallback will never happen and users could be stuck ++// Enables the Android Image Reader path for Video decoding(for AVDA and MCVD) ++BASE_FEATURE(kAImageReaderVideoOutput, ++ "AImageReaderVideoOutput", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + // in playback failures. + BASE_FEATURE(kHardwareSecureDecryptionFallback, + "HardwareSecureDecryptionFallback", +diff --git a/media/base/media_switches.h b/media/base/media_switches.h +index e59c53e4bb47d..1bbc1ee4056aa 100644 +--- a/media/base/media_switches.h ++++ b/media/base/media_switches.h +@@ -328,6 +328,7 @@ MEDIA_EXPORT BASE_DECLARE_FEATURE(kVideoBlitColorAccuracy); + MEDIA_EXPORT BASE_DECLARE_FEATURE(kVideoToolboxVideoDecoder); + #endif // BUILDFLAG(IS_APPLE) + MEDIA_EXPORT BASE_DECLARE_FEATURE(kWebRTCColorAccuracy); ++MEDIA_EXPORT BASE_DECLARE_FEATURE(kAImageReaderVideoOutput); + MEDIA_EXPORT BASE_DECLARE_FEATURE(kVp9kSVCHWDecoding); + MEDIA_EXPORT BASE_DECLARE_FEATURE(kWebContentsCaptureHiDpi); + MEDIA_EXPORT BASE_DECLARE_FEATURE(kWebrtcMediaCapabilitiesParameters); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-AllowUserCertificates-flag.patch b/build/bromite_patches/Add-AllowUserCertificates-flag.patch new file mode 100644 index 0000000000000000000000000000000000000000..e4a9db90b939bc269ca32a9d9af8fd772bc8e035 --- /dev/null +++ b/build/bromite_patches/Add-AllowUserCertificates-flag.patch @@ -0,0 +1,177 @@ +From c2c6c28c0a830799a637c647f8fecea70a240c6d Mon Sep 17 00:00:00 2001 +From: uazo +Date: Mon, 26 Apr 2021 13:28:24 +0000 +Subject: [PATCH 130/192] Add AllowUserCertificates flag + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../org/chromium/chrome/browser/app/ChromeActivity.java | 3 +++ + chrome/browser/about_flags.cc | 7 +++++++ + chrome/browser/flag_descriptions.cc | 5 +++++ + chrome/browser/flag_descriptions.h | 3 +++ + chrome/browser/flags/android/chrome_feature_list.cc | 5 +++++ + chrome/browser/flags/android/chrome_feature_list.h | 1 + + .../chromium/chrome/browser/flags/ChromeFeatureList.java | 4 ++++ + net/android/java/src/org/chromium/net/X509Util.java | 5 +++++ + 8 files changed, 33 insertions(+) + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +index a4956e49ad6b7..dd309ae5dcfd9 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +@@ -230,6 +230,7 @@ import org.chromium.content_public.browser.ScreenOrientationProvider; + import org.chromium.content_public.browser.SelectionPopupController; + import org.chromium.content_public.browser.WebContents; + import org.chromium.content_public.common.ContentSwitches; ++import org.chromium.net.X509Util; + import org.chromium.printing.PrintManagerDelegateImpl; + import org.chromium.printing.PrintingController; + import org.chromium.printing.PrintingControllerImpl; +@@ -981,6 +982,8 @@ public abstract class ChromeActivity + super.onStartWithNative(); + ChromeActivitySessionTracker.getInstance().onStartWithNative(); + ChromeCachedFlags.getInstance().cacheNativeFlags(); ++ X509Util.AllowUserCertificates = ChromeFeatureList.isEnabled( ++ ChromeFeatureList.ALLOW_USER_CERTIFICATES); + + // postDeferredStartupIfNeeded() is called in TabModelSelectorTabObsever#onLoadStopped(), + // #onPageLoadFinished() and #onCrash(). If we are not actively loading a tab (e.g. +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 1b2515a172b22..a30a18f00179e 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -9309,6 +9309,13 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kEnableDrDcDescription, kOsAll, + FEATURE_VALUE_TYPE(features::kEnableDrDc)}, + ++#if BUILDFLAG(IS_ANDROID) ++ {"allow-user-certificates", ++ flag_descriptions::kAllowUserCertificatesName, ++ flag_descriptions::kAllowUserCertificatesDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE(chrome::android::kAllowUserCertificates)}, ++#endif // BUILDFLAG(IS_ANDROID) ++ + {"force-gpu-main-thread-to-normal-priority-drdc", + flag_descriptions::kForceGpuMainThreadToNormalPriorityDrDcName, + flag_descriptions::kForceGpuMainThreadToNormalPriorityDrDcDescription, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 9faf86f94d203..4032bab43c722 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -16,6 +16,11 @@ + + namespace flag_descriptions { + ++const char kAllowUserCertificatesName[] = "Allow user certificates"; ++const char kAllowUserCertificatesDescription[] = ++ "Allow user CA certificates during " ++ "validation of the certificate chain"; ++ + const char kAccelerated2dCanvasName[] = "Accelerated 2D canvas"; + const char kAccelerated2dCanvasDescription[] = + "Enables the use of the GPU to perform 2d canvas rendering instead of " +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index c095902cd95fc..e95beecefb599 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -42,6 +42,9 @@ namespace flag_descriptions { + + // Cross-platform ------------------------------------------------------------- + ++extern const char kAllowUserCertificatesName[]; ++extern const char kAllowUserCertificatesDescription[]; ++ + extern const char kAccelerated2dCanvasName[]; + extern const char kAccelerated2dCanvasDescription[]; + +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +index 81cadb4071645..45fb8f39dd1dd 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -142,6 +142,7 @@ const base::Feature* const kFeaturesExposedToJava[] = { + &feed::kFeedBoCSigninInterstitial, + &feed::kFeedUserInteractionReliabilityReport, + &feed::kInterestFeedContentSuggestions, ++ &kAllowUserCertificates, + &feed::kInterestFeedV2, + &feed::kInterestFeedV2Autoplay, + &feed::kInterestFeedV2Hearts, +@@ -467,6 +468,10 @@ BASE_FEATURE(kSearchReadyOmniboxFeature, + "SearchReadyOmnibox", + base::FEATURE_DISABLED_BY_DEFAULT); + ++BASE_FEATURE(kAllowUserCertificates, ++ "AllowUserCertificates", ++ base::FEATURE_DISABLED_BY_DEFAULT); ++ + BASE_FEATURE(kFocusOmniboxInIncognitoTabIntents, + "FocusOmniboxInIncognitoTabIntents", + base::FEATURE_ENABLED_BY_DEFAULT); +diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h +index b24ca80ae7978..f279afd2d62f0 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.h ++++ b/chrome/browser/flags/android/chrome_feature_list.h +@@ -22,6 +22,7 @@ BASE_DECLARE_FEATURE(kAdvancedPeripheralsSupport); + BASE_DECLARE_FEATURE(kAdvancedPeripheralsSupportTabStrip); + BASE_DECLARE_FEATURE(kAllowNewIncognitoTabIntents); + BASE_DECLARE_FEATURE(kAndroidAppIntegration); ++BASE_DECLARE_FEATURE(kAllowUserCertificates); + BASE_DECLARE_FEATURE(kAndroidAppIntegrationSafeSearch); + BASE_DECLARE_FEATURE(kAndroidSearchEngineChoiceNotification); + BASE_DECLARE_FEATURE(kAndroidImprovedBookmarks); +diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +index cd651e7d1a787..d0d1a4271442a 100644 +--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java ++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +@@ -98,6 +98,7 @@ public abstract class ChromeFeatureList { + } + + /* Alphabetical: */ ++ public static final String ALLOW_USER_CERTIFICATES = "AllowUserCertificates"; + public static final String ADAPTIVE_BUTTON_IN_TOP_TOOLBAR = "AdaptiveButtonInTopToolbar"; + public static final String ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_TRANSLATE = + "AdaptiveButtonInTopToolbarTranslate"; +@@ -504,6 +505,8 @@ public abstract class ChromeFeatureList { + public static final String XSURFACE_METRICS_REPORTING = "XsurfaceMetricsReporting"; + + /* Alphabetical: */ ++ public static final CachedFlag sAllowUserCertificates = ++ new CachedFlag(ALLOW_USER_CERTIFICATES, false); + public static final CachedFlag sAppMenuMobileSiteOption = + new CachedFlag(APP_MENU_MOBILE_SITE_OPTION, false); + public static final CachedFlag sBackGestureActivityTabProvider = +@@ -629,6 +632,7 @@ public abstract class ChromeFeatureList { + + public static final List sFlagsCachedFullBrowser = List.of( + // clang-format off ++ sAllowUserCertificates, + sAppMenuMobileSiteOption, + sBackGestureActivityTabProvider, + sBackGestureRefactorActivityAndroid, +diff --git a/net/android/java/src/org/chromium/net/X509Util.java b/net/android/java/src/org/chromium/net/X509Util.java +index 2d1340a43c36e..7b6ce578c3eef 100644 +--- a/net/android/java/src/org/chromium/net/X509Util.java ++++ b/net/android/java/src/org/chromium/net/X509Util.java +@@ -545,6 +545,8 @@ public class X509Util { + return userRootBytes.toArray(new byte[0][]); + } + ++ public static boolean AllowUserCertificates = false; ++ + public static AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain, + String authType, + String host) +@@ -631,6 +633,9 @@ public class X509Util { + isIssuedByKnownRoot = isKnownRoot(root); + } + ++ if (AllowUserCertificates == false && isIssuedByKnownRoot == false) ++ return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NO_TRUSTED_ROOT); ++ + return new AndroidCertVerifyResult(CertVerifyStatusAndroid.OK, + isIssuedByKnownRoot, verifiedChain); + } +-- +2.34.1 + diff --git a/build/bromite_patches/Add-Alt-D-hotkey-to-focus-address-bar.patch b/build/bromite_patches/Add-Alt-D-hotkey-to-focus-address-bar.patch new file mode 100644 index 0000000000000000000000000000000000000000..7f0715b0cf8f68a4f603a49f6e86cc05aec54c7b --- /dev/null +++ b/build/bromite_patches/Add-Alt-D-hotkey-to-focus-address-bar.patch @@ -0,0 +1,26 @@ +From bdd8e07d76dd8f5e2b18fdc6117ecf6c657dd2d0 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Tue, 9 Mar 2021 19:43:00 +0100 +Subject: [PATCH 128/192] Add Alt+D hotkey to focus address bar + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../java/src/org/chromium/chrome/browser/KeyboardShortcuts.java | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java +index 736a851a56bb4..dfea155c7c963 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/KeyboardShortcuts.java +@@ -152,6 +152,8 @@ public class KeyboardShortcuts { + KeyEvent.KEYCODE_F, KeyEvent.META_CTRL_ON); + addShortcut(context, chromeFeatureShortcutGroup, R.string.keyboard_shortcut_address_bar, + KeyEvent.KEYCODE_L, KeyEvent.META_CTRL_ON); ++ addShortcut(context, chromeFeatureShortcutGroup, R.string.keyboard_shortcut_address_bar, ++ KeyEvent.KEYCODE_D, KeyEvent.META_ALT_ON); + shortcutGroups.add(chromeFeatureShortcutGroup); + + KeyboardShortcutGroup webpageShortcutGroup = new KeyboardShortcutGroup( +-- +2.34.1 + diff --git a/build/bromite_patches/Add-DuckDuckGo-Lite-search-engine.patch b/build/bromite_patches/Add-DuckDuckGo-Lite-search-engine.patch new file mode 100644 index 0000000000000000000000000000000000000000..cc745704ea692514199352d23258b2359a725bb9 --- /dev/null +++ b/build/bromite_patches/Add-DuckDuckGo-Lite-search-engine.patch @@ -0,0 +1,864 @@ +From 84df6b7913e85a3fcef6625a29ff68900f54bbc5 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Thu, 12 Oct 2017 11:06:18 +0200 +Subject: [PATCH 048/192] Add DuckDuckGo Lite search engine + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../search_engines/prepopulated_engines.json | 10 + + .../search_engines/search_engine_type.h | 1 + + .../template_url_prepopulate_data.cc | 184 +++++++++--------- + 3 files changed, 103 insertions(+), 92 deletions(-) + +diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json +index 31042adbe1137..474c5cf13eac3 100644 +--- a/components/search_engines/prepopulated_engines.json ++++ b/components/search_engines/prepopulated_engines.json +@@ -102,6 +102,16 @@ + "id": 92 + }, + ++ "duckduckgo_light": { ++ "name": "DuckDuckGo Light", ++ "keyword": "duckduckgo.com/lite", ++ "favicon_url": "https://duckduckgo.com/favicon.ico", ++ "search_url": "https://duckduckgo.com/lite/?q={searchTerms}", ++ "suggest_url": "https://duckduckgo.com/ac/?q={searchTerms}&type=list", ++ "type": "SEARCH_ENGINE_DUCKDUCKGOLIGHT", ++ "id": 12 ++ }, ++ + "ecosia": { + "name": "Ecosia", + "keyword": "ecosia.org", +diff --git a/components/search_engines/search_engine_type.h b/components/search_engines/search_engine_type.h +index b77ce20c6b13c..4942bba7c6e8c 100644 +--- a/components/search_engines/search_engine_type.h ++++ b/components/search_engines/search_engine_type.h +@@ -79,6 +79,7 @@ enum SearchEngineType { + SEARCH_ENGINE_STARTER_PACK_HISTORY = 62, + SEARCH_ENGINE_STARTER_PACK_TABS = 63, + SEARCH_ENGINE_GOOGLE_EN = 64, ++ SEARCH_ENGINE_DUCKDUCKGOLIGHT = 65, + + SEARCH_ENGINE_MAX // Bounding value needed for UMA histogram macro. + }; +diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc +index 565245bfe88b6..4120aa5f41550 100644 +--- a/components/search_engines/template_url_prepopulate_data.cc ++++ b/components/search_engines/template_url_prepopulate_data.cc +@@ -42,7 +42,7 @@ const PrepopulatedEngine* const engines_AE[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -60,7 +60,7 @@ const PrepopulatedEngine* const engines_AR[] = { + &googleen, &google, + &bing, + &yahoo_ar, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -68,7 +68,7 @@ const PrepopulatedEngine* const engines_AR[] = { + const PrepopulatedEngine* const engines_AT[] = { + &googleen, &google, + &bing, +- &yahoo_at, ++ &yahoo_at, &duckduckgo_light, + &duckduckgo, + &ecosia, + }; +@@ -78,7 +78,7 @@ const PrepopulatedEngine* const engines_AU[] = { + &googleen, &google, + &bing, + &yahoo_au, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -87,7 +87,7 @@ const PrepopulatedEngine* const engines_BA[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -97,7 +97,7 @@ const PrepopulatedEngine* const engines_BE[] = { + &bing, + &yahoo, + &ecosia, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Bulgaria +@@ -105,7 +105,7 @@ const PrepopulatedEngine* const engines_BG[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -114,7 +114,7 @@ const PrepopulatedEngine* const engines_BH[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -123,7 +123,7 @@ const PrepopulatedEngine* const engines_BI[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -132,7 +132,7 @@ const PrepopulatedEngine* const engines_BN[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -141,7 +141,7 @@ const PrepopulatedEngine* const engines_BO[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -150,7 +150,7 @@ const PrepopulatedEngine* const engines_BR[] = { + &googleen, &google, + &bing, + &yahoo_br, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -160,7 +160,7 @@ const PrepopulatedEngine* const engines_BY[] = { + &yandex_by, + &mail_ru, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Belize +@@ -177,7 +177,7 @@ const PrepopulatedEngine* const engines_CA[] = { + &googleen, &google, + &bing, + &yahoo_ca, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -185,7 +185,7 @@ const PrepopulatedEngine* const engines_CA[] = { + const PrepopulatedEngine* const engines_CH[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo_ch, + &ecosia, + }; +@@ -195,7 +195,7 @@ const PrepopulatedEngine* const engines_CL[] = { + &googleen, &google, + &bing, + &yahoo_cl, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -214,7 +214,7 @@ const PrepopulatedEngine* const engines_CO[] = { + &bing, + &yahoo_co, + &duckduckgo, +- &ecosia, ++ &ecosia, &duckduckgo_light, + }; + + // Costa Rica +@@ -222,7 +222,7 @@ const PrepopulatedEngine* const engines_CR[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -232,7 +232,7 @@ const PrepopulatedEngine* const engines_CZ[] = { + &seznam_cz, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Germany +@@ -240,7 +240,7 @@ const PrepopulatedEngine* const engines_DE[] = { + &googleen, &google, + &bing, + &yahoo_de, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -249,7 +249,7 @@ const PrepopulatedEngine* const engines_DK[] = { + &googleen, &google, + &bing, + &yahoo_dk, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -258,7 +258,7 @@ const PrepopulatedEngine* const engines_DO[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -268,7 +268,7 @@ const PrepopulatedEngine* const engines_DZ[] = { + &bing, + &yahoo_fr, + &yandex_com, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Ecuador +@@ -295,7 +295,7 @@ const PrepopulatedEngine* const engines_EG[] = { + &bing, + &yahoo, + &yandex_com, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Spain +@@ -303,7 +303,7 @@ const PrepopulatedEngine* const engines_ES[] = { + &googleen, &google, + &bing, + &yahoo_es, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -312,7 +312,7 @@ const PrepopulatedEngine* const engines_FI[] = { + &googleen, &google, + &bing, + &yahoo_fi, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -339,7 +339,7 @@ const PrepopulatedEngine* const engines_GB[] = { + &googleen, &google, + &bing, + &yahoo_uk, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -348,7 +348,7 @@ const PrepopulatedEngine* const engines_GR[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -357,7 +357,7 @@ const PrepopulatedEngine* const engines_GT[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -367,7 +367,7 @@ const PrepopulatedEngine* const engines_HK[] = { + &bing, + &yahoo_hk, + &baidu, +- &so_360, ++ &so_360, &duckduckgo_light, + }; + + // Honduras +@@ -375,7 +375,7 @@ const PrepopulatedEngine* const engines_HN[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -393,7 +393,7 @@ const PrepopulatedEngine* const engines_HU[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -402,7 +402,7 @@ const PrepopulatedEngine* const engines_ID[] = { + &googleen, &google, + &bing, + &yahoo_id, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -411,7 +411,7 @@ const PrepopulatedEngine* const engines_IE[] = { + &googleen, &google, + &bing, + &yahoo_uk, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -421,7 +421,7 @@ const PrepopulatedEngine* const engines_IL[] = { + &bing, + &yandex_ru, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // India +@@ -429,7 +429,7 @@ const PrepopulatedEngine* const engines_IN[] = { + &googleen, &google, + &bing, + &yahoo_in, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &info_com, + }; + +@@ -439,7 +439,7 @@ const PrepopulatedEngine* const engines_IQ[] = { + &bing, + &yahoo, + &yandex_tr, +- &petal_search, ++ &petal_search, &duckduckgo_light, + }; + + // Iran +@@ -448,14 +448,14 @@ const PrepopulatedEngine* const engines_IR[] = { + &bing, + &yahoo, + &duckduckgo, +- &ask, ++ &ask, &duckduckgo_light, + }; + + // Iceland + const PrepopulatedEngine* const engines_IS[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + &ecosia, + }; +@@ -465,7 +465,7 @@ const PrepopulatedEngine* const engines_IT[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -474,7 +474,7 @@ const PrepopulatedEngine* const engines_JM[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -492,7 +492,7 @@ const PrepopulatedEngine* const engines_JP[] = { + &googleen, &google, + &bing, + &yahoo_jp, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &baidu, + }; + +@@ -501,7 +501,7 @@ const PrepopulatedEngine* const engines_KE[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -529,7 +529,7 @@ const PrepopulatedEngine* const engines_KZ[] = { + &yandex_kz, + &mail_ru, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Lebanon +@@ -537,7 +537,7 @@ const PrepopulatedEngine* const engines_LB[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -545,7 +545,7 @@ const PrepopulatedEngine* const engines_LB[] = { + const PrepopulatedEngine* const engines_LI[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + &ecosia, + }; +@@ -555,7 +555,7 @@ const PrepopulatedEngine* const engines_LT[] = { + &googleen, &google, + &bing, + &yandex_ru, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + }; + +@@ -563,7 +563,7 @@ const PrepopulatedEngine* const engines_LT[] = { + const PrepopulatedEngine* const engines_LU[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + &ecosia, + }; +@@ -574,7 +574,7 @@ const PrepopulatedEngine* const engines_LV[] = { + &bing, + &yandex_ru, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Libya +@@ -583,7 +583,7 @@ const PrepopulatedEngine* const engines_LY[] = { + &bing, + &yahoo, + &yandex_tr, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Morocco +@@ -592,14 +592,14 @@ const PrepopulatedEngine* const engines_MA[] = { + &bing, + &yahoo_fr, + &yandex_com, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Monaco + const PrepopulatedEngine* const engines_MC[] = { + &googleen, &google, + &bing, +- &yahoo_fr, ++ &yahoo_fr, &duckduckgo_light, + &duckduckgo, + &qwant, + }; +@@ -619,7 +619,7 @@ const PrepopulatedEngine* const engines_ME[] = { + &bing, + &yandex_ru, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Macedonia +@@ -627,7 +627,7 @@ const PrepopulatedEngine* const engines_MK[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -636,7 +636,7 @@ const PrepopulatedEngine* const engines_MX[] = { + &googleen, &google, + &bing, + &yahoo_mx, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -645,7 +645,7 @@ const PrepopulatedEngine* const engines_MY[] = { + &googleen, &google, + &bing, + &yahoo_my, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -654,7 +654,7 @@ const PrepopulatedEngine* const engines_NI[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -663,7 +663,7 @@ const PrepopulatedEngine* const engines_NL[] = { + &googleen, &google, + &bing, + &yahoo_nl, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -672,7 +672,7 @@ const PrepopulatedEngine* const engines_NO[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -681,7 +681,7 @@ const PrepopulatedEngine* const engines_NZ[] = { + &googleen, &google, + &bing, + &yahoo_nz, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -699,7 +699,7 @@ const PrepopulatedEngine* const engines_PA[] = { + &googleen, &google, + &bing, + &yahoo_es, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -709,7 +709,7 @@ const PrepopulatedEngine* const engines_PE[] = { + &bing, + &yahoo_pe, + &ecosia, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Philippines +@@ -718,7 +718,7 @@ const PrepopulatedEngine* const engines_PH[] = { + &bing, + &yahoo_ph, + &ecosia, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Pakistan +@@ -726,7 +726,7 @@ const PrepopulatedEngine* const engines_PK[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -735,7 +735,7 @@ const PrepopulatedEngine* const engines_PL[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -744,7 +744,7 @@ const PrepopulatedEngine* const engines_PR[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -753,7 +753,7 @@ const PrepopulatedEngine* const engines_PT[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -762,7 +762,7 @@ const PrepopulatedEngine* const engines_PY[] = { + &googleen, &google, + &bing, + &yahoo_es, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -771,7 +771,7 @@ const PrepopulatedEngine* const engines_QA[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_tr, + }; + +@@ -780,7 +780,7 @@ const PrepopulatedEngine* const engines_RO[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_tr, + }; + +@@ -789,7 +789,7 @@ const PrepopulatedEngine* const engines_RS[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -799,7 +799,7 @@ const PrepopulatedEngine* const engines_RU[] = { + &google, + &duckduckgo, + &bing, +- &mail_ru, ++ &mail_ru, &duckduckgo_light, + }; + + // Rwanda +@@ -807,7 +807,7 @@ const PrepopulatedEngine* const engines_RW[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -817,7 +817,7 @@ const PrepopulatedEngine* const engines_SA[] = { + &bing, + &yahoo, + &yandex_com, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Sweden +@@ -825,7 +825,7 @@ const PrepopulatedEngine* const engines_SE[] = { + &googleen, &google, + &bing, + &yahoo_se, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_ru, + }; + +@@ -834,7 +834,7 @@ const PrepopulatedEngine* const engines_SG[] = { + &googleen, &google, + &bing, + &yahoo_sg, +- &baidu, ++ &baidu, &duckduckgo_light, + &duckduckgo, + }; + +@@ -842,7 +842,7 @@ const PrepopulatedEngine* const engines_SG[] = { + const PrepopulatedEngine* const engines_SI[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + &yandex_com, + }; +@@ -851,7 +851,7 @@ const PrepopulatedEngine* const engines_SI[] = { + const PrepopulatedEngine* const engines_SK[] = { + &googleen, &google, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yahoo, + &yandex_ru, + }; +@@ -861,7 +861,7 @@ const PrepopulatedEngine* const engines_SV[] = { + &googleen, &google, + &bing, + &yahoo_es, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -871,7 +871,7 @@ const PrepopulatedEngine* const engines_SY[] = { + &bing, + &yandex_ru, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Thailand +@@ -888,7 +888,7 @@ const PrepopulatedEngine* const engines_TN[] = { + &googleen, &google, + &bing, + &yahoo_fr, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -898,7 +898,7 @@ const PrepopulatedEngine* const engines_TR[] = { + &yandex_tr, + &yahoo_tr, + &bing, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + }; + + // Trinidad and Tobago +@@ -906,7 +906,7 @@ const PrepopulatedEngine* const engines_TT[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &yandex_com, + }; + +@@ -915,8 +915,8 @@ const PrepopulatedEngine* const engines_TW[] = { + &googleen, &google, + &yahoo_tw, + &bing, +- &baidu, +- &duckduckgo, ++ &baidu, &duckduckgo_light, ++ &duckduckgo, &duckduckgo_light, + }; + + // Tanzania +@@ -924,7 +924,7 @@ const PrepopulatedEngine* const engines_TZ[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -942,7 +942,7 @@ const PrepopulatedEngine* const engines_US[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -951,7 +951,7 @@ const PrepopulatedEngine* const engines_UY[] = { + &googleen, &google, + &bing, + &yahoo_es, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -978,8 +978,8 @@ const PrepopulatedEngine* const engines_YE[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, +- &yandex_com, ++ &duckduckgo, &duckduckgo_light, ++ &yandex_com, &duckduckgo_light, + }; + + // South Africa +@@ -987,7 +987,7 @@ const PrepopulatedEngine* const engines_ZA[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +@@ -996,7 +996,7 @@ const PrepopulatedEngine* const engines_ZW[] = { + &googleen, &google, + &bing, + &yahoo, +- &duckduckgo, ++ &duckduckgo, &duckduckgo_light, + &ecosia, + }; + +-- +2.34.1 + diff --git a/build/bromite_patches/Add-English-only-search-engine.patch b/build/bromite_patches/Add-English-only-search-engine.patch new file mode 100644 index 0000000000000000000000000000000000000000..cac3361c9ef984c32994a58f803f3ab0b1a3c641 --- /dev/null +++ b/build/bromite_patches/Add-English-only-search-engine.patch @@ -0,0 +1,1037 @@ +From 54421bb51eb370e84a9ca4e6070e831fbedbdcd8 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Mon, 11 Dec 2017 22:42:11 +0100 +Subject: [PATCH 047/192] Add English-only search engine + +Add a Google search engine that forces languages to English, +disable from all its searches RLZ and field experiments querystring parameters. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../search_engines/prepopulated_engines.json | 21 ++ + .../search_engines/search_engine_type.h | 1 + + .../template_url_prepopulate_data.cc | 216 +++++++++--------- + 3 files changed, 130 insertions(+), 108 deletions(-) + +diff --git a/components/search_engines/prepopulated_engines.json b/components/search_engines/prepopulated_engines.json +index cdfa06094cfcd..31042adbe1137 100644 +--- a/components/search_engines/prepopulated_engines.json ++++ b/components/search_engines/prepopulated_engines.json +@@ -141,6 +141,27 @@ + "id": 1 + }, + ++ "googleen": { ++ "name": "Google in English", ++ "keyword": "googleen", ++ "favicon_url": "https://www.google.com/favicon.ico", ++ "search_url": "{google:baseURL}search?q={searchTerms}&ie={inputEncoding}&hl=en", ++ "suggest_url": "{google:baseSuggestURL}search?client={google:suggestClient}&q={searchTerms}&hl=en", ++ "image_url": "{google:baseURL}searchbyimage/upload?hl=en", ++ "new_tab_url": "{google:baseURL}_/chrome/newtab?hl=en&ie={inputEncoding}", ++ "contextual_search_url": "{google:baseURL}_/contextualsearch?{google:contextualSearchVersion}{google:contextualSearchContextData}&hl=en", ++ "image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}", ++ "alternate_urls": [ ++ "{google:baseURL}?hl=en#q={searchTerms}", ++ "{google:baseURL}search?hl=en#q={searchTerms}", ++ "{google:baseURL}webhp?hl=en#q={searchTerms}", ++ "{google:baseURL}s?hl=en#q={searchTerms}", ++ "{google:baseURL}s?hl=en&q={searchTerms}" ++ ], ++ "type": "SEARCH_ENGINE_GOOGLE_EN", ++ "id": 13 ++ }, ++ + "mail_ru": { + "name": "@MAIL.RU", + "keyword": "mail.ru", +diff --git a/components/search_engines/search_engine_type.h b/components/search_engines/search_engine_type.h +index ab078967ce677..b77ce20c6b13c 100644 +--- a/components/search_engines/search_engine_type.h ++++ b/components/search_engines/search_engine_type.h +@@ -78,6 +78,7 @@ enum SearchEngineType { + SEARCH_ENGINE_STARTER_PACK_BOOKMARKS = 61, + SEARCH_ENGINE_STARTER_PACK_HISTORY = 62, + SEARCH_ENGINE_STARTER_PACK_TABS = 63, ++ SEARCH_ENGINE_GOOGLE_EN = 64, + + SEARCH_ENGINE_MAX // Bounding value needed for UMA histogram macro. + }; +diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc +index fb459bc4dfedb..565245bfe88b6 100644 +--- a/components/search_engines/template_url_prepopulate_data.cc ++++ b/components/search_engines/template_url_prepopulate_data.cc +@@ -29,7 +29,7 @@ namespace { + + // Default (for countries with no better engine set) + const PrepopulatedEngine* const engines_default[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + }; +@@ -39,7 +39,7 @@ const PrepopulatedEngine* const engines_default[] = { + // clang-format off + // United Arab Emirates + const PrepopulatedEngine* const engines_AE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -48,7 +48,7 @@ const PrepopulatedEngine* const engines_AE[] = { + + // Albania + const PrepopulatedEngine* const engines_AL[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -57,7 +57,7 @@ const PrepopulatedEngine* const engines_AL[] = { + + // Argentina + const PrepopulatedEngine* const engines_AR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_ar, + &duckduckgo, +@@ -66,7 +66,7 @@ const PrepopulatedEngine* const engines_AR[] = { + + // Austria + const PrepopulatedEngine* const engines_AT[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_at, + &duckduckgo, +@@ -75,7 +75,7 @@ const PrepopulatedEngine* const engines_AT[] = { + + // Australia + const PrepopulatedEngine* const engines_AU[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_au, + &duckduckgo, +@@ -84,7 +84,7 @@ const PrepopulatedEngine* const engines_AU[] = { + + // Bosnia and Herzegovina + const PrepopulatedEngine* const engines_BA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -93,7 +93,7 @@ const PrepopulatedEngine* const engines_BA[] = { + + // Belgium + const PrepopulatedEngine* const engines_BE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &ecosia, +@@ -102,7 +102,7 @@ const PrepopulatedEngine* const engines_BE[] = { + + // Bulgaria + const PrepopulatedEngine* const engines_BG[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -111,7 +111,7 @@ const PrepopulatedEngine* const engines_BG[] = { + + // Bahrain + const PrepopulatedEngine* const engines_BH[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -120,7 +120,7 @@ const PrepopulatedEngine* const engines_BH[] = { + + // Burundi + const PrepopulatedEngine* const engines_BI[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -129,7 +129,7 @@ const PrepopulatedEngine* const engines_BI[] = { + + // Brunei + const PrepopulatedEngine* const engines_BN[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -138,7 +138,7 @@ const PrepopulatedEngine* const engines_BN[] = { + + // Bolivia + const PrepopulatedEngine* const engines_BO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -147,7 +147,7 @@ const PrepopulatedEngine* const engines_BO[] = { + + // Brazil + const PrepopulatedEngine* const engines_BR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_br, + &duckduckgo, +@@ -156,7 +156,7 @@ const PrepopulatedEngine* const engines_BR[] = { + + // Belarus + const PrepopulatedEngine* const engines_BY[] = { +- &google, ++ &googleen, &google, + &yandex_by, + &mail_ru, + &bing, +@@ -165,7 +165,7 @@ const PrepopulatedEngine* const engines_BY[] = { + + // Belize + const PrepopulatedEngine* const engines_BZ[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -174,7 +174,7 @@ const PrepopulatedEngine* const engines_BZ[] = { + + // Canada + const PrepopulatedEngine* const engines_CA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_ca, + &duckduckgo, +@@ -183,7 +183,7 @@ const PrepopulatedEngine* const engines_CA[] = { + + // Switzerland + const PrepopulatedEngine* const engines_CH[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo_ch, +@@ -192,7 +192,7 @@ const PrepopulatedEngine* const engines_CH[] = { + + // Chile + const PrepopulatedEngine* const engines_CL[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_cl, + &duckduckgo, +@@ -203,14 +203,14 @@ const PrepopulatedEngine* const engines_CL[] = { + const PrepopulatedEngine* const engines_CN[] = { + &baidu, + &bing, +- &sogou, ++ &googleen, &sogou, + &so_360, + &google, + }; + + // Colombia + const PrepopulatedEngine* const engines_CO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_co, + &duckduckgo, +@@ -219,7 +219,7 @@ const PrepopulatedEngine* const engines_CO[] = { + + // Costa Rica + const PrepopulatedEngine* const engines_CR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -228,7 +228,7 @@ const PrepopulatedEngine* const engines_CR[] = { + + // Czech Republic + const PrepopulatedEngine* const engines_CZ[] = { +- &google, ++ &googleen, &google, + &seznam_cz, + &bing, + &yahoo, +@@ -237,7 +237,7 @@ const PrepopulatedEngine* const engines_CZ[] = { + + // Germany + const PrepopulatedEngine* const engines_DE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_de, + &duckduckgo, +@@ -246,7 +246,7 @@ const PrepopulatedEngine* const engines_DE[] = { + + // Denmark + const PrepopulatedEngine* const engines_DK[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_dk, + &duckduckgo, +@@ -255,7 +255,7 @@ const PrepopulatedEngine* const engines_DK[] = { + + // Dominican Republic + const PrepopulatedEngine* const engines_DO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -264,7 +264,7 @@ const PrepopulatedEngine* const engines_DO[] = { + + // Algeria + const PrepopulatedEngine* const engines_DZ[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fr, + &yandex_com, +@@ -273,7 +273,7 @@ const PrepopulatedEngine* const engines_DZ[] = { + + // Ecuador + const PrepopulatedEngine* const engines_EC[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &ecosia, +@@ -282,7 +282,7 @@ const PrepopulatedEngine* const engines_EC[] = { + + // Estonia + const PrepopulatedEngine* const engines_EE[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &duckduckgo, +@@ -291,7 +291,7 @@ const PrepopulatedEngine* const engines_EE[] = { + + // Egypt + const PrepopulatedEngine* const engines_EG[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &yandex_com, +@@ -300,7 +300,7 @@ const PrepopulatedEngine* const engines_EG[] = { + + // Spain + const PrepopulatedEngine* const engines_ES[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -309,7 +309,7 @@ const PrepopulatedEngine* const engines_ES[] = { + + // Finland + const PrepopulatedEngine* const engines_FI[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fi, + &duckduckgo, +@@ -318,7 +318,7 @@ const PrepopulatedEngine* const engines_FI[] = { + + // Faroe Islands + const PrepopulatedEngine* const engines_FO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_uk, + &duckduckgo, +@@ -327,7 +327,7 @@ const PrepopulatedEngine* const engines_FO[] = { + + // France + const PrepopulatedEngine* const engines_FR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fr, + &qwant, +@@ -336,7 +336,7 @@ const PrepopulatedEngine* const engines_FR[] = { + + // United Kingdom + const PrepopulatedEngine* const engines_GB[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_uk, + &duckduckgo, +@@ -345,7 +345,7 @@ const PrepopulatedEngine* const engines_GB[] = { + + // Greece + const PrepopulatedEngine* const engines_GR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -354,7 +354,7 @@ const PrepopulatedEngine* const engines_GR[] = { + + // Guatemala + const PrepopulatedEngine* const engines_GT[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -363,7 +363,7 @@ const PrepopulatedEngine* const engines_GT[] = { + + // Hong Kong + const PrepopulatedEngine* const engines_HK[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_hk, + &baidu, +@@ -372,7 +372,7 @@ const PrepopulatedEngine* const engines_HK[] = { + + // Honduras + const PrepopulatedEngine* const engines_HN[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -381,7 +381,7 @@ const PrepopulatedEngine* const engines_HN[] = { + + // Croatia + const PrepopulatedEngine* const engines_HR[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -390,7 +390,7 @@ const PrepopulatedEngine* const engines_HR[] = { + + // Hungary + const PrepopulatedEngine* const engines_HU[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -399,7 +399,7 @@ const PrepopulatedEngine* const engines_HU[] = { + + // Indonesia + const PrepopulatedEngine* const engines_ID[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_id, + &duckduckgo, +@@ -408,7 +408,7 @@ const PrepopulatedEngine* const engines_ID[] = { + + // Ireland + const PrepopulatedEngine* const engines_IE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_uk, + &duckduckgo, +@@ -417,7 +417,7 @@ const PrepopulatedEngine* const engines_IE[] = { + + // Israel + const PrepopulatedEngine* const engines_IL[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &yahoo, +@@ -426,7 +426,7 @@ const PrepopulatedEngine* const engines_IL[] = { + + // India + const PrepopulatedEngine* const engines_IN[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_in, + &duckduckgo, +@@ -435,7 +435,7 @@ const PrepopulatedEngine* const engines_IN[] = { + + // Iraq + const PrepopulatedEngine* const engines_IQ[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &yandex_tr, +@@ -444,7 +444,7 @@ const PrepopulatedEngine* const engines_IQ[] = { + + // Iran + const PrepopulatedEngine* const engines_IR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -453,7 +453,7 @@ const PrepopulatedEngine* const engines_IR[] = { + + // Iceland + const PrepopulatedEngine* const engines_IS[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -462,7 +462,7 @@ const PrepopulatedEngine* const engines_IS[] = { + + // Italy + const PrepopulatedEngine* const engines_IT[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -471,7 +471,7 @@ const PrepopulatedEngine* const engines_IT[] = { + + // Jamaica + const PrepopulatedEngine* const engines_JM[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -480,7 +480,7 @@ const PrepopulatedEngine* const engines_JM[] = { + + // Jordan + const PrepopulatedEngine* const engines_JO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &petal_search, +@@ -489,7 +489,7 @@ const PrepopulatedEngine* const engines_JO[] = { + + // Japan + const PrepopulatedEngine* const engines_JP[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_jp, + &duckduckgo, +@@ -498,7 +498,7 @@ const PrepopulatedEngine* const engines_JP[] = { + + // Kenya + const PrepopulatedEngine* const engines_KE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -507,7 +507,7 @@ const PrepopulatedEngine* const engines_KE[] = { + + // South Korea + const PrepopulatedEngine* const engines_KR[] = { +- &google, ++ &googleen, &google, + &naver, + &bing, + &daum, +@@ -516,7 +516,7 @@ const PrepopulatedEngine* const engines_KR[] = { + + // Kuwait + const PrepopulatedEngine* const engines_KW[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -525,7 +525,7 @@ const PrepopulatedEngine* const engines_KW[] = { + + // Kazakhstan + const PrepopulatedEngine* const engines_KZ[] = { +- &google, ++ &googleen, &google, + &yandex_kz, + &mail_ru, + &bing, +@@ -534,7 +534,7 @@ const PrepopulatedEngine* const engines_KZ[] = { + + // Lebanon + const PrepopulatedEngine* const engines_LB[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -543,7 +543,7 @@ const PrepopulatedEngine* const engines_LB[] = { + + // Liechtenstein + const PrepopulatedEngine* const engines_LI[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -552,7 +552,7 @@ const PrepopulatedEngine* const engines_LI[] = { + + // Lithuania + const PrepopulatedEngine* const engines_LT[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &duckduckgo, +@@ -561,7 +561,7 @@ const PrepopulatedEngine* const engines_LT[] = { + + // Luxembourg + const PrepopulatedEngine* const engines_LU[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -570,7 +570,7 @@ const PrepopulatedEngine* const engines_LU[] = { + + // Latvia + const PrepopulatedEngine* const engines_LV[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &yahoo, +@@ -579,7 +579,7 @@ const PrepopulatedEngine* const engines_LV[] = { + + // Libya + const PrepopulatedEngine* const engines_LY[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &yandex_tr, +@@ -588,7 +588,7 @@ const PrepopulatedEngine* const engines_LY[] = { + + // Morocco + const PrepopulatedEngine* const engines_MA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fr, + &yandex_com, +@@ -597,7 +597,7 @@ const PrepopulatedEngine* const engines_MA[] = { + + // Monaco + const PrepopulatedEngine* const engines_MC[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fr, + &duckduckgo, +@@ -606,7 +606,7 @@ const PrepopulatedEngine* const engines_MC[] = { + + // Moldova + const PrepopulatedEngine* const engines_MD[] = { +- &google, ++ &googleen, &google, + &yandex_ru, + &bing, + &mail_ru, +@@ -615,7 +615,7 @@ const PrepopulatedEngine* const engines_MD[] = { + + // Montenegro + const PrepopulatedEngine* const engines_ME[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &yahoo, +@@ -624,7 +624,7 @@ const PrepopulatedEngine* const engines_ME[] = { + + // Macedonia + const PrepopulatedEngine* const engines_MK[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -633,7 +633,7 @@ const PrepopulatedEngine* const engines_MK[] = { + + // Mexico + const PrepopulatedEngine* const engines_MX[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_mx, + &duckduckgo, +@@ -642,7 +642,7 @@ const PrepopulatedEngine* const engines_MX[] = { + + // Malaysia + const PrepopulatedEngine* const engines_MY[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_my, + &duckduckgo, +@@ -651,7 +651,7 @@ const PrepopulatedEngine* const engines_MY[] = { + + // Nicaragua + const PrepopulatedEngine* const engines_NI[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -660,7 +660,7 @@ const PrepopulatedEngine* const engines_NI[] = { + + // Netherlands + const PrepopulatedEngine* const engines_NL[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_nl, + &duckduckgo, +@@ -669,7 +669,7 @@ const PrepopulatedEngine* const engines_NL[] = { + + // Norway + const PrepopulatedEngine* const engines_NO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -678,7 +678,7 @@ const PrepopulatedEngine* const engines_NO[] = { + + // New Zealand + const PrepopulatedEngine* const engines_NZ[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_nz, + &duckduckgo, +@@ -687,7 +687,7 @@ const PrepopulatedEngine* const engines_NZ[] = { + + // Oman + const PrepopulatedEngine* const engines_OM[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &petal_search, +@@ -696,7 +696,7 @@ const PrepopulatedEngine* const engines_OM[] = { + + // Panama + const PrepopulatedEngine* const engines_PA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -705,7 +705,7 @@ const PrepopulatedEngine* const engines_PA[] = { + + // Peru + const PrepopulatedEngine* const engines_PE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_pe, + &ecosia, +@@ -714,7 +714,7 @@ const PrepopulatedEngine* const engines_PE[] = { + + // Philippines + const PrepopulatedEngine* const engines_PH[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_ph, + &ecosia, +@@ -723,7 +723,7 @@ const PrepopulatedEngine* const engines_PH[] = { + + // Pakistan + const PrepopulatedEngine* const engines_PK[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -732,7 +732,7 @@ const PrepopulatedEngine* const engines_PK[] = { + + // Poland + const PrepopulatedEngine* const engines_PL[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -741,7 +741,7 @@ const PrepopulatedEngine* const engines_PL[] = { + + // Puerto Rico + const PrepopulatedEngine* const engines_PR[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -750,7 +750,7 @@ const PrepopulatedEngine* const engines_PR[] = { + + // Portugal + const PrepopulatedEngine* const engines_PT[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -759,7 +759,7 @@ const PrepopulatedEngine* const engines_PT[] = { + + // Paraguay + const PrepopulatedEngine* const engines_PY[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -768,7 +768,7 @@ const PrepopulatedEngine* const engines_PY[] = { + + // Qatar + const PrepopulatedEngine* const engines_QA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -777,7 +777,7 @@ const PrepopulatedEngine* const engines_QA[] = { + + // Romania + const PrepopulatedEngine* const engines_RO[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -786,7 +786,7 @@ const PrepopulatedEngine* const engines_RO[] = { + + // Serbia + const PrepopulatedEngine* const engines_RS[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -795,7 +795,7 @@ const PrepopulatedEngine* const engines_RS[] = { + + // Russia + const PrepopulatedEngine* const engines_RU[] = { +- &yandex_ru, ++ &googleen, &yandex_ru, + &google, + &duckduckgo, + &bing, +@@ -804,7 +804,7 @@ const PrepopulatedEngine* const engines_RU[] = { + + // Rwanda + const PrepopulatedEngine* const engines_RW[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -813,7 +813,7 @@ const PrepopulatedEngine* const engines_RW[] = { + + // Saudi Arabia + const PrepopulatedEngine* const engines_SA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &yandex_com, +@@ -822,7 +822,7 @@ const PrepopulatedEngine* const engines_SA[] = { + + // Sweden + const PrepopulatedEngine* const engines_SE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_se, + &duckduckgo, +@@ -831,7 +831,7 @@ const PrepopulatedEngine* const engines_SE[] = { + + // Singapore + const PrepopulatedEngine* const engines_SG[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_sg, + &baidu, +@@ -840,7 +840,7 @@ const PrepopulatedEngine* const engines_SG[] = { + + // Slovenia + const PrepopulatedEngine* const engines_SI[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -849,7 +849,7 @@ const PrepopulatedEngine* const engines_SI[] = { + + // Slovakia + const PrepopulatedEngine* const engines_SK[] = { +- &google, ++ &googleen, &google, + &bing, + &duckduckgo, + &yahoo, +@@ -858,7 +858,7 @@ const PrepopulatedEngine* const engines_SK[] = { + + // El Salvador + const PrepopulatedEngine* const engines_SV[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -867,7 +867,7 @@ const PrepopulatedEngine* const engines_SV[] = { + + // Syria + const PrepopulatedEngine* const engines_SY[] = { +- &google, ++ &googleen, &google, + &bing, + &yandex_ru, + &yahoo, +@@ -876,7 +876,7 @@ const PrepopulatedEngine* const engines_SY[] = { + + // Thailand + const PrepopulatedEngine* const engines_TH[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_th, + &naver, +@@ -885,7 +885,7 @@ const PrepopulatedEngine* const engines_TH[] = { + + // Tunisia + const PrepopulatedEngine* const engines_TN[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_fr, + &duckduckgo, +@@ -894,7 +894,7 @@ const PrepopulatedEngine* const engines_TN[] = { + + // Turkey + const PrepopulatedEngine* const engines_TR[] = { +- &google, ++ &googleen, &google, + &yandex_tr, + &yahoo_tr, + &bing, +@@ -903,7 +903,7 @@ const PrepopulatedEngine* const engines_TR[] = { + + // Trinidad and Tobago + const PrepopulatedEngine* const engines_TT[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -912,7 +912,7 @@ const PrepopulatedEngine* const engines_TT[] = { + + // Taiwan + const PrepopulatedEngine* const engines_TW[] = { +- &google, ++ &googleen, &google, + &yahoo_tw, + &bing, + &baidu, +@@ -921,7 +921,7 @@ const PrepopulatedEngine* const engines_TW[] = { + + // Tanzania + const PrepopulatedEngine* const engines_TZ[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -930,7 +930,7 @@ const PrepopulatedEngine* const engines_TZ[] = { + + // Ukraine + const PrepopulatedEngine* const engines_UA[] = { +- &google, ++ &googleen, &google, + &yandex_ru, + &bing, + &duckduckgo, +@@ -939,7 +939,7 @@ const PrepopulatedEngine* const engines_UA[] = { + + // United States + const PrepopulatedEngine* const engines_US[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -948,7 +948,7 @@ const PrepopulatedEngine* const engines_US[] = { + + // Uruguay + const PrepopulatedEngine* const engines_UY[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -957,7 +957,7 @@ const PrepopulatedEngine* const engines_UY[] = { + + // Venezuela + const PrepopulatedEngine* const engines_VE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo_es, + &duckduckgo, +@@ -966,7 +966,7 @@ const PrepopulatedEngine* const engines_VE[] = { + + // Vietnam + const PrepopulatedEngine* const engines_VN[] = { +- &google, ++ &googleen, &google, + &coccoc, + &bing, + &yahoo, +@@ -975,7 +975,7 @@ const PrepopulatedEngine* const engines_VN[] = { + + // Yemen + const PrepopulatedEngine* const engines_YE[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -984,7 +984,7 @@ const PrepopulatedEngine* const engines_YE[] = { + + // South Africa + const PrepopulatedEngine* const engines_ZA[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +@@ -993,7 +993,7 @@ const PrepopulatedEngine* const engines_ZA[] = { + + // Zimbabwe + const PrepopulatedEngine* const engines_ZW[] = { +- &google, ++ &googleen, &google, + &bing, + &yahoo, + &duckduckgo, +-- +2.34.1 + diff --git a/build/bromite_patches/Add-IsCleartextPermitted-flag.patch b/build/bromite_patches/Add-IsCleartextPermitted-flag.patch new file mode 100644 index 0000000000000000000000000000000000000000..6b234700389c67eb11029e0daefbc5785fc3d207 --- /dev/null +++ b/build/bromite_patches/Add-IsCleartextPermitted-flag.patch @@ -0,0 +1,106 @@ +From ac498a86a14754cf2372b4f1182205ed79d4ea0d Mon Sep 17 00:00:00 2001 +From: uazo +Date: Mon, 26 Apr 2021 15:04:11 +0000 +Subject: [PATCH 131/192] Add IsCleartextPermitted flag + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 4 ++++ + chrome/browser/flag_descriptions.cc | 4 ++++ + chrome/browser/flag_descriptions.h | 3 +++ + net/base/features.cc | 4 ++++ + net/base/features.h | 2 ++ + net/url_request/url_request_http_job.cc | 4 ++++ + 6 files changed, 21 insertions(+) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index a30a18f00179e..cde019cdb2851 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -9516,6 +9516,10 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kForceOffTextAutosizingName, + flag_descriptions::kForceOffTextAutosizingDescription, kOsAndroid, + FEATURE_VALUE_TYPE(features::kForceOffTextAutosizing)}, ++ {"cleartext-permitted", ++ flag_descriptions::kIsCleartextPermittedName, ++ flag_descriptions::kIsCleartextPermittedDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE(net::features::kIsCleartextPermitted)}, + #endif + + #if BUILDFLAG(IS_ANDROID) +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 4032bab43c722..12e206fab161e 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -1947,6 +1947,10 @@ const char kHttpsUpgradesDescription[] = + "Enable automatically upgrading all top-level navigations to HTTPS with " + "fast fallback to HTTP."; + ++const char kIsCleartextPermittedName[] = "Allow cleartext traffic"; ++const char kIsCleartextPermittedDescription[] = ++ "Allow insecure connections over HTTP"; ++ + const char kIgnoreGpuBlocklistName[] = "Override software rendering list"; + const char kIgnoreGpuBlocklistDescription[] = + "Overrides the built-in software rendering list and enables " +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index e95beecefb599..8e7efeaab8bbb 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -1090,6 +1090,9 @@ extern const char kHideIncognitoMediaMetadataDescription[]; + extern const char kHttpsOnlyModeName[]; + extern const char kHttpsOnlyModeDescription[]; + ++extern const char kIsCleartextPermittedName[]; ++extern const char kIsCleartextPermittedDescription[]; ++ + extern const char kHttpsFirstModeV2Name[]; + extern const char kHttpsFirstModeV2Description[]; + +diff --git a/net/base/features.cc b/net/base/features.cc +index 8d37774c60d90..41dfa7bc63480 100644 +--- a/net/base/features.cc ++++ b/net/base/features.cc +@@ -146,6 +146,10 @@ BASE_FEATURE(kHttpCacheKeyingExperimentControlGroup, + "HttpCacheKeyingExperimentControlGroup", + base::FEATURE_DISABLED_BY_DEFAULT); + ++BASE_FEATURE(kIsCleartextPermitted, ++ "IsCleartextPermitted", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + BASE_FEATURE(kTLS13KeyUpdate, + "TLS13KeyUpdate", + base::FEATURE_DISABLED_BY_DEFAULT); +diff --git a/net/base/features.h b/net/base/features.h +index 31e74ad0d6f2f..47a793b517622 100644 +--- a/net/base/features.h ++++ b/net/base/features.h +@@ -30,6 +30,8 @@ NET_EXPORT BASE_DECLARE_FEATURE(kAvoidH2Reprioritization); + // origin requests are restricted to contain at most the source origin. + NET_EXPORT BASE_DECLARE_FEATURE(kCapReferrerToOriginOnCrossOrigin); + ++NET_EXPORT BASE_DECLARE_FEATURE(kIsCleartextPermitted); ++ + // Support for altering the parameters used for DNS transaction timeout. See + // ResolveContext::SecureTransactionTimeout(). + NET_EXPORT BASE_DECLARE_FEATURE(kDnsTransactionDynamicTimeouts); +diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc +index 61b8138de0f00..97eacf1fb40ef 100644 +--- a/net/url_request/url_request_http_job.cc ++++ b/net/url_request/url_request_http_job.cc +@@ -213,6 +213,10 @@ std::unique_ptr URLRequestHttpJob::Create(URLRequest* request) { + } + + #if BUILDFLAG(IS_ANDROID) ++ if (base::FeatureList::IsEnabled(net::features::kIsCleartextPermitted) == false) { ++ return std::make_unique(request, ++ ERR_CLEARTEXT_NOT_PERMITTED); ++ } + // Check whether the app allows cleartext traffic to this host, and return + // ERR_CLEARTEXT_NOT_PERMITTED if not. + if (request->context()->check_cleartext_permitted() && +-- +2.34.1 + diff --git a/build/bromite_patches/Add-a-proxy-configuration-page.patch b/build/bromite_patches/Add-a-proxy-configuration-page.patch new file mode 100644 index 0000000000000000000000000000000000000000..672253676b2cf3d751dcc60037342b579dd52c1e --- /dev/null +++ b/build/bromite_patches/Add-a-proxy-configuration-page.patch @@ -0,0 +1,1448 @@ +From 57be1b5a2c42f9530d8acfc6fb091392773c767c Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Thu, 29 Mar 2018 00:43:32 +0200 +Subject: [PATCH 066/192] Add a proxy configuration page + +Accessible from proxy settings and chrome://proxy +Allows to use a PAC script URL, automatic configuration and explicit proxy settings. +Offer auto-complete for the proxy page URL. +Store proxy settings in LocalState instead of Profile, so that proxy is used for SimpleURLLoaders as well; +this implementation is the same as the one in ChromeOS which gets proxy information from the LocalState +for the system network context; this is strictly not correct on Android because the network context is +never connected to any user profile and only gets proxy information from the system. +Existing settings on Profile are migrated to LocalState on startup. + +Credits to @uazo for the LocalState integration. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/java/res/values/values.xml | 3 + + .../java/res/xml/privacy_preferences.xml | 4 + + .../privacy/settings/PrivacySettings.java | 1 + + .../chrome_autocomplete_provider_client.cc | 2 + + chrome/browser/browser_resources.grd | 4 +- + .../extensions/api/proxy/proxy_api_helpers.cc | 2 +- + chrome/browser/net/proxy_service_factory.cc | 24 +- + chrome/browser/net/proxy_service_factory.h | 3 + + chrome/browser/prefs/browser_prefs.cc | 4 + + .../prefs/chrome_command_line_pref_store.cc | 2 +- + chrome/browser/resources/proxy_config.css | 61 +++ + chrome/browser/resources/proxy_config.html | 77 ++++ + chrome/browser/resources/proxy_config.js | 278 +++++++++++++ + chrome/browser/ui/BUILD.gn | 2 + + .../webui/chrome_web_ui_controller_factory.cc | 3 + + chrome/browser/ui/webui/proxy_config_ui.cc | 393 ++++++++++++++++++ + chrome/browser/ui/webui/proxy_config_ui.h | 31 ++ + chrome/common/webui_url_constants.cc | 4 + + chrome/common/webui_url_constants.h | 2 + + .../pref_proxy_config_tracker_impl.cc | 5 +- + .../proxy_config/proxy_config_dictionary.cc | 30 +- + .../proxy_config/proxy_config_dictionary.h | 7 +- + .../proxy_config/proxy_policy_handler.cc | 2 +- + net/proxy_resolution/proxy_config.cc | 52 ++- + net/proxy_resolution/proxy_config.h | 3 + + 25 files changed, 981 insertions(+), 18 deletions(-) + create mode 100644 chrome/browser/resources/proxy_config.css + create mode 100644 chrome/browser/resources/proxy_config.html + create mode 100644 chrome/browser/resources/proxy_config.js + create mode 100644 chrome/browser/ui/webui/proxy_config_ui.cc + create mode 100644 chrome/browser/ui/webui/proxy_config_ui.h + +diff --git a/chrome/android/java/res/values/values.xml b/chrome/android/java/res/values/values.xml +index ce68e038c7d31..9a30818faacaa 100644 +--- a/chrome/android/java/res/values/values.xml ++++ b/chrome/android/java/res/values/values.xml +@@ -27,6 +27,9 @@ found in the LICENSE file. + + true + ++ Proxy configuration ++ chrome://proxy ++ + + 1200 + 200 +diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml +index 4c71a1e2ba2ab..68b5042d47935 100644 +--- a/chrome/android/java/res/xml/privacy_preferences.xml ++++ b/chrome/android/java/res/xml/privacy_preferences.xml +@@ -9,6 +9,10 @@ found in the LICENSE file. + + ++ + + + ++ ++ ++ + + + + +- + + + +diff --git a/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc b/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc +index 20f57baa728cd..2712a2dd618de 100644 +--- a/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc ++++ b/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc +@@ -388,7 +388,7 @@ absl::optional CreateProxyConfigDict( + return absl::nullopt; + } + return ProxyConfigDictionary::CreateFixedServers(proxy_rules_string, +- bypass_list); ++ bypass_list, /*reverse_bypass*/false); + } + case ProxyPrefs::MODE_SYSTEM: + return ProxyConfigDictionary::CreateSystem(); +diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc +index 281a393f03936..5dedebc3a9f16 100644 +--- a/chrome/browser/net/proxy_service_factory.cc ++++ b/chrome/browser/net/proxy_service_factory.cc +@@ -6,6 +6,7 @@ + + #include + ++#include "base/logging.h" + #include "base/task/single_thread_task_runner.h" + #include "build/build_config.h" + #include "build/chromeos_buildflags.h" +@@ -13,6 +14,9 @@ + #include "content/public/browser/browser_task_traits.h" + #include "content/public/browser/browser_thread.h" + #include "net/proxy_resolution/proxy_config_service.h" ++#include "components/proxy_config/proxy_config_pref_names.h" ++#include "components/prefs/pref_service.h" ++#include "components/prefs/pref_registry_simple.h" + + #if BUILDFLAG(IS_CHROMEOS_ASH) + #include "chromeos/ash/components/network/proxy/proxy_config_service_impl.h" +@@ -70,7 +74,20 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile( + return std::make_unique( + profile_prefs, local_state_prefs, nullptr); + #else +- return std::make_unique(profile_prefs, nullptr); ++ // Migrate from profile_prefs to local_state_prefs ++ if (local_state_prefs->GetBoolean("proxy_migrated") == false) { ++ const base::Value::Dict& dict = ++ profile_prefs->GetDict(proxy_config::prefs::kProxy); ++ ++ LOG(INFO) << "CreatePrefProxyConfigTrackerOfProfile: Migration from profile to local state"; ++ ++ const base::Value /*ProxyConfigDictionary*/ proxy_dict(dict.Clone()); ++ local_state_prefs->Set(proxy_config::prefs::kProxy, proxy_dict); ++ ++ local_state_prefs->SetBoolean("proxy_migrated", true); ++ local_state_prefs->CommitPendingWrite(); ++ } ++ return std::make_unique(local_state_prefs, nullptr); + #endif // BUILDFLAG(IS_CHROMEOS_ASH) + } + +@@ -86,3 +103,8 @@ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState( + nullptr); + #endif // BUILDFLAG(IS_CHROMEOS_ASH) + } ++ ++// static ++void ProxyServiceFactory::RegisterPrefs(PrefRegistrySimple* registry) { ++ registry->RegisterBooleanPref("proxy_migrated", false); ++} +diff --git a/chrome/browser/net/proxy_service_factory.h b/chrome/browser/net/proxy_service_factory.h +index a5074896c5ebd..9d2060d09fca2 100644 +--- a/chrome/browser/net/proxy_service_factory.h ++++ b/chrome/browser/net/proxy_service_factory.h +@@ -6,6 +6,7 @@ + #define CHROME_BROWSER_NET_PROXY_SERVICE_FACTORY_H_ + + #include ++#include "components/prefs/pref_registry_simple.h" + + class PrefProxyConfigTracker; + class PrefService; +@@ -35,6 +36,8 @@ class ProxyServiceFactory { + CreatePrefProxyConfigTrackerOfProfile(PrefService* profile_prefs, + PrefService* local_state_prefs); + ++ static void RegisterPrefs(PrefRegistrySimple* registry); ++ + // Creates a PrefProxyConfigTracker that tracks local state only. This tracker + // should be used for the system request context and the signin screen + // (ChromeOS only). +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index 8f75f154b981f..17436d6fab77f 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -177,6 +177,8 @@ + #include "rlz/buildflags/buildflags.h" + #include "third_party/abseil-cpp/absl/types/optional.h" + ++#include "chrome/browser/net/proxy_service_factory.h" ++ + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) + #include "chrome/browser/background/background_mode_manager.h" + #endif +@@ -1410,6 +1412,8 @@ void RegisterLocalState(PrefRegistrySimple* registry) { + component_updater::RegisterPrefs(registry); + domain_reliability::RegisterPrefs(registry); + embedder_support::OriginTrialPrefs::RegisterPrefs(registry); ++ ProxyServiceFactory::RegisterPrefs(registry); ++ + enterprise_reporting::RegisterLocalStatePrefs(registry); + ExternalProtocolHandler::RegisterPrefs(registry); + flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry); +diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc +index dd4824764ecf6..03019827319af 100644 +--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc ++++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc +@@ -156,7 +156,7 @@ void ChromeCommandLinePrefStore::ApplyProxyMode() { + command_line()->GetSwitchValueASCII(switches::kProxyBypassList); + SetValue(proxy_config::prefs::kProxy, + base::Value(ProxyConfigDictionary::CreateFixedServers( +- proxy_server, bypass_list)), ++ proxy_server, bypass_list, false)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + } +diff --git a/chrome/browser/resources/proxy_config.css b/chrome/browser/resources/proxy_config.css +new file mode 100644 +index 0000000000000..0ce4fc031fa12 +--- /dev/null ++++ b/chrome/browser/resources/proxy_config.css +@@ -0,0 +1,61 @@ ++/* Copyright (c) 2013 The Chromium Authors. All rights reserved. ++ * Use of this source code is governed by a BSD-style license that can be ++ * found in the LICENSE file. ++ */ ++ ++body { ++ font-size: 80%; ++ margin: 1em; ++} ++ ++#main-container { ++ max-width: 60em; ++ margin-left: auto; ++ margin-right: auto; ++} ++ ++button { ++ display: block; ++ font-size: 110%; ++ font-weight: bold; ++ margin: 10px auto; ++ padding: 1em; ++ width: 15em; ++} ++ ++h2 { ++ color: #546E7A; ++ font-weight: normal; ++ font-size: 170%; ++ margin-bottom: 1.5em; ++} ++ ++.radio-button-div { ++ margin: 7px auto; ++} ++ ++.warning { ++ color: red; ++ font-size: 90%; ++} ++ ++.section-container { ++ margin-top: 2em; ++} ++ ++#file-path-logging, ++#file-path-stopped { ++ font-family: monospace; ++} ++ ++.outline-box { ++ margin-top: 2em; ++ border: 1px solid #ababab; ++ padding: 0.5em; ++ line-height: 1.5em; ++} ++ ++textarea { ++ width: 95%; ++ height: 4em; ++} +diff --git a/chrome/browser/resources/proxy_config.html b/chrome/browser/resources/proxy_config.html +new file mode 100644 +index 0000000000000..71df46a98a601 +--- /dev/null ++++ b/chrome/browser/resources/proxy_config.html +@@ -0,0 +1,77 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++Proxy configuration ++ ++ ++
++ ++
++

Proxy configuration

++ Loading... ++
++ ++ ++ ++ ++
++ ++ +diff --git a/chrome/browser/resources/proxy_config.js b/chrome/browser/resources/proxy_config.js +new file mode 100644 +index 0000000000000..7f92b18dfff49 +--- /dev/null ++++ b/chrome/browser/resources/proxy_config.js +@@ -0,0 +1,278 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++import {$} from 'chrome://resources/js/util_ts.js'; ++ ++/** ++ * Main entry point called once the page has loaded. ++ */ ++function onLoad() { ++ ProxyConfigView.getInstance(); ++} ++ ++document.addEventListener('DOMContentLoaded', onLoad); ++ ++/** ++ * This class handles the presentation of the proxy-config view. Used as a ++ * singleton. ++ */ ++var ProxyConfigView = (function() { ++ 'use strict'; ++ ++ // -------------------------------------------------------------------------- ++ ++ var kIdStateDivUninitialized = 'state-pending'; ++ var kIdStateDivMain = 'state-main'; ++ var kIdApplyButton = 'apply'; ++ var kIdResetButton = 'reset'; ++ var kIdClearButton = 'clear'; ++ ++ var kIdModeEmpty = 'empty'; ++ var kIdModeDirect = 'direct'; ++ var kIdModeAutoDetect = 'auto-detect'; ++ var kIdModeUsePacURL = 'use-pac-url'; ++ ++ var kIdModeUseSingleList = 'use-single-list'; ++ var kIdModeUseListPerScheme = 'use-list-per-scheme'; ++ ++ var kIdPacURL = 'pac-url'; ++ var kIdPacMandatory = 'pac-mandatory'; ++ var kIdBypassRules = 'bypass-rules'; ++ var kIdReverseBypass = 'reverse-bypass'; ++ var kIdSingleProxies = 'single-proxies'; ++ var kIdHttpProxies = 'http-proxies'; ++ var kIdHttpsProxies = 'https-proxies'; ++ var kIdFtpProxies = 'ftp-proxies'; ++ var kIdFallbackProxies = 'fallback-proxies'; ++ ++ /** ++ * Adds a {@code getInstance} static method that always return the same ++ * instance object. ++ * @param {!Function} ctor The constructor for the class to add the static ++ * method to. ++ */ ++ function addSingletonGetter(ctor) { ++ ctor.getInstance = function() { ++ return ctor.instance_ || (ctor.instance_ = new ctor()); ++ }; ++ } ++ ++ /** ++ * @constructor ++ */ ++ function ProxyConfigView() { ++ this.currentConfig = null; ++ ++ $(kIdResetButton).onclick = this.onReset_.bind(this); ++ $(kIdApplyButton).onclick = this.onApply_.bind(this); ++ $(kIdClearButton).onclick = this.onClear_.bind(this); ++ ++ // Tell ProxyConfigMessageHandler to notify the UI of future state changes ++ // from this point on. ++ chrome.send('enableNotifyUIWithState'); ++ } ++ ++ addSingletonGetter(ProxyConfigView); ++ window.ProxyConfigView = ProxyConfigView; ++ ++ ProxyConfigView.prototype = { ++ /** ++ * Updates the UI to reflect the current state. The state transitions are ++ * sent by the browser controller (ProxyConfigMessageHandler): ++ * ++ * * PENDING - This is the initial state when proxy configuration is opened ++ * for the first time, or there was an error during initialization. ++ * This state is short-lived and likely not observed; will ++ * immediately transition to AVAILABLE). ++ * ++ * * AVAILABLE - The reported proxy configuration is active; this state is entered ++ * on first page load (or right after PENDING if configuration was not ++ * available on page load) and every time some configuration change was applied. ++ * It can transition to either AVAILABLE or UNSET. ++ * ++ * * UNSET - Proxy configuration is reported to be currently not set. ++ * ++ */ ++ onProxyConfigChanged: function(state) { ++ // may happen only on first load; leave the loading page as another update is expected ++ // when proxy configuration has finished loading ++ if (state.pending) { ++ $(kIdStateDivMain).hidden = true; ++ $(kIdStateDivUninitialized).hidden = false; ++ return; ++ } ++ ++ if (!state.hasOwnProperty('config')) { ++ // configuration has been unset, use an empty one ++ this.eraseCurrentConfig_(); ++ } else { ++ // save the configuration as current and reset all controls to it ++ this.currentConfig = state.config; ++ } ++ ++ this.renderConfig_(); ++ ++ this.toggleButtons_(false); ++ $(kIdStateDivUninitialized).hidden = true; ++ $(kIdStateDivMain).hidden = false; ++ }, ++ ++ /** ++ * Set current configuration to an empty (default) one. ++ */ ++ eraseCurrentConfig_: function() { ++ this.currentConfig = { ++ "auto_detect": false, ++ "pending": false, ++ "rules": { ++ "bypass_rules": "", ++ "reverse_bypass": false, ++ "type": "none" ++ } ++ }; ++ }, ++ ++ /** ++ * Serialize the user-selected configuration in an object. ++ */ ++ serializeConfig_: function() { ++ if ($(kIdModeEmpty).checked) { ++ return { ++ "auto_detect": false, ++ "rules": { ++ "type": "none" ++ } ++ }; ++ } else if ($(kIdModeDirect).checked) { ++ return { ++ "auto_detect": false, ++ "rules": { ++ "type": "direct" ++ } ++ }; ++ } else if ($(kIdModeAutoDetect).checked) { ++ return { ++ "auto_detect": true ++ }; ++ } else if ($(kIdModeUsePacURL).checked) { ++ return { ++ "auto_detect": false, ++ "pac_url": $(kIdPacURL).value.trim(), ++ "pac_mandatory": $(kIdPacMandatory).checked, ++ "rules": {} ++ }; ++ } else if ($(kIdModeUseListPerScheme).checked || $(kIdModeUseSingleList).checked) { ++ var config = { ++ "auto_detect": false, ++ "rules": { ++ "bypass_rules": $(kIdBypassRules).value.trim(), ++ "reverse_bypass": $(kIdReverseBypass).checked, ++ "type": "list" ++ } ++ }; ++ ++ if ($(kIdModeUseListPerScheme).checked) { ++ config.rules.type = "list_per_scheme"; ++ ++ config.rules.proxies_for_http = $(kIdHttpProxies).value.trim(); ++ config.rules.proxies_for_https = $(kIdHttpsProxies).value.trim(); ++ config.rules.proxies_for_ftp = $(kIdFtpProxies).value.trim(); ++ config.rules.fallback_proxies = $(kIdFallbackProxies).value.trim(); ++ } else { ++ config.rules.single_proxies = $(kIdSingleProxies).value.trim(); ++ } ++ ++ return config; ++ } ++ ++ throw new Error('unexpected mode'); ++ }, ++ ++ /** ++ * Updates the UI to display the current proxy configuration. ++ */ ++ renderConfig_: function() { ++ if (this.currentConfig.auto_detect) { ++ $(kIdModeAutoDetect).checked = true; ++ } else if (this.currentConfig.hasOwnProperty('pac_url')) { ++ $(kIdPacURL).value = this.currentConfig.pac_url; ++ $(kIdPacMandatory).checked = this.currentConfig.pac_mandatory; ++ $(kIdModeUsePacURL).checked = true; ++ } else if (this.currentConfig.rules.type == "none") { ++ $(kIdModeEmpty).checked = true; ++ } else if (this.currentConfig.rules.type == "direct") { ++ $(kIdModeDirect).checked = true; ++ } else { ++ $(kIdBypassRules).value = this.currentConfig.rules.bypass_rules; ++ $(kIdReverseBypass).checked = this.currentConfig.rules.reverse_bypass; ++ ++ switch (this.currentConfig.rules.type) { ++ case "list": ++ $(kIdModeUseSingleList).checked = true; ++ $(kIdSingleProxies).value = this.currentConfig.rules.single_proxies; ++ break; ++ case "list_per_scheme": ++ $(kIdModeUseListPerScheme).checked = true; ++ $(kIdHttpProxies).value = this.currentConfig.rules.proxies_for_http; ++ $(kIdHttpsProxies).value = this.currentConfig.rules.proxies_for_https; ++ $(kIdFtpProxies).value = this.currentConfig.rules.proxies_for_ftp; ++ $(kIdFallbackProxies).value = this.currentConfig.rules.fallback_proxies; ++ break; ++ } ++ } ++ }, ++ ++ /** ++ * Apply the configuration currently displayed. ++ */ ++ onApply_: function() { ++ var config = this.serializeConfig_(); ++ ++ // disable buttons; will be enabled back when UI receives a state update ++ this.toggleButtons_(true); ++ chrome.send('apply', [config]); ++ }, ++ ++ /** ++ * Apply the configuration currently displayed. ++ */ ++ onClear_: function() { ++ // disable buttons; will be enabled back when UI receives a state update ++ this.toggleButtons_(true); ++ this.eraseCurrentConfig_(); ++ chrome.send('clear', []); ++ }, ++ ++ /** ++ * Toggle the disabled status of the action buttons. ++ */ ++ toggleButtons_: function(disabled) { ++ $(kIdApplyButton).disabled = disabled; ++ $(kIdResetButton).disabled = disabled; ++ $(kIdClearButton).disabled = disabled; ++ }, ++ ++ /** ++ * Reset currently displayed configuration to the last known configuration in use. ++ */ ++ onReset_: function() { ++ this.renderConfig_(); ++ } ++ }; ++ ++ return ProxyConfigView; ++})(); +diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn +index b499b28307af0..9b37cf6098ee0 100644 +--- a/chrome/browser/ui/BUILD.gn ++++ b/chrome/browser/ui/BUILD.gn +@@ -299,6 +299,8 @@ static_library("ui") { + "webui/metrics_internals/metrics_internals_ui.h", + "webui/net_export_ui.cc", + "webui/net_export_ui.h", ++ "webui/proxy_config_ui.cc", ++ "webui/proxy_config_ui.h", + "webui/net_internals/net_internals_ui.cc", + "webui/net_internals/net_internals_ui.h", + "webui/ntp_tiles_internals_ui.cc", +diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +index 2eb4bd497a036..b448dcb118d41 100644 +--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc ++++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +@@ -59,6 +59,7 @@ + #include "chrome/browser/ui/webui/ntp_tiles_internals_ui.h" + #include "chrome/browser/ui/webui/omnibox/omnibox_ui.h" + #include "chrome/browser/ui/webui/policy/policy_ui.h" ++#include "chrome/browser/ui/webui/proxy_config_ui.h" + #include "chrome/browser/ui/webui/predictors/predictors_ui.h" + #include "chrome/browser/ui/webui/segmentation_internals/segmentation_internals_ui.h" + #include "chrome/browser/ui/webui/signin_internals_ui.h" +@@ -493,6 +494,8 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, + return &NewWebUI; + if (url.host_piece() == chrome::kChromeUINetExportHost) + return &NewWebUI; ++ if (url.host_piece() == chrome::kChromeUIProxyConfigHost) ++ return &NewWebUI; + if (url.host_piece() == chrome::kChromeUINetInternalsHost) + return &NewWebUI; + if (url.host_piece() == chrome::kChromeUINTPTilesInternalsHost) +diff --git a/chrome/browser/ui/webui/proxy_config_ui.cc b/chrome/browser/ui/webui/proxy_config_ui.cc +new file mode 100644 +index 0000000000000..e0269904b6dfe +--- /dev/null ++++ b/chrome/browser/ui/webui/proxy_config_ui.cc +@@ -0,0 +1,393 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++#include "chrome/browser/ui/webui/proxy_config_ui.h" ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "base/command_line.h" ++#include "base/lazy_instance.h" ++#include "base/memory/ref_counted.h" ++#include "base/strings/string_util.h" ++#include "base/strings/utf_string_conversions.h" ++#include "base/values.h" ++#include "chrome/browser/browser_process.h" ++#include "chrome/browser/net/proxy_service_factory.h" ++#include "chrome/browser/platform_util.h" ++#include "chrome/browser/profiles/profile.h" ++#include "chrome/common/url_constants.h" ++#include "chrome/grit/browser_resources.h" ++#include "components/prefs/pref_service.h" ++#include "components/proxy_config/pref_proxy_config_tracker_impl.h" ++#include "components/proxy_config/proxy_config_pref_names.h" ++#include "components/grit/components_resources.h" ++#include "content/public/browser/browser_thread.h" ++#include "content/public/browser/url_data_source.h" ++#include "content/public/browser/web_contents.h" ++#include "content/public/browser/web_ui.h" ++#include "content/public/browser/web_ui_data_source.h" ++#include "content/public/browser/web_ui_message_handler.h" ++ ++#include "url/gurl.h" ++ ++using content::BrowserThread; ++using content::WebContents; ++using content::WebUIMessageHandler; ++ ++namespace { ++ ++// This class receives javascript messages from the renderer. ++// Note that the WebUI infrastructure runs on the UI thread, therefore all of ++// this class's public methods are expected to run on the UI thread. ++class ProxyConfigMessageHandler ++ : public WebUIMessageHandler, ++ public base::SupportsWeakPtr, ++ public net::ProxyConfigService::Observer { ++ public: ++ ProxyConfigMessageHandler(const ProxyConfigMessageHandler&) = delete; ++ ProxyConfigMessageHandler& operator=(const ProxyConfigMessageHandler&) = delete; ++ // Creates a ProxyConfigMessageHandler that handles message exchanges with the Javascript ++ // side of the UI and gets proxy settings from the Web UI associated profile to watch for changes. ++ // The created ProxyConfigMessageHandler must be destroyed before |profile|. ++ ProxyConfigMessageHandler(Profile *profile); ++ ~ProxyConfigMessageHandler() override; ++ ++ // WebUIMessageHandler implementation. ++ void RegisterMessages() override; ++ ++ // Messages ++ void OnEnableNotifyUIWithState(const base::Value::List& args); ++ void OnApply(const base::Value::List& args); ++ void OnClear(const base::Value::List& args); ++ ++ // net::ProxyConfigService::Observer implementation: ++ // Calls ProxyConfigView.onProxyConfigChanged JavaScript function in the ++ // renderer. ++ void OnProxyConfigChanged( ++ const net::ProxyConfigWithAnnotation& config, ++ net::ProxyConfigService::ConfigAvailability availability) override; ++ ++ private: ++ // Not owned. ++ raw_ptr pref_service_; ++ std::unique_ptr proxy_config_service_; ++ // Monitors global and Profile prefs related to proxy configuration. ++ std::unique_ptr pref_proxy_config_tracker_; ++ bool is_observing_; ++ ++ void encodeConfig(const net::ProxyConfig& config, base::Value::Dict& state); ++ ++ void apply(const net::ProxyConfig& config); ++ ++ base::WeakPtrFactory weak_ptr_factory_; ++}; ++ ++ProxyConfigMessageHandler::ProxyConfigMessageHandler(Profile *profile) ++ : ++ weak_ptr_factory_(this) { ++ ++ // used to set new configuration preferences ++ pref_service_ = g_browser_process->local_state(); ++ // observer is explicitly added only later in enableNotifyUIWithState ++ is_observing_ = false; ++ ++// If this is the ChromeOS sign-in profile, just create the tracker from global ++// state. ++#if defined(OS_CHROMEOS) ++ if (chromeos::ProfileHelper::IsSigninProfile(profile)) { ++ pref_proxy_config_tracker_.reset( ++ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState( ++ g_browser_process->local_state())); ++ } ++#endif // defined(OS_CHROMEOS) ++ ++ if (!pref_proxy_config_tracker_) { ++ pref_proxy_config_tracker_ = ++ ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile( ++ profile->GetPrefs(), g_browser_process->local_state()); ++ } ++ ++ proxy_config_service_ = ProxyServiceFactory::CreateProxyConfigService( ++ pref_proxy_config_tracker_.get(), nullptr); ++} ++ ++void ProxyConfigMessageHandler::OnProxyConfigChanged( ++ const net::ProxyConfigWithAnnotation& config, ++ net::ProxyConfigService::ConfigAvailability availability) { ++ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || ++ !BrowserThread::IsThreadInitialized(BrowserThread::UI)); ++ ++ base::Value::Dict state; ++ bool pending = false; ++ switch (availability) { ++ case net::ProxyConfigService::CONFIG_VALID: ++ encodeConfig(config.value(), state); ++ break; ++ case net::ProxyConfigService::CONFIG_UNSET: ++ state.SetByDottedPath("config.rules.type", base::Value("none")); ++ break; ++ case net::ProxyConfigService::CONFIG_PENDING: ++ //NOTE: this can only happen when triggered manually first time ++ pending = true; ++ break; ++ } ++ state.Set("pending", base::Value(pending)); ++ ++ // call Javascript function ++ web_ui()->CallJavascriptFunctionUnsafe("ProxyConfigView.getInstance().onProxyConfigChanged", ++ state.Clone()); ++} ++ ++const std::string omitDirect(const std::string pacString) { ++ if (pacString == "DIRECT") { ++ return ""; ++ } ++ return pacString; ++} ++ ++void ProxyConfigMessageHandler::encodeConfig(const net::ProxyConfig& config, base::Value::Dict& state) { ++ // when automatic settings are enabled they take precedence over manual settings ++ // automatic settings are either the "auto-detect" flag or the existance of a PAC URL ++ ++ state.SetByDottedPath("config.auto_detect", base::Value(config.auto_detect())); ++ ++ auto rules = config.proxy_rules(); ++ if (config.has_pac_url()) { ++ state.SetByDottedPath("config.pac_url", base::Value(config.pac_url().spec())); ++ state.SetByDottedPath("config.pac_mandatory", base::Value(config.pac_mandatory())); ++ state.SetByDottedPath("config.rules.type", base::Value("none")); ++ state.SetByDottedPath("config.rules.bypass_rules", base::Value(rules.bypass_rules.ToString())); ++ state.SetByDottedPath("config.rules.reverse_bypass", base::Value(rules.reverse_bypass)); ++ return; ++ } ++ ++ const char *type; ++ switch (rules.type) { ++ case net::ProxyConfig::ProxyRules::Type::EMPTY: ++ type = "direct"; ++ break; ++ case net::ProxyConfig::ProxyRules::Type::PROXY_LIST: ++ type = "list"; ++ ++ state.SetByDottedPath("config.rules.single_proxies", base::Value(omitDirect(rules.single_proxies.ToPacString()))); ++ break; ++ case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME: ++ type = "list_per_scheme"; ++ ++ state.SetByDottedPath("config.rules.proxies_for_http", base::Value(omitDirect(rules.proxies_for_http.ToPacString()))); ++ state.SetByDottedPath("config.rules.proxies_for_https", base::Value(omitDirect(rules.proxies_for_https.ToPacString()))); ++ state.SetByDottedPath("config.rules.proxies_for_ftp", base::Value(omitDirect(rules.proxies_for_ftp.ToPacString()))); ++ state.SetByDottedPath("config.rules.fallback_proxies", base::Value(omitDirect(rules.fallback_proxies.ToPacString()))); ++ break; ++ default: ++ NOTREACHED(); ++ break; ++ } ++ state.SetByDottedPath("config.rules.type", base::Value(type)); ++ state.SetByDottedPath("config.rules.bypass_rules", base::Value(rules.bypass_rules.ToString())); ++ state.SetByDottedPath("config.rules.reverse_bypass", base::Value(rules.reverse_bypass)); ++} ++ ++ProxyConfigMessageHandler::~ProxyConfigMessageHandler() { ++ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || ++ !BrowserThread::IsThreadInitialized(BrowserThread::UI)); ++ if (is_observing_) { ++ proxy_config_service_->RemoveObserver(this); ++ } ++ pref_proxy_config_tracker_->DetachFromPrefService(); ++} ++ ++void ProxyConfigMessageHandler::RegisterMessages() { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); ++ ++ web_ui()->RegisterMessageCallback( ++ "enableNotifyUIWithState", ++ base::BindRepeating(&ProxyConfigMessageHandler::OnEnableNotifyUIWithState, ++ base::Unretained(this))); ++ web_ui()->RegisterMessageCallback( ++ "apply", ++ base::BindRepeating(&ProxyConfigMessageHandler::OnApply, ++ base::Unretained(this))); ++ web_ui()->RegisterMessageCallback( ++ "clear", ++ base::BindRepeating(&ProxyConfigMessageHandler::OnClear, ++ base::Unretained(this))); ++} ++ ++// The proxy configuration UI is not notified of state changes until this function runs. ++// After this function, OnProxyConfigChanged() will be called on all proxy state changes. ++void ProxyConfigMessageHandler::OnEnableNotifyUIWithState( ++ const base::Value::List& list) { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); ++ ++ if (!is_observing_) { ++ is_observing_ = true; ++ proxy_config_service_->AddObserver(this); ++ } ++ ++ net::ProxyConfigWithAnnotation config; ++ auto availability = proxy_config_service_->GetLatestProxyConfig(&config); ++ ++ const PrefService::Preference* const pref = ++ pref_service_->FindPreference(proxy_config::prefs::kProxy); ++ ProxyConfigDictionary proxy_dict(pref->GetValue()->GetDict().Clone()); ++ ProxyPrefs::ProxyMode mode; ++ if (!proxy_dict.GetMode(&mode) || mode == ProxyPrefs::MODE_SYSTEM) { ++ availability = net::ProxyConfigService::CONFIG_UNSET; ++ } ++ ++ OnProxyConfigChanged(config, availability); ++} ++ ++void ProxyConfigMessageHandler::OnClear(const base::Value::List& list) { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); ++ ++ const base::Value::Dict cfg = ProxyConfigDictionary::CreateSystem(); ++ pref_service_->SetDict(proxy_config::prefs::kProxy, cfg.Clone()); ++ pref_service_->CommitPendingWrite(); ++ OnEnableNotifyUIWithState(list); ++} ++ ++void ProxyConfigMessageHandler::OnApply(const base::Value::List& list) { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); ++ ++ if ((list.size() != 1) || !list[0].is_dict()) { ++ return; ++ } ++ ++ const base::Value::Dict& config = list[0].GetDict(); ++ ++ if (config.FindBool("auto_detect").value_or(false)) { ++ apply(net::ProxyConfig::CreateAutoDetect()); ++ return; ++ } ++ ++ if (const std::string* pacURL = config.FindString("pac_url")) { ++ absl::optional pacMandatory = config.FindBool("pac_mandatory"); ++ auto proxyConfig = net::ProxyConfig::CreateFromCustomPacURL( ++ GURL(*pacURL)); ++ proxyConfig.set_pac_mandatory(pacMandatory.value_or(false)); ++ apply(proxyConfig); ++ return; ++ } ++ ++ const base::Value::Dict* rules = config.FindDict("rules"); ++ if (rules == nullptr) ++ return; ++ ++ net::ProxyConfig proxyConfig; ++ bool readBypass = false; ++ ++ const std::string* type = rules->FindString("type"); ++ if (type == nullptr) ++ return; ++ ++ if (*type == "list") { ++ const std::string* single_proxies = rules->FindString("single_proxies"); ++ if (single_proxies == nullptr) ++ return; ++ proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST; ++ proxyConfig.proxy_rules().single_proxies.SetFromPacString(*single_proxies); ++ readBypass = true; ++ } else if (*type == "list_per_scheme") { ++ const std::string* http = rules->FindString("proxies_for_http"); ++ const std::string* https = rules->FindString("proxies_for_https"); ++ const std::string* ftp = rules->FindString("proxies_for_ftp"); ++ const std::string* fallback = rules->FindString("fallback_proxies"); ++ ++ proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME; ++ if (http) ++ proxyConfig.proxy_rules().proxies_for_http.SetFromPacString(*http); ++ if (https) ++ proxyConfig.proxy_rules().proxies_for_https.SetFromPacString(*https); ++ if (ftp) ++ proxyConfig.proxy_rules().proxies_for_ftp.SetFromPacString(*ftp); ++ if (fallback) ++ proxyConfig.proxy_rules().fallback_proxies.SetFromPacString(*fallback); ++ readBypass = true; ++ } else if (*type == "direct") { ++ proxyConfig.proxy_rules().type = net::ProxyConfig::ProxyRules::Type::EMPTY; ++ } else if (*type == "none") { ++ base::Value::List empty; ++ OnClear(empty); ++ return; ++ } else { ++ // invalid type ++ LOG(WARNING) << "invalid proxy configuration type"; ++ return; ++ } ++ ++ // bypass rules and reverse flag are common to both list types of proxy rules ++ if (readBypass) { ++ const std::string* bypass_rules = rules->FindString("bypass_rules"); ++ if (bypass_rules == nullptr) ++ return; ++ ++ absl::optional reverse_bypass = rules->FindBool("reverse_bypass"); ++ ++ proxyConfig.proxy_rules().bypass_rules.ParseFromString(*bypass_rules); ++ proxyConfig.proxy_rules().reverse_bypass = reverse_bypass.value_or(false); ++ } ++ ++ apply(proxyConfig); ++} ++ ++void ProxyConfigMessageHandler::apply(const net::ProxyConfig& proxyConfig) { ++ if (proxyConfig.auto_detect()) { ++ const base::Value::Dict cfg = ProxyConfigDictionary::CreateAutoDetect(); ++ pref_service_->SetDict(proxy_config::prefs::kProxy, cfg.Clone()); ++ } else if (proxyConfig.has_pac_url()) { ++ const base::Value::Dict cfg = ProxyConfigDictionary::CreatePacScript(proxyConfig.pac_url().spec(), proxyConfig.pac_mandatory()); ++ pref_service_->SetDict(proxy_config::prefs::kProxy, cfg.Clone()); ++ } else if (proxyConfig.proxy_rules().type == net::ProxyConfig::ProxyRules::Type::EMPTY) { ++ const base::Value::Dict cfg = ProxyConfigDictionary::CreateDirect(); ++ pref_service_->SetDict(proxy_config::prefs::kProxy, cfg.Clone()); ++ } else { ++ auto proxyRulesAsString = proxyConfig.proxy_rules().ToString(); ++ auto bypassRulesAsString = proxyConfig.proxy_rules().bypass_rules.ToString(); ++ ++ // fixed servers ++ const base::Value::Dict cfg = ProxyConfigDictionary::CreateFixedServers(proxyRulesAsString, ++ bypassRulesAsString, proxyConfig.proxy_rules().reverse_bypass); ++ pref_service_->SetDict(proxy_config::prefs::kProxy, cfg.Clone()); ++ } ++ pref_service_->CommitPendingWrite(); ++ ++ base::Value::List empty; ++ OnEnableNotifyUIWithState(empty); ++} ++ ++} // namespace ++ ++ProxyConfigUI::ProxyConfigUI(content::WebUI* web_ui) : WebUIController(web_ui) { ++ Profile* profile = Profile::FromWebUI(web_ui); ++ ++ web_ui->AddMessageHandler(std::make_unique(profile)); ++ ++ // Set up the chrome://proxy/ source. ++ content::WebUIDataSource* source = ++ content::WebUIDataSource::CreateAndAdd(web_ui->GetWebContents()->GetBrowserContext(), ++ chrome::kChromeUIProxyConfigHost); ++ ++ source->UseStringsJs(); ++ source->AddResourcePath("proxy_config.js", IDR_PROXY_CONFIG_JS); ++ source->SetDefaultResource(IDR_PROXY_CONFIG_HTML); ++} +diff --git a/chrome/browser/ui/webui/proxy_config_ui.h b/chrome/browser/ui/webui/proxy_config_ui.h +new file mode 100644 +index 0000000000000..04ef3f7c1fb6d +--- /dev/null ++++ b/chrome/browser/ui/webui/proxy_config_ui.h +@@ -0,0 +1,31 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++#ifndef CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_ ++#define CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_ ++ ++#include "content/public/browser/web_ui_controller.h" ++ ++// The WebUI for chrome://proxy/. ++class ProxyConfigUI : public content::WebUIController { ++ public: ++ ProxyConfigUI(const ProxyConfigUI&) = delete; ++ ProxyConfigUI& operator=(const ProxyConfigUI&) = delete; ++ explicit ProxyConfigUI(content::WebUI* web_ui); ++}; ++ ++#endif // CHROME_BROWSER_UI_WEBUI_PROXY_CONFIG_UI_H_ +diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc +index f25f690144d2d..e2610eb6e0f01 100644 +--- a/chrome/common/webui_url_constants.cc ++++ b/chrome/common/webui_url_constants.cc +@@ -51,6 +51,8 @@ const char kChromeUICertificateViewerHost[] = "view-cert"; + const char kChromeUICertificateViewerURL[] = "chrome://view-cert/"; + const char kChromeUIChromeSigninHost[] = "chrome-signin"; + const char kChromeUIChromeSigninURL[] = "chrome://chrome-signin/"; ++const char kChromeUIProxyConfigHost[] = "proxy"; ++const char kChromeUIProxyConfigURL[] = "chrome://proxy/"; + const char kChromeUIChromeURLsHost[] = "chrome-urls"; + const char kChromeUIChromeURLsURL[] = "chrome://chrome-urls/"; + const char kChromeUIComponentsHost[] = "components"; +@@ -436,6 +438,7 @@ bool IsSystemWebUIHost(base::StringPiece host) { + kChromeUILockScreenNetworkHost, + kChromeUILockScreenStartReauthHost, + kChromeUIMobileSetupHost, ++ kChromeUIProxyConfigHost, + kChromeUIMultiDeviceSetupHost, + kChromeUINetworkHost, + kChromeUINotificationTesterHost, +@@ -741,6 +744,7 @@ const char* const kChromeHostURLs[] = { + #if !BUILDFLAG(IS_ANDROID) + #if !BUILDFLAG(IS_CHROMEOS) + kChromeUIAppLauncherPageHost, ++ kChromeUIProxyConfigHost, + #endif + kChromeUIBookmarksHost, + kChromeUIDownloadsHost, +diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h +index caca46377bbeb..17cce860b2f02 100644 +--- a/chrome/common/webui_url_constants.h ++++ b/chrome/common/webui_url_constants.h +@@ -138,6 +138,8 @@ extern const char kChromeUINTPTilesInternalsHost[]; + extern const char kChromeUINaClHost[]; + extern const char kChromeUINetExportHost[]; + extern const char kChromeUINetExportURL[]; ++extern const char kChromeUIProxyConfigHost[]; ++extern const char kChromeUIProxyConfigURL[]; + extern const char kChromeUINetInternalsHost[]; + extern const char kChromeUINetInternalsURL[]; + extern const char kChromeUINewTabHost[]; +diff --git a/components/proxy_config/pref_proxy_config_tracker_impl.cc b/components/proxy_config/pref_proxy_config_tracker_impl.cc +index 343ab90710d59..eee846925378a 100644 +--- a/components/proxy_config/pref_proxy_config_tracker_impl.cc ++++ b/components/proxy_config/pref_proxy_config_tracker_impl.cc +@@ -244,14 +244,14 @@ PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( + // static + void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(proxy_config::prefs::kProxy, +- ProxyConfigDictionary::CreateSystem()); ++ ProxyConfigDictionary::CreateDirect()); + } + + // static + void PrefProxyConfigTrackerImpl::RegisterProfilePrefs( + PrefRegistrySimple* registry) { + registry->RegisterDictionaryPref(proxy_config::prefs::kProxy, +- ProxyConfigDictionary::CreateSystem()); ++ ProxyConfigDictionary::CreateDirect()); + registry->RegisterBooleanPref(proxy_config::prefs::kUseSharedProxies, false); + } + +@@ -388,6 +388,7 @@ bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig( + if (proxy_dict.GetBypassList(&proxy_bypass)) { + proxy_config.proxy_rules().bypass_rules.ParseFromString(proxy_bypass); + } ++ proxy_config.proxy_rules().reverse_bypass = proxy_dict.HasReverseBypass(); + *config = net::ProxyConfigWithAnnotation( + proxy_config, kSettingsProxyConfigTrafficAnnotation); + return true; +diff --git a/components/proxy_config/proxy_config_dictionary.cc b/components/proxy_config/proxy_config_dictionary.cc +index e2c6ce30f8e6e..ba6d314afeca6 100644 +--- a/components/proxy_config/proxy_config_dictionary.cc ++++ b/components/proxy_config/proxy_config_dictionary.cc +@@ -30,6 +30,8 @@ const char kProxyPacMandatory[] = "pac_mandatory"; + // String containing proxy bypass rules. For a specification of the + // expected syntax see net::ProxyBypassRules::ParseFromString(). + const char kProxyBypassList[] = "bypass_list"; ++// Boolean telling whether to reverse the meaning of the bypass list. ++const char kProxyReverseBypass[] = "reverse_bypass"; + + } // namespace + +@@ -76,6 +78,14 @@ bool ProxyConfigDictionary::HasBypassList() const { + return dict_.Find(kProxyBypassList); + } + ++bool ProxyConfigDictionary::HasReverseBypass() const { ++ const base::Value* value = dict_.Find(kProxyReverseBypass); ++ if (!value || !value->is_bool()) { ++ return false; ++ } ++ return value->GetBool(); ++} ++ + const base::Value::Dict& ProxyConfigDictionary::GetDictionary() const { + return dict_; + } +@@ -83,13 +93,13 @@ const base::Value::Dict& ProxyConfigDictionary::GetDictionary() const { + // static + base::Value::Dict ProxyConfigDictionary::CreateDirect() { + return CreateDictionary(ProxyPrefs::MODE_DIRECT, std::string(), false, +- std::string(), std::string()); ++ std::string(), std::string(), false); + } + + // static + base::Value::Dict ProxyConfigDictionary::CreateAutoDetect() { + return CreateDictionary(ProxyPrefs::MODE_AUTO_DETECT, std::string(), false, +- std::string(), std::string()); ++ std::string(), std::string(), false); + } + + // static +@@ -97,16 +107,17 @@ base::Value::Dict ProxyConfigDictionary::CreatePacScript( + const std::string& pac_url, + bool pac_mandatory) { + return CreateDictionary(ProxyPrefs::MODE_PAC_SCRIPT, pac_url, pac_mandatory, +- std::string(), std::string()); ++ std::string(), std::string(), false); + } + + // static + base::Value::Dict ProxyConfigDictionary::CreateFixedServers( + const std::string& proxy_server, +- const std::string& bypass_list) { ++ const std::string& bypass_list, ++ bool reverse_bypass) { + if (!proxy_server.empty()) { + return CreateDictionary(ProxyPrefs::MODE_FIXED_SERVERS, std::string(), +- false, proxy_server, bypass_list); ++ false, proxy_server, bypass_list, reverse_bypass); + } else { + return CreateDirect(); + } +@@ -115,7 +126,7 @@ base::Value::Dict ProxyConfigDictionary::CreateFixedServers( + // static + base::Value::Dict ProxyConfigDictionary::CreateSystem() { + return CreateDictionary(ProxyPrefs::MODE_SYSTEM, std::string(), false, +- std::string(), std::string()); ++ std::string(), std::string(), false); + } + + // static +@@ -124,7 +135,8 @@ base::Value::Dict ProxyConfigDictionary::CreateDictionary( + const std::string& pac_url, + bool pac_mandatory, + const std::string& proxy_server, +- const std::string& bypass_list) { ++ const std::string& bypass_list, ++ bool reverse_bypass) { + base::Value::Dict dict; + dict.Set(kProxyMode, base::Value(ProxyModeToString(mode))); + if (!pac_url.empty()) { +@@ -133,8 +145,10 @@ base::Value::Dict ProxyConfigDictionary::CreateDictionary( + } + if (!proxy_server.empty()) + dict.Set(kProxyServer, base::Value(proxy_server)); +- if (!bypass_list.empty()) ++ if (!bypass_list.empty()) { + dict.Set(kProxyBypassList, base::Value(bypass_list)); ++ dict.Set(kProxyReverseBypass, base::Value(reverse_bypass)); ++ } + return dict; + } + +diff --git a/components/proxy_config/proxy_config_dictionary.h b/components/proxy_config/proxy_config_dictionary.h +index 6f65a50e97f1f..76a439905877d 100644 +--- a/components/proxy_config/proxy_config_dictionary.h ++++ b/components/proxy_config/proxy_config_dictionary.h +@@ -42,6 +42,7 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary { + bool GetProxyServer(std::string* out) const; + bool GetBypassList(std::string* out) const; + bool HasBypassList() const; ++ bool HasReverseBypass() const; + + const base::Value::Dict& GetDictionary() const; + +@@ -50,7 +51,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary { + static base::Value::Dict CreatePacScript(const std::string& pac_url, + bool pac_mandatory); + static base::Value::Dict CreateFixedServers(const std::string& proxy_server, +- const std::string& bypass_list); ++ const std::string& bypass_list, ++ bool reverse_bypass); + static base::Value::Dict CreateSystem(); + + // Encodes the proxy server as "=://". +@@ -66,7 +68,8 @@ class PROXY_CONFIG_EXPORT ProxyConfigDictionary { + const std::string& pac_url, + bool pac_mandatory, + const std::string& proxy_server, +- const std::string& bypass_list); ++ const std::string& bypass_list, ++ bool reverse_bypass); + + base::Value::Dict dict_; + }; +diff --git a/components/proxy_config/proxy_policy_handler.cc b/components/proxy_config/proxy_policy_handler.cc +index 5976751880f5d..4f8eeacc93347 100644 +--- a/components/proxy_config/proxy_policy_handler.cc ++++ b/components/proxy_config/proxy_policy_handler.cc +@@ -262,7 +262,7 @@ void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, + set_proxy_pref_value(ProxyConfigDictionary::CreateFixedServers( + server->GetString(), bypass_list && bypass_list->is_string() + ? bypass_list->GetString() +- : std::string())); ++ : std::string(), false)); + } + break; + } +diff --git a/net/proxy_resolution/proxy_config.cc b/net/proxy_resolution/proxy_config.cc +index 190d25a2d3a50..972e472501699 100644 +--- a/net/proxy_resolution/proxy_config.cc ++++ b/net/proxy_resolution/proxy_config.cc +@@ -112,7 +112,7 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) { + &single_proxies, + ProxyServer::SCHEME_HTTP); + type = Type::PROXY_LIST; +- return; ++ continue; + } + + // Trim whitespace off the url scheme. +@@ -143,6 +143,56 @@ void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) { + } + } + ++std::string ProxyConfig::ProxyRules::ToString() const { ++ if (type == Type::EMPTY) { ++ return ""; ++ } ++ ++ // special case: a single proxy servers list specified ++ if (type == Type::PROXY_LIST) { ++ std::string proxy_list; ++ for (const ProxyServer& proxy_server : ++ single_proxies.GetAll()) { ++ proxy_list += ProxyServerToProxyUri(proxy_server) + ";"; ++ } ++ // remove last semicolon ++ if (proxy_list.length() != 0 ) { ++ proxy_list.pop_back(); ++ } ++ return proxy_list; ++ } ++ ++ if (type != Type::PROXY_LIST_PER_SCHEME) { ++ NOTREACHED(); ++ // Unexpected LIST with fallback, or other type values ++ return ""; ++ } ++ ++ // start to build a per-scheme list ++ std::string list; ++ for (const ProxyServer& proxy_server : ++ proxies_for_http.GetAll()) { ++ list += "http=" + ProxyServerToProxyUri(proxy_server) + ";"; ++ } ++ for (const ProxyServer& proxy_server : ++ proxies_for_https.GetAll()) { ++ list += "https=" + ProxyServerToProxyUri(proxy_server) + ";"; ++ } ++ for (const ProxyServer& proxy_server : ++ proxies_for_ftp.GetAll()) { ++ list += "ftp=" + ProxyServerToProxyUri(proxy_server) + ";"; ++ } ++ for (const ProxyServer& proxy_server : ++ fallback_proxies.GetAll()) { ++ list += "socks=" + ProxyServerToProxyUri(proxy_server) + ";"; ++ } ++ if (list.length() != 0 ) { ++ // remove last semicolon ++ list.pop_back(); ++ } ++ return list; ++} ++ + const ProxyList* ProxyConfig::ProxyRules::MapUrlSchemeToProxyList( + const std::string& url_scheme) const { + const ProxyList* proxy_server_list = const_cast(this)-> +diff --git a/net/proxy_resolution/proxy_config.h b/net/proxy_resolution/proxy_config.h +index c14a396559000..096df1e2e5f65 100644 +--- a/net/proxy_resolution/proxy_config.h ++++ b/net/proxy_resolution/proxy_config.h +@@ -103,6 +103,9 @@ class NET_EXPORT ProxyConfig { + // and use socks4://foopy2 for all other + // URLs. + void ParseFromString(const std::string& proxy_rules); ++ // Returns the proxy rules in a format that can be parsed by ParseFromString; ++ // all information except bypass rules is used. ++ std::string ToString() const; + + // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp, + // &fallback_proxies}, or NULL if there is no proxy to use. +-- +2.34.1 + diff --git a/build/bromite_patches/Add-an-always-incognito-mode.patch b/build/bromite_patches/Add-an-always-incognito-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..8e67e2196697324e02bf52b9c9039464950a1eb9 --- /dev/null +++ b/build/bromite_patches/Add-an-always-incognito-mode.patch @@ -0,0 +1,2224 @@ +From d27adb054e24278e3169e4d8a3fd659903ae6863 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 2 Oct 2021 13:20:36 +0200 +Subject: [PATCH 063/192] Add an always-incognito mode + +Add a preference that causes all new tabs and all clicked links to launch in incognito. +Make sure initial incognito status is correctly recognized. +Enable incognito custom tabs and fix crashes for incognito/custom tab intents +Use a native flag to correctly start new tabs on app startup + +Add history, recents, offlinepages and send to home screen support for always incognito. +History, recent tabs and offline pages require the INCOGNITO_TAB_HISTORY_ENABLED +flag turned on. +IncognitoPlaceholder is also deactivated, both in the phone and tablet version. +The relative tests are also present. + +based on the original work by Ryan Archer +Major contributions by uazo. +See also: https://github.com/bromite/bromite/pull/1427 + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/chrome_java_resources.gni | 1 + + chrome/android/chrome_java_sources.gni | 2 + + .../java/res/xml/incognito_preferences.xml | 37 ++++ + .../java/res/xml/privacy_preferences.xml | 5 + + .../AlwaysIncognitoLinkInterceptor.java | 53 ++++++ + .../chrome/browser/ChromeTabbedActivity.java | 20 ++- + .../chrome/browser/app/ChromeActivity.java | 4 + + .../AppMenuPropertiesDelegateImpl.java | 26 ++- + .../ChromeContextMenuPopulator.java | 7 +- + .../CustomTabActivityLifecycleUmaTracker.java | 25 --- + .../CustomTabAppMenuPropertiesDelegate.java | 4 + + .../CustomTabIntentDataProvider.java | 5 +- + .../browser/download/DownloadUtils.java | 16 +- + .../browser/history/HistoryManager.java | 28 ++- + .../chrome/browser/history/HistoryPage.java | 16 ++ + .../native_page/NativePageFactory.java | 4 +- + .../chrome/browser/ntp/RecentTabsManager.java | 28 ++- + .../privacy/settings/IncognitoSettings.java | 160 ++++++++++++++++++ + .../browser/settings/SettingsActivity.java | 4 + + .../HistoricalTabModelObserver.java | 6 +- + .../tab/tab_restore/HistoricalTabSaver.java | 2 +- + .../tab_restore/HistoricalTabSaverImpl.java | 17 +- + .../tabbed_mode/TabbedRootUiCoordinator.java | 4 +- + .../browser/tabmodel/ChromeTabCreator.java | 5 +- + .../tabmodel/TabModelSelectorImpl.java | 3 + + .../browser/tabmodel/TabPersistentStore.java | 9 + + .../browser/toolbar/ToolbarManager.java | 4 +- + .../webapps/WebappIntentDataProvider.java | 14 ++ + chrome/browser/about_flags.cc | 4 + + .../browser/android/historical_tab_saver.cc | 22 ++- + .../chrome_autocomplete_provider_client.cc | 9 + + .../chrome_autocomplete_provider_client.h | 1 + + .../remote_suggestions_service_factory.cc | 5 + + .../host_content_settings_map_factory.cc | 22 ++- + chrome/browser/flag_descriptions.cc | 6 + + chrome/browser/flag_descriptions.h | 3 + + .../flags/android/chrome_feature_list.cc | 4 +- + chrome/browser/history/history_tab_helper.cc | 20 +++ + chrome/browser/history/history_tab_helper.h | 10 +- + .../android/offline_page_bridge.cc | 11 +- + .../android/offline_page_model_factory.cc | 20 ++- + .../android/request_coordinator_factory.cc | 34 +++- + .../offline_page_model_factory.h | 1 + + .../offline_pages/recent_tab_helper.cc | 19 ++- + .../browser/offline_pages/recent_tab_helper.h | 3 + + .../request_coordinator_factory.h | 4 +- + chrome/browser/prefs/browser_prefs.cc | 5 + + chrome/browser/profiles/profile_selections.cc | 10 ++ + chrome/browser/profiles/profile_selections.h | 7 +- + .../browser/ui/android/native_page/BUILD.gn | 2 + + .../browser/ui/native_page/NativePage.java | 16 +- + .../strings/android_chrome_strings.grd | 31 ++++ + .../browser/toolbar/LocationBarModel.java | 5 +- + chrome/browser/ui/messages/android/BUILD.gn | 1 + + .../snackbar/INeedSnackbarManager.java | 27 +++ + chrome/common/pref_names.h | 6 + + .../browser/content_settings_pref_provider.cc | 8 +- + .../browser/content_settings_pref_provider.h | 2 + + .../core/browser/host_content_settings_map.cc | 4 +- + .../core/browser/host_content_settings_map.h | 3 + + .../core/offline_page_feature.cc | 3 + + .../offline_pages/core/offline_page_feature.h | 1 + + .../browser/autocomplete_provider_client.cc | 4 + + .../browser/autocomplete_provider_client.h | 1 + + .../omnibox/browser/base_search_provider.cc | 2 +- + components/omnibox/browser/search_provider.cc | 4 +- + .../host_content_settings_map_factory.cc | 1 + + 67 files changed, 759 insertions(+), 91 deletions(-) + create mode 100644 chrome/android/java/res/xml/incognito_preferences.xml + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/IncognitoSettings.java + create mode 100644 chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java + +diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni +index 4592ccd7a1d48..25dd9bddf366d 100644 +--- a/chrome/android/chrome_java_resources.gni ++++ b/chrome/android/chrome_java_resources.gni +@@ -667,6 +667,7 @@ chrome_java_resources = [ + "java/res/xml/main_preferences.xml", + "java/res/xml/manage_sync_preferences.xml", + "java/res/xml/phone_as_a_security_key_accessory_filter.xml", ++ "java/res/xml/incognito_preferences.xml", + "java/res/xml/privacy_preferences.xml", + "java/res/xml/privacy_preferences_v2.xml", + "java/res/xml/search_widget_info.xml", +diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni +index f6d0511ae94c3..c7972b041e693 100644 +--- a/chrome/android/chrome_java_sources.gni ++++ b/chrome/android/chrome_java_sources.gni +@@ -3,6 +3,7 @@ + # found in the LICENSE file. + + chrome_java_sources = [ ++ "java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java", + "java/src/com/google/android/apps/chrome/appwidget/bookmarks/BookmarkThumbnailWidgetProvider.java", + "java/src/org/chromium/chrome/browser/ActivityTabProvider.java", + "java/src/org/chromium/chrome/browser/ActivityUtils.java", +@@ -953,6 +954,7 @@ chrome_java_sources = [ + "java/src/org/chromium/chrome/browser/privacy/settings/IncognitoLockSettings.java", + "java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerImpl.java", + "java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java", ++ "java/src/org/chromium/chrome/browser/privacy/settings/IncognitoSettings.java", + "java/src/org/chromium/chrome/browser/provider/BaseColumns.java", + "java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java", + "java/src/org/chromium/chrome/browser/provider/ChromeBrowserProviderImpl.java", +diff --git a/chrome/android/java/res/xml/incognito_preferences.xml b/chrome/android/java/res/xml/incognito_preferences.xml +new file mode 100644 +index 0000000000000..d52ffe9170b94 +--- /dev/null ++++ b/chrome/android/java/res/xml/incognito_preferences.xml +@@ -0,0 +1,37 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml +index 7dea487f76b53..31168cba63391 100644 +--- a/chrome/android/java/res/xml/privacy_preferences.xml ++++ b/chrome/android/java/res/xml/privacy_preferences.xml +@@ -50,6 +50,11 @@ found in the LICENSE file. + android:key="secure_dns" + android:title="@string/settings_secure_dns_title" + android:fragment="org.chromium.chrome.browser.privacy.secure_dns.SecureDnsSettings"/> ++ + + throw new IllegalStateException( + "Attempting to access TabCreator before initialization"); + } ++ if (AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ incognito = true; ++ } + return mTabCreatorManagerSupplier.get().getTabCreator(incognito); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java +index 26e56eb82b03f..211cd162129e7 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java +@@ -37,6 +37,7 @@ import org.chromium.base.supplier.OneshotSupplier; + import org.chromium.base.supplier.Supplier; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.ActivityTabProvider; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.banners.AppMenuVerbiage; + import org.chromium.chrome.browser.bookmarks.BookmarkFeatures; + import org.chromium.chrome.browser.bookmarks.BookmarkModel; +@@ -104,6 +105,10 @@ import java.lang.annotation.RetentionPolicy; + import java.util.ArrayList; + import java.util.List; + ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.chrome.browser.preferences.Pref; ++ + /** + * Base implementation of {@link AppMenuPropertiesDelegate} that handles hiding and showing menu + * items based on activity state. +@@ -567,6 +572,13 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate + } + + private void prepareCommonMenuItems(Menu menu, @MenuGroup int menuGroup, boolean isIncognito) { ++ boolean always_incognito = AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); ++ if (always_incognito) { ++ final MenuItem newTabOption = menu.findItem(R.id.new_tab_menu_id); ++ if (newTabOption != null) ++ newTabOption.setVisible(false); ++ } ++ + // We have to iterate all menu items since same menu item ID may be associated with more + // than one menu items. + boolean isOverviewModeMenu = menuGroup == MenuGroup.OVERVIEW_MODE_MENU; +@@ -627,7 +639,15 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate + } + + if (item.getItemId() == R.id.recent_tabs_menu_id) { +- item.setVisible(!isIncognito); ++ if (always_incognito) { ++ PrefService prefService = ++ UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean historyEnabledInIncognito = ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ item.setVisible(historyEnabledInIncognito); ++ } ++ else ++ item.setVisible(!isIncognito); + } + if (item.getItemId() == R.id.menu_select_tabs) { + item.setVisible(isMenuSelectTabsVisible); +@@ -831,7 +851,9 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate + // is not persisted when adding to the homescreen. + // * If creating shortcuts it not supported by the current home screen. + return WebappsUtils.isAddToHomeIntentSupported() && !isChromeScheme && !isFileScheme +- && !isContentScheme && !isIncognito && !url.isEmpty(); ++ && !isContentScheme && !url.isEmpty() ++ && (!isIncognito || ++ AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()); + } + + /** +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java +index f46b319c4fc0e..3680169398ef4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java +@@ -28,6 +28,7 @@ import org.chromium.base.Callback; + import org.chromium.base.ContextUtils; + import org.chromium.base.supplier.Supplier; + import org.chromium.chrome.R; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCoordinator; + import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItem.Item; + import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType; +@@ -231,6 +232,9 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { + public List> buildContextMenu() { + mShowEphemeralTabNewLabel = null; + ++ boolean always_incognito = ++ AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); ++ + List> groupedItems = new ArrayList<>(); + + if (mParams.isAnchor()) { +@@ -240,6 +244,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { + if (mMode == ContextMenuMode.NORMAL) { + linkGroup.add(createListItem(Item.OPEN_IN_NEW_TAB_IN_GROUP)); + linkGroup.add(createListItem(Item.OPEN_IN_NEW_TAB)); ++ + if (!mItemDelegate.isIncognito() && mItemDelegate.isIncognitoSupported()) { + linkGroup.add(createListItem(Item.OPEN_IN_INCOGNITO_TAB)); + } +@@ -264,7 +269,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { + } + } + if (FirstRunStatus.getFirstRunFlowComplete()) { +- if (!mItemDelegate.isIncognito() ++ if ((always_incognito || !mItemDelegate.isIncognito()) + && UrlUtilities.isDownloadableScheme(mParams.getLinkUrl())) { + linkGroup.add(createListItem(Item.SAVE_LINK_AS)); + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java +index b5c3be7662ec9..4d9cb62ecff8b 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java +@@ -67,31 +67,6 @@ public class CustomTabActivityLifecycleUmaTracker + private boolean mIsInitialResume = true; + + private void recordIncognitoLaunchReason() { +- IncognitoCustomTabIntentDataProvider incognitoProvider = +- (IncognitoCustomTabIntentDataProvider) mIntentDataProvider; +- +- @IntentHandler.IncognitoCCTCallerId +- int incognitoCCTCallerId = incognitoProvider.getFeatureIdForMetricsCollection(); +- RecordHistogram.recordEnumeratedHistogram("CustomTabs.IncognitoCCTCallerId", +- incognitoCCTCallerId, IntentHandler.IncognitoCCTCallerId.NUM_ENTRIES); +- +- // Record which 1P app launched Incognito CCT. +- if (incognitoCCTCallerId == IntentHandler.IncognitoCCTCallerId.GOOGLE_APPS) { +- String sendersPackageName = incognitoProvider.getSendersPackageName(); +- @IntentHandler.ExternalAppId +- int externalId = IntentHandler.mapPackageToExternalAppId(sendersPackageName); +- if (externalId != IntentHandler.ExternalAppId.OTHER) { +- RecordHistogram.recordEnumeratedHistogram("CustomTabs.ClientAppId.Incognito", +- externalId, IntentHandler.ExternalAppId.NUM_ENTRIES); +- } else { +- // Using package name didn't give any meaningful insight on who launched the +- // Incognito CCT, falling back to check if they provided EXTRA_APPLICATION_ID. +- externalId = +- IntentHandler.determineExternalIntentSource(incognitoProvider.getIntent()); +- RecordHistogram.recordEnumeratedHistogram("CustomTabs.ClientAppId.Incognito", +- externalId, IntentHandler.ExternalAppId.NUM_ENTRIES); +- } +- } + } + + private void recordUserAction() { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +index 9d5327aa84ce9..ca00a6a29cacf 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +@@ -21,6 +21,7 @@ import org.chromium.base.ContextUtils; + import org.chromium.base.supplier.ObservableSupplier; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.ActivityTabProvider; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.DefaultBrowserInfo; + import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl; + import org.chromium.chrome.browser.app.appmenu.DividerLineMenuItemViewBinder; +@@ -179,6 +180,9 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat + downloadItemVisible = false; + openInChromeItemVisible = false; + } ++ if (AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ downloadItemVisible = true; ++ } + + boolean isChromeScheme = url.getScheme().equals(UrlConstants.CHROME_SCHEME) + || url.getScheme().equals(UrlConstants.CHROME_NATIVE_SCHEME); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +index 6b61529be5901..65f5ef9cc129d 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java +@@ -63,6 +63,9 @@ import org.chromium.components.embedder_support.util.UrlConstants; + import org.chromium.components.version_info.VersionInfo; + import org.chromium.device.mojom.ScreenOrientationLockType; + ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++ + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + import java.util.ArrayList; +@@ -1210,7 +1213,7 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid + + @Override + public boolean isIncognito() { +- return false; ++ return AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); + } + + @Nullable +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java +index 3d2a15c9eb6c3..440a6d8685fd4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java +@@ -33,6 +33,7 @@ import org.chromium.base.annotations.NativeMethods; + import org.chromium.base.metrics.RecordHistogram; + import org.chromium.base.metrics.RecordUserAction; + import org.chromium.chrome.R; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.ChromeTabbedActivity; + import org.chromium.chrome.browser.IntentHandler; + import org.chromium.chrome.browser.app.download.home.DownloadActivity; +@@ -74,6 +75,10 @@ import org.chromium.url.GURL; + + import java.io.File; + ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.chrome.browser.preferences.Pref; ++ + /** + * A class containing some utility static methods. + */ +@@ -317,7 +322,16 @@ public class DownloadUtils { + // Offline pages isn't supported in Incognito. This should be checked before calling + // OfflinePageBridge.getForProfile because OfflinePageBridge instance will not be found + // for incognito profile. +- if (tab.isIncognito()) return false; ++ boolean always_incognito = AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); ++ if (always_incognito) { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean historyEnabledInIncognito = ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ if (historyEnabledInIncognito == false) ++ return false; ++ } else { ++ if (tab.isIncognito()) return false; ++ } + + // Check if the page url is supported for saving. Only HTTP and HTTPS pages are allowed. + if (!OfflinePageBridge.canSavePage(tab.getUrl())) return false; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java +index 401778c0e5458..61d49d0f26f7e 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java +@@ -35,6 +35,7 @@ import com.google.android.material.tabs.TabLayout.OnTabSelectedListener; + import org.chromium.base.metrics.RecordHistogram; + import org.chromium.base.metrics.RecordUserAction; + import org.chromium.base.supplier.ObservableSupplier; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.base.supplier.ObservableSupplierImpl; + import org.chromium.base.supplier.Supplier; + import org.chromium.chrome.R; +@@ -76,6 +77,12 @@ import java.io.Serializable; + import java.util.List; + import java.util.concurrent.atomic.AtomicReference; + ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.base.ContextUtils; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.chrome.browser.preferences.Pref; ++ + /** + * Combines and manages the different UI components of browsing history. + */ +@@ -157,7 +164,7 @@ public class HistoryManager implements OnMenuItemClickListener, SelectionObserve + + recordUserAction("Show"); + // If incognito placeholder is shown, we don't need to create History UI elements. +- if (mIsIncognito) { ++ if (shouldShowIncognitoPlaceholder()) { + mSelectableListLayout = null; + mRootView = getIncognitoHistoryPlaceholderView(); + return; +@@ -594,7 +601,7 @@ public class HistoryManager implements OnMenuItemClickListener, SelectionObserve + + private void swapContentView() { + boolean toHistoryClusters; +- if (mIsIncognito) { ++ if (shouldShowIncognitoPlaceholder()) { + return; + } else if (isHistoryClustersUIShowing()) { + toHistoryClusters = false; +@@ -657,11 +664,24 @@ public class HistoryManager implements OnMenuItemClickListener, SelectionObserve + && mContentView == mHistoryClustersCoordinator.getActivityContentView(); + } + ++ public boolean isIncognito() { return mIsIncognito; } ++ ++ public boolean shouldShowIncognitoPlaceholder() { ++ if (mIsIncognito && ++ AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean historyEnabledInIncognito = ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ if (historyEnabledInIncognito) return false; ++ } ++ return mIsIncognito; ++ } ++ + /** + * Called when the activity/native page is destroyed. + */ + public void onDestroyed() { +- if (mIsIncognito) { ++ if (shouldShowIncognitoPlaceholder()) { + // If Incognito placeholder is shown no need to call any destroy method. + return; + } +@@ -681,7 +701,7 @@ public class HistoryManager implements OnMenuItemClickListener, SelectionObserve + * @return True if manager handles this event, false if it decides to ignore. + */ + public boolean onBackPressed() { +- if (mIsIncognito || mSelectableListLayout == null) { ++ if (shouldShowIncognitoPlaceholder() || mSelectableListLayout == null) { + // If Incognito placeholder is shown, the back press should handled by HistoryActivity. + return false; + } else if (isHistoryClustersUIShowing()) { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java +index c5b4f80048821..77ed5ad27dac1 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryPage.java +@@ -9,6 +9,7 @@ import android.net.Uri; + + import org.chromium.base.supplier.Supplier; + import org.chromium.chrome.R; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.history_clusters.HistoryClustersConstants; + import org.chromium.chrome.browser.profiles.Profile; + import org.chromium.chrome.browser.tab.Tab; +@@ -17,6 +18,12 @@ import org.chromium.chrome.browser.ui.native_page.BasicNativePage; + import org.chromium.chrome.browser.ui.native_page.NativePageHost; + import org.chromium.components.embedder_support.util.UrlConstants; + ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.base.ContextUtils; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.chrome.browser.preferences.Pref; ++ + /** + * Native page for managing browsing history. + */ +@@ -39,6 +46,15 @@ public class HistoryPage extends BasicNativePage { + boolean isIncognito, Supplier tabSupplier, String url) { + super(host); + ++ if (isIncognito && ++ AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean historyEnabledInIncognito = ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ if (historyEnabledInIncognito == true) isIncognito = false; ++ } ++ ++ + Uri uri = Uri.parse(url); + assert uri.getHost().equals(UrlConstants.HISTORY_HOST); + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java +index 47450d033b081..0f3682c319db1 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java +@@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting; + import org.chromium.base.supplier.DestroyableObservableSupplier; + import org.chromium.base.supplier.ObservableSupplier; + import org.chromium.base.supplier.Supplier; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.app.ChromeActivity; + import org.chromium.chrome.browser.app.download.home.DownloadPage; + import org.chromium.chrome.browser.bookmarks.BookmarkPage; +@@ -226,7 +227,8 @@ public class NativePageFactory { + String url, NativePage candidatePage, Tab tab, boolean isIncognito) { + NativePage page; + +- switch (NativePage.nativePageType(url, candidatePage, isIncognito)) { ++ boolean isAlwaysIncognito = AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); ++ switch (NativePage.nativePageType(url, candidatePage, isIncognito, isAlwaysIncognito)) { + case NativePageType.NONE: + return null; + case NativePageType.CANDIDATE: +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java +index a0414fbfcad0c..df675e6fa7000 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java +@@ -35,6 +35,15 @@ import org.chromium.components.signin.metrics.SigninAccessPoint; + import org.chromium.components.sync.SyncService; + import org.chromium.url.GURL; + ++import android.content.Intent; ++import android.provider.Browser; ++import android.net.Uri; ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++import org.chromium.chrome.browser.IntentHandler; ++import org.chromium.ui.mojom.WindowOpenDisposition; ++import org.chromium.components.embedder_support.util.UrlUtilities; ++ + import java.util.HashMap; + import java.util.List; + import java.util.Map; +@@ -99,7 +108,8 @@ public class RecentTabsManager implements SyncService.SyncStateChangedListener, + */ + public RecentTabsManager(Tab tab, TabModelSelector tabModelSelector, Profile profile, + Context context, Runnable showHistoryManager) { +- mProfile = profile; ++ mProfile = profile.getOriginalProfile(); ++ profile = mProfile; + mActiveTab = tab; + mTabModelSelector = tabModelSelector; + mShowHistoryManager = showHistoryManager; +@@ -251,6 +261,22 @@ public class RecentTabsManager implements SyncService.SyncStateChangedListener, + */ + public void openRecentlyClosedTab(RecentlyClosedTab tab, int windowDisposition) { + if (mIsDestroyed) return; ++ if (AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ // allow only http/https urls ++ if (!UrlUtilities.isHttpOrHttps(tab.getUrl())) return; ++ ++ Context context = ContextUtils.getApplicationContext(); ++ Intent intent = new Intent(Intent.ACTION_VIEW, ++ Uri.parse(tab.getUrl().getSpec())); ++ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); ++ if (windowDisposition != WindowOpenDisposition.CURRENT_TAB) { ++ intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true); ++ } ++ intent.setPackage(context.getPackageName()); ++ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ++ IntentHandler.startActivityForTrustedIntent(intent); ++ return; ++ } + mTabSessionIdsRestored.put(tab.getSessionId(), true); + RecordUserAction.record("MobileRecentTabManagerRecentTabOpened"); + // Window disposition will select which tab to open. +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/IncognitoSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/IncognitoSettings.java +new file mode 100644 +index 0000000000000..b60a5b0951bb9 +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/IncognitoSettings.java +@@ -0,0 +1,160 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++package org.chromium.chrome.browser.privacy.settings; ++ ++import android.os.Bundle; ++import android.content.Context; ++import android.content.Intent; ++import android.provider.Browser; ++import android.net.Uri; ++import android.view.Menu; ++import android.view.MenuInflater; ++import android.view.MenuItem; ++ ++import androidx.preference.Preference; ++import androidx.preference.PreferenceFragmentCompat; ++import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; ++ ++import org.chromium.chrome.R; ++import org.chromium.chrome.browser.preferences.Pref; ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; ++import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager; ++import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; ++import org.chromium.chrome.browser.ApplicationLifetime; ++import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; ++import org.chromium.components.browser_ui.settings.SettingsUtils; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++ ++/** ++ * Fragment to keep track of the all the always incognito related preferences. ++ */ ++public class IncognitoSettings ++ extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener, ++ INeedSnackbarManager { ++ private SnackbarManager mSnackbarManager; ++ private Snackbar mSnackbar; ++ ++ private static final String PREF_ALWAYS_INCOGNITO = "always_incognito"; ++ private static final String PREF_INCOGNITO_TAB_HISTORY = "incognito_history"; ++ private static final String PREF_INCOGNITO_SAVE_SITE_SETTING = "incognito_save_site_setting"; ++ ++ private final PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ ++ @Override ++ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { ++ PrivacyPreferencesManagerImpl privacyPrefManager = ++ PrivacyPreferencesManagerImpl.getInstance(); ++ SettingsUtils.addPreferencesFromResource(this, R.xml.incognito_preferences); ++ getActivity().setTitle(R.string.incognito_settings_title); ++ ++ setHasOptionsMenu(true); ++ ++ updatePreferences(); ++ } ++ ++ @Override ++ public void onResume() { ++ super.onResume(); ++ updatePreferences(); ++ } ++ ++ public void updatePreferences() { ++ ChromeSwitchPreference alwaysIncognitoPref = ++ (ChromeSwitchPreference) findPreference(PREF_ALWAYS_INCOGNITO); ++ alwaysIncognitoPref.setChecked( ++ prefService.getBoolean(Pref.ALWAYS_INCOGNITO_ENABLED)); ++ alwaysIncognitoPref.setOnPreferenceChangeListener(this); ++ ++ mSnackbar = Snackbar.make(getActivity().getString(R.string.ui_relaunch_notice), ++ new SnackbarManager.SnackbarController() { ++ @Override ++ public void onDismissNoAction(Object actionData) { } ++ ++ @Override ++ public void onAction(Object actionData) { ++ ApplicationLifetime.terminate(true); ++ } ++ }, Snackbar.TYPE_NOTIFICATION, Snackbar.UMA_UNKNOWN) ++ .setSingleLine(false) ++ .setAction(getActivity().getString(R.string.relaunch), ++ /*actionData*/null) ++ .setDuration(/*durationMs*/70000); ++ ++ ChromeSwitchPreference historyInIncognitoPref = ++ (ChromeSwitchPreference) findPreference(PREF_INCOGNITO_TAB_HISTORY); ++ historyInIncognitoPref.setChecked( ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED)); ++ historyInIncognitoPref.setOnPreferenceChangeListener(this); ++ ++ ChromeSwitchPreference saveSiteSettingsPref = ++ (ChromeSwitchPreference) findPreference(PREF_INCOGNITO_SAVE_SITE_SETTING); ++ saveSiteSettingsPref.setChecked( ++ prefService.getBoolean(Pref.INCOGNITO_SAVE_SITE_SETTING_ENABLED)); ++ saveSiteSettingsPref.setOnPreferenceChangeListener(this); ++ } ++ ++ @Override ++ public boolean onPreferenceChange(Preference preference, Object newValue) { ++ String key = preference.getKey(); ++ if (PREF_ALWAYS_INCOGNITO.equals(key)) { ++ AlwaysIncognitoLinkInterceptor.setAlwaysIncognito((boolean) newValue); ++ } else if (PREF_INCOGNITO_TAB_HISTORY.equals(key)) { ++ prefService.setBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED, (boolean) newValue); ++ } else if (PREF_INCOGNITO_SAVE_SITE_SETTING.equals(key)) { ++ prefService.setBoolean(Pref.INCOGNITO_SAVE_SITE_SETTING_ENABLED, (boolean) newValue); ++ } ++ if (!mSnackbarManager.isShowing()) { ++ mSnackbarManager.showSnackbar(mSnackbar); ++ } ++ return true; ++ } ++ ++ @Override ++ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { ++ menu.clear(); ++ MenuItem help = ++ menu.add(Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help); ++ help.setIcon(VectorDrawableCompat.create( ++ getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme())); ++ } ++ ++ @Override ++ public boolean onOptionsItemSelected(MenuItem item) { ++ if (item.getItemId() == R.id.menu_id_targeted_help) { ++ Context context = getContext(); ++ ++ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/bromite/bromite/wiki/AlwaysIncognito")); ++ // Let Chromium know that this intent is from Chromium, so that it does not close the app when ++ // the user presses 'back' button. ++ intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); ++ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); ++ intent.setPackage(context.getPackageName()); ++ context.startActivity(intent); ++ return true; ++ } ++ return false; ++ } ++ ++ @Override ++ public void setSnackbarManager(SnackbarManager manager) { ++ mSnackbarManager = manager; ++ } ++} +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +index 130443d449f84..b5cf520457519 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +@@ -72,6 +72,7 @@ import org.chromium.chrome.browser.search_engines.settings.SearchEngineSettings; + import org.chromium.chrome.browser.signin.SyncConsentActivityLauncherImpl; + import org.chromium.chrome.browser.site_settings.ChromeSiteSettingsDelegate; + import org.chromium.chrome.browser.sync.SyncServiceFactory; ++import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager; + import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; + import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarManageable; + import org.chromium.components.browser_ui.accessibility.AccessibilitySettings; +@@ -322,6 +323,9 @@ public class SettingsActivity extends ChromeBaseAppCompatActivity + ((PrivacySandboxSettingsBaseFragment) fragment) + .setSnackbarManager(getSnackbarManager()); + } ++ if (fragment instanceof INeedSnackbarManager) { ++ ((INeedSnackbarManager)fragment).setSnackbarManager(mSnackbarManager); ++ } + initBackPressHandler(); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java +index 6cfc9f42401db..505e63ae4db07 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabModelObserver.java +@@ -14,6 +14,8 @@ import java.util.ArrayList; + import java.util.HashMap; + import java.util.List; + ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++ + /** + * A tab model observer for managing bulk closures. + */ +@@ -41,7 +43,8 @@ public class HistoricalTabModelObserver implements TabModelObserver { + if (tabs.isEmpty()) return; + + if (tabs.size() == 1) { +- mHistoricalTabSaver.createHistoricalTab(tabs.get(0)); ++ boolean is_always_incognito = AlwaysIncognitoLinkInterceptor.isAlwaysIncognito(); ++ mHistoricalTabSaver.createHistoricalTab(tabs.get(0), is_always_incognito); + return; + } + +@@ -72,7 +75,6 @@ public class HistoricalTabModelObserver implements TabModelObserver { + entries.add(historicalGroup); + idToGroup.put(groupId, historicalGroup); + } +- + mHistoricalTabSaver.createHistoricalBulkClosure(entries); + } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaver.java +index 0b9241a7f8741..d413e81a8dda5 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaver.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaver.java +@@ -16,7 +16,7 @@ public interface HistoricalTabSaver { + * Creates a Tab entry in TabRestoreService. + * @param tab The {@link Tab} to create an entry for. + */ +- void createHistoricalTab(Tab tab); ++ void createHistoricalTab(Tab tab, boolean is_always_incognito); + + /** + * Creates a Group or Tab entry in TabRestoreService. +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImpl.java +index 422a5716b6cd3..0ca4a9dd44fd6 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImpl.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImpl.java +@@ -26,6 +26,8 @@ import java.util.Arrays; + import java.util.Collections; + import java.util.List; + ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++ + /** + * Creates historical entries in TabRestoreService. + */ +@@ -59,10 +61,10 @@ public class HistoricalTabSaverImpl implements HistoricalTabSaver { + + // HistoricalTabSaver implementation. + @Override +- public void createHistoricalTab(Tab tab) { ++ public void createHistoricalTab(Tab tab, boolean is_always_incognito) { + if (!shouldSave(tab)) return; + +- createHistoricalTabInternal(tab); ++ createHistoricalTabInternal(tab, is_always_incognito); + } + + @Override +@@ -114,7 +116,7 @@ public class HistoricalTabSaverImpl implements HistoricalTabSaver { + + // If there is only a single valid tab remaining save it individually. + if (validEntries.size() == 1 && validEntries.get(0).isSingleTab()) { +- createHistoricalTabInternal(allTabs.get(0)); ++ createHistoricalTabInternal(allTabs.get(0), false); + return; + } + +@@ -140,11 +142,12 @@ public class HistoricalTabSaverImpl implements HistoricalTabSaver { + CollectionUtil.integerCollectionToIntArray(savedStateVersions)); + } + +- private void createHistoricalTabInternal(Tab tab) { ++ private void createHistoricalTabInternal(Tab tab, boolean is_always_incognito) { + RecordHistogram.recordEnumeratedHistogram("Tabs.RecentlyClosed.HistoricalSaverCloseType", + HistoricalSaverCloseType.TAB, HistoricalSaverCloseType.COUNT); + HistoricalTabSaverImplJni.get().createHistoricalTab( +- tab, getWebContentsState(tab).buffer(), getWebContentsState(tab).version()); ++ tab, getWebContentsState(tab).buffer(), getWebContentsState(tab).version(), ++ is_always_incognito); + } + + /** +@@ -152,7 +155,7 @@ public class HistoricalTabSaverImpl implements HistoricalTabSaver { + * internal Chrome scheme, about:blank, or a native page and it cannot be incognito. + */ + private boolean shouldSave(Tab tab) { +- if (tab.isIncognito()) return false; ++ if (tab.isIncognito() && !AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) return false; + + // {@link GURL#getScheme()} is not available in unit tests. + if (mIgnoreUrlSchemesForTesting) return true; +@@ -224,7 +227,7 @@ public class HistoricalTabSaverImpl implements HistoricalTabSaver { + + @NativeMethods + interface Natives { +- void createHistoricalTab(Tab tab, ByteBuffer state, int savedStateVersion); ++ void createHistoricalTab(Tab tab, ByteBuffer state, int savedStateVersion, boolean is_always_incognito); + void createHistoricalGroup(TabModel model, String title, Tab[] tabs, + ByteBuffer[] byteBuffers, int[] savedStationsVersions); + void createHistoricalBulkClosure(TabModel model, int[] groupIds, String[] titles, +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java +index e6263844f95a2..f1381b9bfe1e2 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java +@@ -123,6 +123,8 @@ import org.chromium.components.webapps.bottomsheet.PwaBottomSheetControllerFacto + import org.chromium.content_public.browser.WebContents; + import org.chromium.ui.base.ActivityWindowAndroid; + import org.chromium.ui.base.DeviceFormFactor; ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.ui.base.IntentRequestTracker; + import org.chromium.ui.modaldialog.ModalDialogManager; + +@@ -794,7 +796,7 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator { + mAppMenuCoordinator.getAppMenuHandler(), mActivityTabProvider, + mToolbarManager.getMenuButtonView(), () -> { + mTabCreatorManagerSupplier.get() +- .getTabCreator(/*incognito=*/false) ++ .getTabCreator(AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) + .launchUrl(NewTabPageUtils.encodeNtpUrl( + NewTabPageLaunchOrigin.WEB_FEED), + TabLaunchType.FROM_CHROME_UI); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +index 18b403cfc342e..4300ad0894ab0 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +@@ -53,6 +53,10 @@ import org.chromium.ui.base.PageTransition; + import org.chromium.ui.base.WindowAndroid; + import org.chromium.url.GURL; + ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++import org.chromium.chrome.browser.tab.TabObserver; ++ + /** + * This class creates various kinds of new tabs and adds them to the right {@link TabModel}. + */ +@@ -487,7 +491,6 @@ public class ChromeTabCreator extends TabCreator { + // TODO(crbug.com/1081924): Clean up the launches from SearchActivity/Chrome. + public Tab launchUrlFromExternalApp( + LoadUrlParams loadUrlParams, String appId, boolean forceNewTab, Intent intent) { +- assert !mIncognito; + // Don't re-use tabs for intents from Chrome. Note that this can be spoofed so shouldn't be + // relied on for anything security sensitive. + boolean isLaunchedFromChrome = TextUtils.equals(appId, mActivity.getPackageName()); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java +index 5d7832fb69496..1c86eebc01a19 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java +@@ -10,6 +10,7 @@ import androidx.annotation.Nullable; + import androidx.annotation.VisibleForTesting; + + import org.chromium.base.supplier.Supplier; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; + import org.chromium.chrome.browser.flags.ActivityType; + import org.chromium.chrome.browser.ntp.RecentlyClosedBridge; +@@ -109,6 +110,8 @@ public class TabModelSelectorImpl extends TabModelSelectorBase implements TabMod + public void onNativeLibraryReady(TabContentManager tabContentProvider) { + assert mTabContentManager == null : "onNativeLibraryReady called twice!"; + ++ AlwaysIncognitoLinkInterceptor.migrateSettingToNative(); ++ + ChromeTabCreator regularTabCreator = + (ChromeTabCreator) getTabCreatorManager().getTabCreator(false); + ChromeTabCreator incognitoTabCreator = +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +index 8caf8dae6dbf7..20cdf55ecf431 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +@@ -55,6 +55,8 @@ import org.chromium.components.embedder_support.util.UrlUtilities; + import org.chromium.content_public.browser.LoadUrlParams; + import org.chromium.url.GURL; + ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++ + import java.io.BufferedInputStream; + import java.io.ByteArrayInputStream; + import java.io.ByteArrayOutputStream; +@@ -747,6 +749,13 @@ public class TabPersistentStore { + } + } + } ++ if (AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ if (!isIncognito) { ++ Log.w(TAG, "Failed to restore tab: not in incognito mode."); ++ return; ++ } ++ } ++ + TabModel model = mTabModelSelector.getModel(isIncognito); + + if (model.isIncognito() != isIncognito) { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +index a61c435b6238a..37065641f06e0 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java +@@ -40,6 +40,7 @@ import org.chromium.base.supplier.OneshotSupplier; + import org.chromium.base.supplier.Supplier; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.ActivityTabProvider; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; + import org.chromium.chrome.browser.IntentHandler; + import org.chromium.chrome.browser.app.tab_activity_glue.TabReparentingController; + import org.chromium.chrome.browser.app.tabmodel.TabWindowManagerSingleton; +@@ -509,7 +510,8 @@ public class ToolbarManager implements UrlFocusChangeListener, ThemeColorObserve + return ret; + } + }, +- SearchEngineLogoUtils.getInstance()); ++ SearchEngineLogoUtils.getInstance(), ++ AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()); + mControlContainer = controlContainer; + assert mControlContainer != null; + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java +index 4f718f825dfdf..3e6af3acbc59c 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java +@@ -32,6 +32,9 @@ import org.chromium.chrome.browser.flags.ActivityType; + import org.chromium.components.browser_ui.widget.TintedDrawable; + import org.chromium.device.mojom.ScreenOrientationLockType; + ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++ + /** + * Stores info about a web app. + */ +@@ -46,6 +49,8 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider + private final ColorProviderImpl mColorProvider; + private final ColorProviderImpl mDarkColorProvider; + ++ private boolean mIsIncognito = false; ++ + /** + * Returns the toolbar color to use if a custom color is not specified by the webapp. + */ +@@ -77,6 +82,10 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider + mWebappExtras = webappExtras; + mWebApkExtras = webApkExtras; + mActivityType = (webApkExtras != null) ? ActivityType.WEB_APK : ActivityType.WEBAPP; ++ ++ if (AlwaysIncognitoLinkInterceptor.isAlwaysIncognito()) { ++ mIsIncognito = true; ++ } + } + + @Override +@@ -162,6 +171,11 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider + return mWebApkExtras; + } + ++ @Override ++ public boolean isIncognito() { ++ return mIsIncognito; ++ } ++ + @Override + public @ScreenOrientationLockType.EnumType int getDefaultOrientation() { + return mWebappExtras.orientation; +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index d7531ac080f86..de0c0efdddf7d 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -5140,6 +5140,10 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kOfflinePagesLivePageSharingName, + flag_descriptions::kOfflinePagesLivePageSharingDescription, kOsAndroid, + FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesLivePageSharingFeature)}, ++ {"offline-pages-auto-save", ++ flag_descriptions::kOfflinePagesAutoSaveFeatureName, ++ flag_descriptions::kOfflinePagesAutoSaveFeatureDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesAutoSaveFeature)}, + {"query-tiles", flag_descriptions::kQueryTilesName, + flag_descriptions::kQueryTilesDescription, kOsAndroid, + FEATURE_WITH_PARAMS_VALUE_TYPE(query_tiles::features::kQueryTiles, +diff --git a/chrome/browser/android/historical_tab_saver.cc b/chrome/browser/android/historical_tab_saver.cc +index 8757668fa8e58..a46771f1bd970 100644 +--- a/chrome/browser/android/historical_tab_saver.cc ++++ b/chrome/browser/android/historical_tab_saver.cc +@@ -26,6 +26,11 @@ + #include "components/sessions/core/tab_restore_service.h" + #include "content/public/browser/web_contents.h" + ++#include "chrome/common/pref_names.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "historical_tab_saver.h" ++ + using base::android::JavaParamRef; + using base::android::ScopedJavaLocalRef; + +@@ -38,7 +43,8 @@ constexpr int kInvalidGroupId = -1; + + void CreateHistoricalTab( + TabAndroid* tab_android, +- WebContentsStateByteBuffer web_contents_state_byte_buffer) { ++ WebContentsStateByteBuffer web_contents_state_byte_buffer, ++ bool is_always_incognito) { + if (!tab_android) { + return; + } +@@ -49,9 +55,14 @@ void CreateHistoricalTab( + return; + } + ++ auto* profile = Profile::FromBrowserContext(scoped_web_contents->web_contents()->GetBrowserContext()); ++ if (is_always_incognito) { ++ if (profile->GetOriginalProfile()->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) ++ profile = profile->GetOriginalProfile(); ++ } ++ + sessions::TabRestoreService* service = +- TabRestoreServiceFactory::GetForProfile(Profile::FromBrowserContext( +- scoped_web_contents->web_contents()->GetBrowserContext())); ++ TabRestoreServiceFactory::GetForProfile(profile); + if (!service) { + return; + } +@@ -243,14 +254,15 @@ static void JNI_HistoricalTabSaverImpl_CreateHistoricalTab( + JNIEnv* env, + const JavaParamRef& jtab_android, + const JavaParamRef& state, +- jint saved_state_version) { ++ jint saved_state_version, ++ jboolean is_always_incognito) { + void* data = env->GetDirectBufferAddress(state); + int size = env->GetDirectBufferCapacity(state); + + WebContentsStateByteBuffer web_contents_state = + WebContentsStateByteBuffer(data, size, (int)saved_state_version); + CreateHistoricalTab(TabAndroid::GetNativeTab(env, jtab_android), +- std::move(web_contents_state)); ++ std::move(web_contents_state), is_always_incognito); + } + + // static +diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc +index 27b043a2dff7a..d08bd3c772c6b 100644 +--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc ++++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc +@@ -343,6 +343,15 @@ ChromeAutocompleteProviderClient::GetOnDeviceTailModelService() const { + #endif // BUILDFLAG(BUILD_WITH_TFLITE_LIB) + } + ++bool ChromeAutocompleteProviderClient::IsAlwaysIncognitoEnabled() const { ++#if BUILDFLAG(IS_ANDROID) ++ if (profile_->GetPrefs()->GetBoolean(prefs::kAlwaysIncognitoEnabled)) { ++ return true; ++ } ++#endif ++ return false; ++} ++ + bool ChromeAutocompleteProviderClient::IsOffTheRecord() const { + return profile_->IsOffTheRecord(); + } +diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h +index 954fd5603bb6a..ed6f2306883e4 100644 +--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h ++++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h +@@ -84,6 +84,7 @@ class ChromeAutocompleteProviderClient : public AutocompleteProviderClient { + const override; + OnDeviceTailModelService* GetOnDeviceTailModelService() const override; + bool IsOffTheRecord() const override; ++ bool IsAlwaysIncognitoEnabled() const override; + bool IsIncognitoProfile() const override; + bool IsGuestSession() const override; + bool SearchSuggestEnabled() const override; +diff --git a/chrome/browser/autocomplete/remote_suggestions_service_factory.cc b/chrome/browser/autocomplete/remote_suggestions_service_factory.cc +index 4a1e8860b64c0..87e1a48fa2c75 100644 +--- a/chrome/browser/autocomplete/remote_suggestions_service_factory.cc ++++ b/chrome/browser/autocomplete/remote_suggestions_service_factory.cc +@@ -4,6 +4,7 @@ + + #include "chrome/browser/autocomplete/remote_suggestions_service_factory.h" + ++#include "build/build_config.h" + #include "base/no_destructor.h" + #include "chrome/browser/autocomplete/document_suggestions_service_factory.h" + #include "chrome/browser/profiles/profile.h" +@@ -39,7 +40,11 @@ RemoteSuggestionsServiceFactory::RemoteSuggestionsServiceFactory() + : ProfileKeyedServiceFactory( + "RemoteSuggestionsService", + ProfileSelections::Builder() ++#if BUILDFLAG(IS_ANDROID) ++ .WithRegular(ProfileSelection::kOriginalOnlyAndAlwaysIncognito) ++#else + .WithRegular(ProfileSelection::kOriginalOnly) ++#endif + // TODO(crbug.com/1418376): Check if this service is needed in + // Guest mode. + .WithGuest(ProfileSelection::kOriginalOnly) +diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc +index 6f842a7535a56..241d70c4052eb 100644 +--- a/chrome/browser/content_settings/host_content_settings_map_factory.cc ++++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc +@@ -15,6 +15,7 @@ + #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/search_engines/template_url_service_factory.h" + #include "chrome/common/buildflags.h" ++#include "chrome/common/pref_names.h" + #include "components/content_settings/core/browser/content_settings_pref_provider.h" + #include "components/content_settings/core/browser/host_content_settings_map.h" + #include "components/permissions/features.h" +@@ -119,9 +120,25 @@ scoped_refptr + should_record_metrics && ash::ProfileHelper::IsUserProfile(profile); + #endif // BUILDFLAG(IS_CHROMEOS_ASH) + ++ bool always_incognito_enabled = false; ++ bool force_save_site_settings = false; ++ ++#if BUILDFLAG(IS_ANDROID) ++ PrefService* prefService = original_profile->GetPrefs(); ++ if (prefService->GetBoolean(prefs::kAlwaysIncognitoEnabled)) { ++ always_incognito_enabled = true; ++ } ++ ++ if (prefService->GetBoolean(prefs::kIncognitoSaveSiteSettingEnabled)) { ++ profile = original_profile; ++ force_save_site_settings = true; ++ } ++#endif ++ + scoped_refptr settings_map(new HostContentSettingsMap( + profile->GetPrefs(), +- profile->IsOffTheRecord() || profile->IsGuestSession(), ++ !force_save_site_settings && (profile->IsOffTheRecord() || profile->IsGuestSession()), ++ force_save_site_settings, + /*store_last_modified=*/true, profile->ShouldRestoreOldSessionCookies(), + should_record_metrics)); + +@@ -131,6 +148,9 @@ scoped_refptr + HostContentSettingsMap::WEBUI_ALLOWLIST_PROVIDER, + std::move(allowlist_provider)); + ++ if (always_incognito_enabled) ++ return settings_map; ++ + #if BUILDFLAG(ENABLE_EXTENSIONS) + // These must be registered before before the HostSettings are passed over to + // the IOThread. Simplest to do this on construction. +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 41f7c6e21de77..09846683d9e1e 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -4116,6 +4116,12 @@ const char kOfflinePagesLivePageSharingDescription[] = + "Enables to share current loaded page as offline page by saving as MHTML " + "first."; + ++const char kOfflinePagesAutoSaveFeatureName[] = ++ "Enables autosave of offline page"; ++const char kOfflinePagesAutoSaveFeatureDescription[] = ++ "Enables autosave of offline page, as automatic switching in case " ++ "the device goes offline."; ++ + const char kPageInfoHistoryName[] = "Page info history"; + const char kPageInfoHistoryDescription[] = + "Enable a history sub page to the page info menu, and a button to forget " +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 36b991b2f4c7c..fff998efaf7cd 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -2414,6 +2414,9 @@ extern const char kOmahaMinSdkVersionAndroidDescription[]; + extern const char kOmahaMinSdkVersionAndroidMinSdk1Description[]; + extern const char kOmahaMinSdkVersionAndroidMinSdk1000Description[]; + ++extern const char kOfflinePagesAutoSaveFeatureName[]; ++extern const char kOfflinePagesAutoSaveFeatureDescription[]; ++ + extern const char kPageInfoHistoryName[]; + extern const char kPageInfoHistoryDescription[]; + +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +index 1ba904380664c..cf06093694030 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -554,8 +554,8 @@ BASE_FEATURE(kCCTFeatureUsage, + BASE_FEATURE(kCCTIncognito, "CCTIncognito", base::FEATURE_ENABLED_BY_DEFAULT); + + BASE_FEATURE(kCCTIncognitoAvailableToThirdParty, +- "CCTIncognitoAvailableToThirdParty", +- base::FEATURE_DISABLED_BY_DEFAULT); ++ "CCTIncognitoAvailableToThirdParty", // must be enabled ++ base::FEATURE_ENABLED_BY_DEFAULT); // in Bromite + + BASE_FEATURE(kCCTIntentFeatureOverrides, + "CCTIntentFeatureOverrides", +diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc +index e199e7be077cb..fba6e924e6772 100644 +--- a/chrome/browser/history/history_tab_helper.cc ++++ b/chrome/browser/history/history_tab_helper.cc +@@ -38,6 +38,9 @@ + #include "chrome/browser/ui/android/tab_model/tab_model_list.h" + #include "components/feed/core/v2/public/feed_api.h" + #include "components/feed/core/v2/public/feed_service.h" ++#include "chrome/common/pref_names.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" + #else + #include "chrome/browser/ui/browser.h" + #include "chrome/browser/ui/browser_finder.h" +@@ -476,6 +479,13 @@ void HistoryTabHelper::TitleWasSet(NavigationEntry* entry) { + history::HistoryService* HistoryTabHelper::GetHistoryService() { + Profile* profile = + Profile::FromBrowserContext(web_contents()->GetBrowserContext()); ++ ++#if BUILDFLAG(IS_ANDROID) ++ if (profile->GetOriginalProfile()->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) { ++ return HistoryServiceFactory::GetForProfile(profile, ServiceAccessType::IMPLICIT_ACCESS); ++ } ++#endif ++ + if (profile->IsOffTheRecord()) + return nullptr; + +@@ -483,6 +493,16 @@ history::HistoryService* HistoryTabHelper::GetHistoryService() { + profile, ServiceAccessType::IMPLICIT_ACCESS); + } + ++// static ++void HistoryTabHelper::RegisterProfilePrefs(PrefRegistrySimple* registry) { ++#if BUILDFLAG(IS_ANDROID) ++ registry->RegisterBooleanPref(prefs::kIncognitoTabHistoryEnabled, ++ /*default_value=*/false); ++ registry->RegisterBooleanPref(prefs::kIncognitoSaveSiteSettingEnabled, ++ /*default_value=*/false); ++#endif ++} ++ + void HistoryTabHelper::WebContentsDestroyed() { + translate_observation_.Reset(); + +diff --git a/chrome/browser/history/history_tab_helper.h b/chrome/browser/history/history_tab_helper.h +index 859f9067ee50c..df6f3c06285b6 100644 +--- a/chrome/browser/history/history_tab_helper.h ++++ b/chrome/browser/history/history_tab_helper.h +@@ -14,6 +14,8 @@ + #include "content/public/browser/web_contents_observer.h" + #include "content/public/browser/web_contents_user_data.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" + + namespace history { + struct HistoryAddPageArgs; +@@ -53,6 +55,11 @@ class HistoryTabHelper + force_eligible_tab_for_testing_ = force; + } + ++ static void RegisterProfilePrefs(PrefRegistrySimple* registry); ++ ++ // Helper function to return the history service. May return null. ++ history::HistoryService* GetHistoryService(); ++ + private: + explicit HistoryTabHelper(content::WebContents* web_contents); + friend class content::WebContentsUserData; +@@ -85,9 +92,6 @@ class HistoryTabHelper + void OnLanguageDetermined( + const translate::LanguageDetectionDetails& details) override; + +- // Helper function to return the history service. May return null. +- history::HistoryService* GetHistoryService(); +- + // Returns true if our observed web contents is an eligible tab. + bool IsEligibleTab(const history::HistoryAddPageArgs& add_page_args) const; + +diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc +index e567a53a881eb..7de1a718b7044 100644 +--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc ++++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc +@@ -47,6 +47,9 @@ + #include "content/public/browser/web_contents.h" + #include "net/base/filename_util.h" + #include "url/android/gurl_android.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" + + using base::android::ConvertJavaStringToUTF8; + using base::android::ConvertUTF16ToJavaString; +@@ -782,9 +785,15 @@ void OfflinePageBridge::GetPageByOfflineIdDone( + } + + if (offline_page_model_->IsArchiveInInternalDir(offline_page->file_path)) { ++ bool is_trusted = true; ++ // in always incognito, never trust input file (show file name in url) ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key_); ++ if (profile_key->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) ++ is_trusted = false; ++ + ValidateFileCallback(launch_location, j_callback_obj, + offline_page->offline_id, offline_page->url, +- offline_page->file_path, true /* is_trusted*/); ++ offline_page->file_path, is_trusted); + return; + } + +diff --git a/chrome/browser/offline_pages/android/offline_page_model_factory.cc b/chrome/browser/offline_pages/android/offline_page_model_factory.cc +index 3c0f1cd75db59..de4c670b29b80 100644 +--- a/chrome/browser/offline_pages/android/offline_page_model_factory.cc ++++ b/chrome/browser/offline_pages/android/offline_page_model_factory.cc +@@ -24,6 +24,9 @@ + #include "components/keyed_service/core/simple_dependency_manager.h" + #include "components/offline_pages/core/model/offline_page_model_taskified.h" + #include "components/offline_pages/core/offline_page_metadata_store.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" + + namespace offline_pages { + +@@ -55,13 +58,15 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + scoped_refptr background_task_runner = + base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); + ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key)->GetOriginalKey(); ++ + base::FilePath store_path = +- key->GetPath().Append(chrome::kOfflinePageMetadataDirname); ++ profile_key->GetPath().Append(chrome::kOfflinePageMetadataDirname); + std::unique_ptr metadata_store( + new OfflinePageMetadataStore(background_task_runner, store_path)); + + base::FilePath persistent_archives_dir = +- key->GetPath().Append(chrome::kOfflinePageArchivesDirname); ++ profile_key->GetPath().Append(chrome::kOfflinePageArchivesDirname); + // If base::PathService::Get returns false, the temporary_archives_dir will be + // empty, and no temporary pages will be saved during this chrome lifecycle. + base::FilePath temporary_archives_dir; +@@ -70,7 +75,6 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + temporary_archives_dir.Append(chrome::kOfflinePageArchivesDirname); + } + +- ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key); + auto archive_manager = std::make_unique( + temporary_archives_dir, persistent_archives_dir, + DownloadPrefs::GetDefaultDownloadDirectory(), background_task_runner, +@@ -88,4 +92,14 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + return model; + } + ++SimpleFactoryKey* OfflinePageModelFactory::GetKeyToUse( ++ SimpleFactoryKey* key) const { ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key); ++ if (profile_key->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == false) { ++ return SimpleKeyedServiceFactory::GetKeyToUse(key); ++ } ++ ++ return profile_key->GetOriginalKey(); ++} ++ + } // namespace offline_pages +diff --git a/chrome/browser/offline_pages/android/request_coordinator_factory.cc b/chrome/browser/offline_pages/android/request_coordinator_factory.cc +index 10cf16f6d57c3..f13b974706496 100644 +--- a/chrome/browser/offline_pages/android/request_coordinator_factory.cc ++++ b/chrome/browser/offline_pages/android/request_coordinator_factory.cc +@@ -19,6 +19,7 @@ + #include "chrome/browser/ui/android/tab_model/tab_model.h" + #include "chrome/browser/ui/android/tab_model/tab_model_list.h" + #include "chrome/common/chrome_constants.h" ++#include "components/keyed_service/content/browser_context_dependency_manager.h" + #include "components/offline_pages/core/background/offliner.h" + #include "components/offline_pages/core/background/offliner_policy.h" + #include "components/offline_pages/core/background/request_coordinator.h" +@@ -28,6 +29,11 @@ + #include "components/offline_pages/core/offline_page_feature.h" + #include "content/public/browser/web_contents.h" + ++#include "chrome/browser/profiles/incognito_helpers.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" ++ + namespace network { + class NetworkQualityTracker; + } +@@ -65,14 +71,8 @@ class ActiveTabInfo : public RequestCoordinator::ActiveTabInfo { + } // namespace + + RequestCoordinatorFactory::RequestCoordinatorFactory() +- : ProfileKeyedServiceFactory( +- "OfflineRequestCoordinator", +- ProfileSelections::Builder() +- .WithRegular(ProfileSelection::kOriginalOnly) +- // TODO(crbug.com/1418376): Check if this service is needed in +- // Guest mode. +- .WithGuest(ProfileSelection::kOriginalOnly) +- .Build()) { ++ : BrowserContextKeyedServiceFactory("OfflineRequestCoordinator", ++ BrowserContextDependencyManager::GetInstance()) { + // Depends on OfflinePageModelFactory in SimpleDependencyManager. + } + +@@ -91,6 +91,12 @@ RequestCoordinator* RequestCoordinatorFactory::GetForBrowserContext( + + KeyedService* RequestCoordinatorFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { ++ if (context->IsOffTheRecord() && ++ Profile::FromBrowserContext(context)->GetOriginalProfile() ++ ->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == false) { ++ // do not track history in incognito mode if preference is disabled ++ return nullptr; ++ } + std::unique_ptr policy(new OfflinerPolicy()); + std::unique_ptr offliner; + OfflinePageModel* model = +@@ -123,4 +129,16 @@ KeyedService* RequestCoordinatorFactory::BuildServiceInstanceFor( + return request_coordinator; + } + ++content::BrowserContext* ++RequestCoordinatorFactory::GetBrowserContextToUse( ++ content::BrowserContext* context) const { ++ if (Profile::FromBrowserContext(context)->GetOriginalProfile() ++ ->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == false) { ++ return BrowserContextKeyedServiceFactory::GetBrowserContextToUse(context); ++ } ++ ++ return chrome::GetBrowserContextRedirectedInIncognito(context); ++} ++ ++ + } // namespace offline_pages +diff --git a/chrome/browser/offline_pages/offline_page_model_factory.h b/chrome/browser/offline_pages/offline_page_model_factory.h +index 98e1a42f20b36..86d76f9a3e7d8 100644 +--- a/chrome/browser/offline_pages/offline_page_model_factory.h ++++ b/chrome/browser/offline_pages/offline_page_model_factory.h +@@ -48,6 +48,7 @@ class OfflinePageModelFactory : public SimpleKeyedServiceFactory { + + std::unique_ptr BuildServiceInstanceFor( + SimpleFactoryKey* key) const override; ++ SimpleFactoryKey* GetKeyToUse(SimpleFactoryKey* key) const override; + }; + + } // namespace offline_pages +diff --git a/chrome/browser/offline_pages/recent_tab_helper.cc b/chrome/browser/offline_pages/recent_tab_helper.cc +index 4fec7898e108e..251e8917b22fc 100644 +--- a/chrome/browser/offline_pages/recent_tab_helper.cc ++++ b/chrome/browser/offline_pages/recent_tab_helper.cc +@@ -28,6 +28,11 @@ + #include "content/public/browser/navigation_entry.h" + #include "content/public/browser/navigation_handle.h" + ++#include "chrome/browser/profiles/profile.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" ++ + namespace { + class DefaultRecentTabHelperDelegate + : public offline_pages::RecentTabHelper::Delegate { +@@ -180,6 +185,14 @@ bool RecentTabHelper::EnsureInitialized() { + // WebContents with its origin as well. + snapshots_enabled_ = !tab_id_.empty() && + !web_contents()->GetBrowserContext()->IsOffTheRecord(); ++ if (!tab_id_.empty() && web_contents()->GetBrowserContext()->IsOffTheRecord()) { ++ if (Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ++ ->GetOriginalProfile() ++ ->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == true) { ++ snapshots_enabled_ = true; ++ incognito_tab_history_enabled_ = true; ++ } ++ } + + if (snapshots_enabled_) { + page_model_ = OfflinePageModelFactory::GetForBrowserContext( +@@ -456,7 +469,11 @@ void RecentTabHelper::ContinueSnapshotWithIdsToPurge( + void RecentTabHelper::ContinueSnapshotAfterPurge( + SnapshotProgressInfo* snapshot_info, + OfflinePageModel::DeletePageResult result) { +- if (result != OfflinePageModel::DeletePageResult::SUCCESS) { ++ // remove snapshot save of recent tab if always incognito mode is active ++ // so recents tab list is empty at every startup ++ // the user can choose to disable the feature ++ if (incognito_tab_history_enabled_ || !base::FeatureList::IsEnabled(offline_pages::kOfflinePagesAutoSaveFeature) ++ || result != OfflinePageModel::DeletePageResult::SUCCESS) { + ReportSnapshotCompleted(snapshot_info, false); + return; + } +diff --git a/chrome/browser/offline_pages/recent_tab_helper.h b/chrome/browser/offline_pages/recent_tab_helper.h +index 9567d334d86f1..697aed8a8a4c2 100644 +--- a/chrome/browser/offline_pages/recent_tab_helper.h ++++ b/chrome/browser/offline_pages/recent_tab_helper.h +@@ -146,6 +146,9 @@ class RecentTabHelper + // Not page-specific. + bool snapshots_enabled_ = false; + ++ // If true, tab history in incognito mode is enabled ++ bool incognito_tab_history_enabled_ = false; ++ + // Snapshot progress information for an ongoing snapshot requested by + // downloads. Null if there's no ongoing request. + std::unique_ptr downloads_ongoing_snapshot_info_; +diff --git a/chrome/browser/offline_pages/request_coordinator_factory.h b/chrome/browser/offline_pages/request_coordinator_factory.h +index 7d68579a86760..be37d7fa33442 100644 +--- a/chrome/browser/offline_pages/request_coordinator_factory.h ++++ b/chrome/browser/offline_pages/request_coordinator_factory.h +@@ -18,7 +18,7 @@ namespace offline_pages { + class RequestCoordinator; + + // A factory to create one unique RequestCoordinator. +-class RequestCoordinatorFactory : public ProfileKeyedServiceFactory { ++class RequestCoordinatorFactory : public BrowserContextKeyedServiceFactory { + public: + static RequestCoordinatorFactory* GetInstance(); + static RequestCoordinator* GetForBrowserContext( +@@ -36,6 +36,8 @@ class RequestCoordinatorFactory : public ProfileKeyedServiceFactory { + + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; ++ content::BrowserContext* GetBrowserContextToUse( ++ content::BrowserContext* context) const override; + }; + + } // namespace offline_pages +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index 1944adea64d4c..8f75f154b981f 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -236,6 +236,7 @@ + + #if BUILDFLAG(IS_ANDROID) + #include "chrome/browser/accessibility/accessibility_prefs/android/accessibility_prefs_controller.h" ++#include "chrome/browser/history/history_tab_helper.h" + #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h" + #include "chrome/browser/android/ntp/recent_tabs_page_prefs.h" + #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h" +@@ -1796,6 +1797,10 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, + usage_stats::UsageStatsBridge::RegisterProfilePrefs(registry); + variations::VariationsService::RegisterProfilePrefs(registry); + webapps::InstallPromptPrefs::RegisterProfilePrefs(registry); ++ // register incognito pref ++ registry->RegisterBooleanPref(prefs::kAlwaysIncognitoEnabled, ++ /*default_value=*/false); ++ HistoryTabHelper::RegisterProfilePrefs(registry); + #else // BUILDFLAG(IS_ANDROID) + bookmarks_webui::RegisterProfilePrefs(registry); + browser_sync::ForeignSessionHandler::RegisterProfilePrefs(registry); +diff --git a/chrome/browser/profiles/profile_selections.cc b/chrome/browser/profiles/profile_selections.cc +index 7ecd897db8830..4ad14b1003942 100644 +--- a/chrome/browser/profiles/profile_selections.cc ++++ b/chrome/browser/profiles/profile_selections.cc +@@ -5,6 +5,9 @@ + #include "chrome/browser/profiles/profile_selections.h" + + #include "base/memory/ptr_util.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" + #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/profiles/profile_types_ash.h" + #include "components/profile_metrics/browser_profile_type.h" +@@ -110,6 +113,13 @@ Profile* ProfileSelections::ApplyProfileSelection(Profile* profile) const { + return nullptr; + case ProfileSelection::kOriginalOnly: + return profile->IsOffTheRecord() ? nullptr : profile; ++#if BUILDFLAG(IS_ANDROID) ++ case ProfileSelection::kOriginalOnlyAndAlwaysIncognito: ++ return profile->IsOffTheRecord() && ++ !(profile->GetOriginalProfile() ++ ->GetPrefs() ++ ->GetBoolean(prefs::kAlwaysIncognitoEnabled)) ? nullptr : profile; ++#endif + case ProfileSelection::kOwnInstance: + return profile; + case ProfileSelection::kRedirectedToOriginal: +diff --git a/chrome/browser/profiles/profile_selections.h b/chrome/browser/profiles/profile_selections.h +index 8eb83625919ce..df626e2ebf430 100644 +--- a/chrome/browser/profiles/profile_selections.h ++++ b/chrome/browser/profiles/profile_selections.h +@@ -6,7 +6,7 @@ + #define CHROME_BROWSER_PROFILES_PROFILE_SELECTIONS_H_ + + #include "base/feature_list.h" +- ++#include "build/build_config.h" + #include "third_party/abseil-cpp/absl/types/optional.h" + + class Profile; +@@ -27,6 +27,11 @@ bool AreKeyedServicesDisabledForProfileByDefault(const Profile* profile); + enum class ProfileSelection { + kNone, // Original: No Profile -- OTR: No Profile + kOriginalOnly, // Original: Self -- OTR: No Profile ++#if BUILDFLAG(IS_ANDROID) ++ kOriginalOnlyAndAlwaysIncognito, ++ // Original: Self -- OTR: Self (with AlwaysIncognito ON) ++ // -- OTR: No Profile (with AlwaysIncognito OFF) ++#endif + kOwnInstance, // Original: Self -- OTR: Self + kRedirectedToOriginal, // Original: Self -- OTR: Original + kOffTheRecordOnly // Original: No Profile -- OTR: Self +diff --git a/chrome/browser/ui/android/native_page/BUILD.gn b/chrome/browser/ui/android/native_page/BUILD.gn +index 259376bfd3214..777c799e0910d 100644 +--- a/chrome/browser/ui/android/native_page/BUILD.gn ++++ b/chrome/browser/ui/android/native_page/BUILD.gn +@@ -28,7 +28,9 @@ robolectric_library("junit") { + + deps = [ + ":java", ++ "//base:base_java", + "//base:base_junit_test_support", + "//third_party/junit", ++ "//components/embedder_support/android:util_java", + ] + } +diff --git a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java +index d81c018376715..3e61a38b7ab5c 100644 +--- a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java ++++ b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java +@@ -16,6 +16,8 @@ import org.chromium.url.GURL; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.base.ContextUtils; ++ + /** + * An interface for pages that will be using Android views instead of html/rendered Web content. + */ +@@ -120,12 +122,12 @@ public interface NativePage { + */ + @Deprecated // Use GURL-variant instead. + static boolean isNativePageUrl(String url, boolean isIncognito) { +- return nativePageType(url, null, isIncognito) != NativePageType.NONE; ++ return nativePageType(url, null, isIncognito, false) != NativePageType.NONE; + } + + static boolean isNativePageUrl(GURL url, boolean isIncognito) { + return url != null +- && nativePageType(url.getHost(), url.getScheme(), null, isIncognito) ++ && nativePageType(url.getHost(), url.getScheme(), null, isIncognito, false) + != NativePageType.NONE; + } + +@@ -137,11 +139,12 @@ public interface NativePage { + */ + // TODO(crbug/783819) - Convert to using GURL. + static @NativePageType int nativePageType( +- String url, NativePage candidatePage, boolean isIncognito) { ++ String url, NativePage candidatePage, boolean isIncognito, ++ boolean isAlwaysIncognito) { + if (url == null) return NativePageType.NONE; + + Uri uri = Uri.parse(url); +- return nativePageType(uri.getHost(), uri.getScheme(), candidatePage, isIncognito); ++ return nativePageType(uri.getHost(), uri.getScheme(), candidatePage, isIncognito, isAlwaysIncognito); + } + + /** +@@ -150,7 +153,7 @@ public interface NativePage { + * @return Type of the native page defined in {@link NativePageType}. + */ + private static @NativePageType int nativePageType( +- String host, String scheme, NativePage candidatePage, boolean isIncognito) { ++ String host, String scheme, NativePage candidatePage, boolean isIncognito, boolean isAlwaysIncognito) { + if (!UrlConstants.CHROME_NATIVE_SCHEME.equals(scheme) + && !UrlConstants.CHROME_SCHEME.equals(scheme)) { + return NativePageType.NONE; +@@ -168,7 +171,8 @@ public interface NativePage { + return NativePageType.DOWNLOADS; + } else if (UrlConstants.HISTORY_HOST.equals(host)) { + return NativePageType.HISTORY; +- } else if (UrlConstants.RECENT_TABS_HOST.equals(host) && !isIncognito) { ++ } else if (UrlConstants.RECENT_TABS_HOST.equals(host) && ++ (!isIncognito || isAlwaysIncognito)) { + return NativePageType.RECENT_TABS; + } else if (UrlConstants.EXPLORE_HOST.equals(host)) { + return NativePageType.EXPLORE; +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index aa35bda9b1066..55d3ab59aca1f 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -1408,6 +1408,37 @@ Your Google account may have other forms of browsing history like searches and a + + Clears history from all synced devices + ++ ++ ++ Always incognito mode ++ ++ ++ Incognito navigation settings ++ ++ ++ Always open links in incognito ++ ++ ++ Opens links in incognito tabs when you click on new tab or on a link ++ ++ ++ Relaunch ++ ++ ++ Your changes will take effect the next time you relaunch Bromite. ++ ++ ++ Enable history ++ ++ ++ Record history even in incognito mode ++ ++ ++ Remember site settings ++ ++ ++ Remember site settings changes in incognito mode ++ + + <link1>Search history</link1> and <link2>other forms of activity</link2> may be saved in your Google Account when you’re signed in. You can delete them anytime. + +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java +index 3f4e661754726..14639486ac4c8 100644 +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java +@@ -178,6 +178,7 @@ public class LocationBarModel implements ToolbarDataProvider, LocationBarDataPro + protected String mFormattedFullUrl; + protected String mUrlForDisplay; + private boolean mOmniboxUpdatedConnectionSecurityIndicatorsEnabled; ++ private boolean mIsAlwaysIncognito; + + // notifyUrlChanged and notifySecurityStateChanged are usually called 3 times across a same + // document navigation. The first call is usually necessary, which updates the UrlBar to reflect +@@ -200,7 +201,9 @@ public class LocationBarModel implements ToolbarDataProvider, LocationBarDataPro + public LocationBarModel(Context context, NewTabPageDelegate newTabPageDelegate, + @NonNull UrlFormatter urlFormatter, @NonNull ProfileProvider profileProvider, + @NonNull OfflineStatus offlineStatus, +- @NonNull SearchEngineLogoUtils searchEngineLogoUtils) { ++ @NonNull SearchEngineLogoUtils searchEngineLogoUtils, ++ boolean isAlwaysIncognito) { ++ mIsAlwaysIncognito = isAlwaysIncognito; // (uazo) to do, check + mContext = context; + mNtpDelegate = newTabPageDelegate; + mUrlFormatter = urlFormatter; +diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn +index c8fd54fdf5650..fb7bde00d9d21 100644 +--- a/chrome/browser/ui/messages/android/BUILD.gn ++++ b/chrome/browser/ui/messages/android/BUILD.gn +@@ -24,6 +24,7 @@ android_library("java") { + srcjar_deps = [ ":jni_headers" ] + sources = [ + "java/src/org/chromium/chrome/browser/ui/messages/infobar/SimpleConfirmInfoBarBuilder.java", ++ "java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java", + "java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java", + "java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarCollection.java", + "java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java", +diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java +new file mode 100644 +index 0000000000000..18703fa8bf83a +--- /dev/null ++++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java +@@ -0,0 +1,27 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++package org.chromium.chrome.browser.ui.messages.snackbar; ++ ++import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; ++ ++/** ++ * An interface that allows using snackbars in the settings ++ */ ++public interface INeedSnackbarManager { ++ void setSnackbarManager(SnackbarManager manager); ++} +diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h +index da940b12a103c..9f29945d75551 100644 +--- a/chrome/common/pref_names.h ++++ b/chrome/common/pref_names.h +@@ -3942,6 +3942,12 @@ inline constexpr char kOutOfProcessSystemDnsResolutionEnabled[] = + "net.out_of_process_system_dns_resolution_enabled"; + #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) + ++#if BUILDFLAG(IS_ANDROID) ++inline constexpr char kAlwaysIncognitoEnabled[] = "always_incognito_enabled"; ++inline constexpr char kIncognitoSaveSiteSettingEnabled[] = "incognito_tab_history_enabled"; ++inline constexpr char kIncognitoTabHistoryEnabled[] = "incognito_site_setting_enabled"; ++#endif ++ + // A list of hostnames to disable HTTPS Upgrades / HTTPS-First Mode warnings on. + inline constexpr char kHttpAllowlist[] = "https_upgrades.policy.http_allowlist"; + +diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc +index 362a28af89211..37ba829e74723 100644 +--- a/components/content_settings/core/browser/content_settings_pref_provider.cc ++++ b/components/content_settings/core/browser/content_settings_pref_provider.cc +@@ -89,10 +89,12 @@ void PrefProvider::RegisterProfilePrefs( + + PrefProvider::PrefProvider(PrefService* prefs, + bool off_the_record, ++ bool force_save_site_settings, + bool store_last_modified, + bool restore_session) + : prefs_(prefs), + off_the_record_(off_the_record), ++ force_save_site_settings_(force_save_site_settings), + store_last_modified_(store_last_modified), + clock_(base::DefaultClock::GetInstance()) { + TRACE_EVENT_BEGIN("startup", "PrefProvider::PrefProvider"); +@@ -115,10 +117,14 @@ PrefProvider::PrefProvider(PrefService* prefs, + WebsiteSettingsRegistry* website_settings = + WebsiteSettingsRegistry::GetInstance(); + for (const WebsiteSettingsInfo* info : *website_settings) { ++ bool save_site_settings = force_save_site_settings_ && ++ info->incognito_behavior() == WebsiteSettingsInfo::INHERIT_IN_INCOGNITO; + content_settings_prefs_.insert(std::make_pair( + info->type(), std::make_unique( + info->type(), prefs_, &pref_change_registrar_, +- info->pref_name(), off_the_record_, restore_session, ++ info->pref_name(), ++ off_the_record_ && !save_site_settings, ++ restore_session, + base::BindRepeating(&PrefProvider::Notify, + base::Unretained(this))))); + } +diff --git a/components/content_settings/core/browser/content_settings_pref_provider.h b/components/content_settings/core/browser/content_settings_pref_provider.h +index 12bf884471737..2ad048cef58a1 100644 +--- a/components/content_settings/core/browser/content_settings_pref_provider.h ++++ b/components/content_settings/core/browser/content_settings_pref_provider.h +@@ -37,6 +37,7 @@ class PrefProvider : public UserModifiableProvider { + + PrefProvider(PrefService* prefs, + bool off_the_record, ++ bool force_save_site_settings, + bool store_last_modified, + bool restore_session); + +@@ -113,6 +114,7 @@ class PrefProvider : public UserModifiableProvider { + raw_ptr prefs_; + + const bool off_the_record_; ++ const bool force_save_site_settings_; + + bool store_last_modified_; + +diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc +index 58e51dacd5b35..27dc8d6d7546c 100644 +--- a/components/content_settings/core/browser/host_content_settings_map.cc ++++ b/components/content_settings/core/browser/host_content_settings_map.cc +@@ -280,6 +280,7 @@ struct ContentSettingEntry { + + HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs, + bool is_off_the_record, ++ bool force_save_site_settings, + bool store_last_modified, + bool restore_session, + bool should_record_metrics) +@@ -289,6 +290,7 @@ HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs, + #endif + prefs_(prefs), + is_off_the_record_(is_off_the_record), ++ force_save_site_settings_(force_save_site_settings), + store_last_modified_(store_last_modified), + allow_invalid_secondary_pattern_for_testing_(false), + clock_(base::DefaultClock::GetInstance()) { +@@ -301,7 +303,7 @@ HostContentSettingsMap::HostContentSettingsMap(PrefService* prefs, + policy_provider->AddObserver(this); + + auto pref_provider_ptr = std::make_unique( +- prefs_, is_off_the_record_, store_last_modified_, restore_session); ++ prefs_, is_off_the_record_, force_save_site_settings_, store_last_modified_, restore_session); + pref_provider_ = pref_provider_ptr.get(); + content_settings_providers_[PREF_PROVIDER] = std::move(pref_provider_ptr); + user_modifiable_providers_.push_back(pref_provider_); +diff --git a/components/content_settings/core/browser/host_content_settings_map.h b/components/content_settings/core/browser/host_content_settings_map.h +index a853d29761f4d..c35cbc76df2bf 100644 +--- a/components/content_settings/core/browser/host_content_settings_map.h ++++ b/components/content_settings/core/browser/host_content_settings_map.h +@@ -103,6 +103,7 @@ class HostContentSettingsMap : public content_settings::Observer, + // profile or a guest session. + HostContentSettingsMap(PrefService* prefs, + bool is_off_the_record, ++ bool force_save_site_settings, + bool store_last_modified, + bool restore_session, + bool should_record_metrics); +@@ -524,6 +525,8 @@ class HostContentSettingsMap : public content_settings::Observer, + // Whether this settings map is for an incognito or guest session. + bool is_off_the_record_; + ++ bool force_save_site_settings_ = false; ++ + // Whether ContentSettings in the PrefProvider will store a last_modified + // timestamp. + bool store_last_modified_; +diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc +index 0fd9e4f89e427..9f0a6a7792016 100644 +--- a/components/offline_pages/core/offline_page_feature.cc ++++ b/components/offline_pages/core/offline_page_feature.cc +@@ -44,6 +44,9 @@ BASE_FEATURE(kOfflinePagesNetworkStateLikelyUnknown, + "OfflinePagesNetworkStateLikelyUnknown", + base::FEATURE_DISABLED_BY_DEFAULT); + ++const base::Feature kOfflinePagesAutoSaveFeature{ ++ "OfflinePagesAutoSaveEnabled", base::FEATURE_DISABLED_BY_DEFAULT}; ++ + const char kPrefetchingOfflinePagesExperimentsOption[] = "exp"; + + bool IsOfflinePagesCTEnabled() { +diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h +index d9715f87577a1..3603a438e11fe 100644 +--- a/components/offline_pages/core/offline_page_feature.h ++++ b/components/offline_pages/core/offline_page_feature.h +@@ -19,6 +19,7 @@ BASE_DECLARE_FEATURE(kOfflinePagesInDownloadHomeOpenInCctFeature); + BASE_DECLARE_FEATURE(kOfflinePagesDescriptiveFailStatusFeature); + BASE_DECLARE_FEATURE(kOnTheFlyMhtmlHashComputationFeature); + BASE_DECLARE_FEATURE(kOfflinePagesNetworkStateLikelyUnknown); ++extern const base::Feature kOfflinePagesAutoSaveFeature; + + // The parameter name used to find the experiment tag for prefetching offline + // pages. +diff --git a/components/omnibox/browser/autocomplete_provider_client.cc b/components/omnibox/browser/autocomplete_provider_client.cc +index 5682ca1c935bb..4757f1dd91791 100644 +--- a/components/omnibox/browser/autocomplete_provider_client.cc ++++ b/components/omnibox/browser/autocomplete_provider_client.cc +@@ -29,3 +29,7 @@ base::WeakPtr + AutocompleteProviderClient::GetWeakPtr() { + return nullptr; + } ++ ++bool AutocompleteProviderClient::IsAlwaysIncognitoEnabled() const { ++ return false; ++} +diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h +index b47affe8a2096..3009138e5e1b7 100644 +--- a/components/omnibox/browser/autocomplete_provider_client.h ++++ b/components/omnibox/browser/autocomplete_provider_client.h +@@ -136,6 +136,7 @@ class AutocompleteProviderClient : public OmniboxAction::Client { + virtual bool IsOffTheRecord() const = 0; + virtual bool IsIncognitoProfile() const = 0; + virtual bool IsGuestSession() const = 0; ++ virtual bool IsAlwaysIncognitoEnabled() const = 0; + + virtual bool SearchSuggestEnabled() const = 0; + +diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc +index c3b44ff7635a0..ac9fa06fc03a1 100644 +--- a/components/omnibox/browser/base_search_provider.cc ++++ b/components/omnibox/browser/base_search_provider.cc +@@ -346,7 +346,7 @@ bool BaseSearchProvider::CanSendZeroSuggestRequest( + } + + // Don't make a suggest request if in incognito mode. +- if (client->IsOffTheRecord()) { ++ if (client->IsOffTheRecord() && client->IsAlwaysIncognitoEnabled() == false) { + return false; + } + +diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc +index 63b5696e1f8a1..a385686403d8a 100644 +--- a/components/omnibox/browser/search_provider.cc ++++ b/components/omnibox/browser/search_provider.cc +@@ -780,7 +780,9 @@ bool SearchProvider::IsQuerySuitableForSuggest(bool* query_is_private) const { + // keyword input to a keyword suggest server, if any.) + const TemplateURL* default_url = providers_.GetDefaultProviderURL(); + const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); +- return !client()->IsOffTheRecord() && client()->SearchSuggestEnabled() && ++ return (client()->IsOffTheRecord() == false || ++ client()->IsAlwaysIncognitoEnabled() == true) && ++ client()->SearchSuggestEnabled() && + ((default_url && !default_url->suggestions_url().empty() && + !*query_is_private) || + (keyword_url && !keyword_url->suggestions_url().empty())); +diff --git a/weblayer/browser/host_content_settings_map_factory.cc b/weblayer/browser/host_content_settings_map_factory.cc +index d9e57d3974f97..320462af3751f 100644 +--- a/weblayer/browser/host_content_settings_map_factory.cc ++++ b/weblayer/browser/host_content_settings_map_factory.cc +@@ -45,6 +45,7 @@ HostContentSettingsMapFactory::BuildServiceInstanceFor( + scoped_refptr settings_map = + base::MakeRefCounted( + user_prefs::UserPrefs::Get(context), context->IsOffTheRecord(), ++ /*force_save_site_settings*/false, + /*store_last_modified=*/true, + /*restore_session=*/false, + /*should_record_metrics=*/!context->IsOffTheRecord()); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-bookmark-import-export-actions.patch b/build/bromite_patches/Add-bookmark-import-export-actions.patch new file mode 100644 index 0000000000000000000000000000000000000000..5a52d81300a8a51ff90fee6f193bc928406b8a21 --- /dev/null +++ b/build/bromite_patches/Add-bookmark-import-export-actions.patch @@ -0,0 +1,1906 @@ +From 65fee6a0aae835b2f9ab0c0e16fda4f00c729511 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Wed, 1 Aug 2018 09:19:40 +0200 +Subject: [PATCH 061/192] Add bookmark import/export actions + +Add bookmark import/export actions in bookmarks activity and page +Reduce permissions needed for bookmarks import/export +Completely remove contacts picker permission from the file dialog + +Requires patch: Adds-support-for-writing-URIs.patch + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/java/AndroidManifest.xml | 1 - + .../java/res/menu/bookmark_toolbar_menu.xml | 14 + + .../menu/bookmark_toolbar_menu_improved.xml | 14 + + .../browser/TabbedModeTabDelegateFactory.java | 5 +- + .../app/bookmarks/BookmarkActivity.java | 32 ++ + .../browser/bookmarks/BookmarkBridge.java | 278 +++++++++++++++++ + .../browser/bookmarks/BookmarkDelegate.java | 10 + + .../bookmarks/BookmarkManagerCoordinator.java | 9 + + .../bookmarks/BookmarkManagerMediator.java | 23 ++ + .../browser/bookmarks/BookmarkPage.java | 8 +- + .../browser/bookmarks/BookmarkToolbar.java | 23 ++ + .../bookmarks/BookmarkToolbarMediator.java | 4 + + .../bookmarks/BookmarkToolbarProperties.java | 7 +- + .../bookmarks/BookmarkToolbarViewBinder.java | 6 + + .../native_page/NativePageFactory.java | 11 +- + chrome/browser/BUILD.gn | 11 +- + chrome/browser/about_flags.cc | 8 + + .../bookmarks/android/bookmark_bridge.cc | 283 ++++++++++++++++++ + .../bookmarks/android/bookmark_bridge.h | 30 +- + .../browser/bookmarks/bookmark_html_writer.cc | 11 + + .../dialogs/DownloadLocationCustomView.java | 8 +- + .../DownloadLocationDialogCoordinator.java | 8 +- + chrome/browser/flag_descriptions.cc | 5 + + chrome/browser/flag_descriptions.h | 3 + + .../flags/android/chrome_feature_list.cc | 6 + + .../flags/android/chrome_feature_list.h | 1 + + .../browser/flags/ChromeFeatureList.java | 1 + + chrome/browser/importer/profile_writer.cc | 12 + + chrome/browser/importer/profile_writer.h | 6 + + .../preferences/ChromePreferenceKeys.java | 3 + + .../strings/android_chrome_strings.grd | 18 ++ + chrome/common/BUILD.gn | 3 + + chrome/utility/BUILD.gn | 7 +- + .../utility/importer/bookmark_html_reader.cc | 27 +- + .../utility/importer/bookmark_html_reader.h | 8 + + .../headless_select_file_dialog.cc | 4 + + .../chromium/ui/base/SelectFileDialog.java | 18 +- + .../java/strings/android_ui_strings.grd | 3 + + ui/shell_dialogs/select_file_dialog.h | 2 + + .../select_file_dialog_android.cc | 6 + + ui/shell_dialogs/select_file_dialog_android.h | 2 + + ui/shell_dialogs/select_file_dialog_win.cc | 5 + + 42 files changed, 915 insertions(+), 29 deletions(-) + +diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml +index f2eba66ddc541..8974f51e25548 100644 +--- a/chrome/android/java/AndroidManifest.xml ++++ b/chrome/android/java/AndroidManifest.xml +@@ -62,7 +62,6 @@ by a child template that "extends" this file. + + +- + + + +diff --git a/chrome/android/java/res/menu/bookmark_toolbar_menu.xml b/chrome/android/java/res/menu/bookmark_toolbar_menu.xml +index d57102c6877d1..bb414a0d4069d 100644 +--- a/chrome/android/java/res/menu/bookmark_toolbar_menu.xml ++++ b/chrome/android/java/res/menu/bookmark_toolbar_menu.xml +@@ -23,6 +23,20 @@ found in the LICENSE file. + android:visible="false" + app:showAsAction="ifRoom" + app:iconTint="@color/default_icon_color_secondary_tint_list" /> ++ ++ + ++ ++ + mShareDelegateSupplier; + private final Supplier mEphemeralTabCoordinatorSupplier; +@@ -74,7 +75,7 @@ public class TabbedModeTabDelegateFactory implements TabDelegateFactory { + + private NativePageFactory mNativePageFactory; + +- public TabbedModeTabDelegateFactory(Activity activity, ++ public TabbedModeTabDelegateFactory(ChromeActivity activity, + BrowserControlsVisibilityDelegate appBrowserControlsVisibilityDelegate, + Supplier shareDelegateSupplier, + Supplier ephemeralTabCoordinatorSupplier, +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java +index f1c645990fbce..e21745babe837 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkActivity.java +@@ -21,6 +21,11 @@ import org.chromium.chrome.browser.preferences.SharedPreferencesManager; + import org.chromium.chrome.browser.profiles.Profile; + import org.chromium.components.bookmarks.BookmarkId; + import org.chromium.components.embedder_support.util.UrlConstants; ++import org.chromium.ui.base.ActivityWindowAndroid; ++import org.chromium.ui.base.IntentRequestTracker; ++ ++import org.chromium.ui.modaldialog.ModalDialogManager; ++import org.chromium.components.browser_ui.modaldialog.AppModalPresenter; + + /** + * The activity that displays the bookmark UI on the phone. It keeps a {@link +@@ -33,6 +38,9 @@ public class BookmarkActivity extends SnackbarActivity { + public static final int EDIT_BOOKMARK_REQUEST_CODE = 14; + public static final String INTENT_VISIT_BOOKMARK_ID = "BookmarkEditActivity.VisitBookmarkId"; + ++ private ActivityWindowAndroid mWindowAndroid; ++ private IntentRequestTracker mIntentRequestTracker; ++ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); +@@ -54,8 +62,23 @@ public class BookmarkActivity extends SnackbarActivity { + BackPressHelper.create(this, getOnBackPressedDispatcher(), + mBookmarkManagerCoordinator::onBackPressed, SecondaryActivity.BOOKMARK); + } ++ ++ final boolean listenToActivityState = true; ++ mIntentRequestTracker = IntentRequestTracker.createFromActivity(this); ++ mWindowAndroid = new ActivityWindowAndroid(this, listenToActivityState, mIntentRequestTracker); ++ mWindowAndroid.getIntentRequestTracker().restoreInstanceState(savedInstanceState); ++ mBookmarkManagerCoordinator.setWindow(mWindowAndroid, ++ new ModalDialogManager( ++ new AppModalPresenter(this), ModalDialogManager.ModalDialogType.APP)); + } + ++ @Override ++ protected void onSaveInstanceState(Bundle outState) { ++ super.onSaveInstanceState(outState); ++ ++ mWindowAndroid.getIntentRequestTracker().saveInstanceState(outState); ++ } ++ + @Override + protected void onDestroy() { + super.onDestroy(); +@@ -65,6 +88,7 @@ public class BookmarkActivity extends SnackbarActivity { + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); ++ mWindowAndroid.getIntentRequestTracker().onActivityResult(requestCode, resultCode, data); + if (requestCode == EDIT_BOOKMARK_REQUEST_CODE && resultCode == RESULT_OK) { + BookmarkId bookmarkId = BookmarkId.getBookmarkIdFromString( + data.getStringExtra(INTENT_VISIT_BOOKMARK_ID)); +@@ -72,6 +96,14 @@ public class BookmarkActivity extends SnackbarActivity { + } + } + ++ @Override ++ public void onRequestPermissionsResult( ++ int requestCode, String[] permissions, int[] grantResults) { ++ if (mWindowAndroid.handlePermissionResult(requestCode, permissions, grantResults)) ++ return; ++ super.onRequestPermissionsResult(requestCode, permissions, grantResults); ++ } ++ + /** + * @return The {@link BookmarkManagerCoordinator} for testing purposes. + */ +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java +index 3cf4fa48a2581..95788b70eff1e 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java +@@ -4,7 +4,20 @@ + + package org.chromium.chrome.browser.bookmarks; + ++import android.app.Activity; ++import android.content.Intent; ++import android.content.Context; ++import android.content.pm.PackageManager; ++import android.content.DialogInterface; ++import android.content.Intent; ++import android.net.Uri; ++import android.content.ContentResolver; ++import android.provider.Browser; ++import android.provider.DocumentsContract; ++import android.Manifest.permission; ++import androidx.appcompat.app.AlertDialog; + import android.os.SystemClock; ++import android.os.Build; + import android.text.TextUtils; + import android.util.Pair; + +@@ -38,6 +51,32 @@ import org.chromium.url.GURL; + import java.util.ArrayList; + import java.util.List; + ++import org.chromium.base.ContentUriUtils; ++import org.chromium.chrome.R; ++import org.chromium.chrome.browser.document.ChromeLauncherActivity; ++import org.chromium.chrome.browser.IntentHandler; ++import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; ++import org.chromium.chrome.browser.preferences.SharedPreferencesManager; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.ui.base.PageTransition; ++import org.chromium.ui.base.WindowAndroid; ++import org.chromium.ui.modaldialog.ModalDialogManager; ++ ++import android.view.View; ++import android.view.LayoutInflater; ++import org.chromium.ui.modelutil.PropertyModel; ++import org.chromium.ui.modaldialog.ModalDialogProperties; ++import org.chromium.ui.modaldialog.DialogDismissalCause; ++import org.chromium.chrome.browser.download.DownloadLocationDialogType; ++import org.chromium.chrome.browser.download.dialogs.DownloadLocationDialogController; ++import org.chromium.chrome.browser.download.dialogs.DownloadLocationDialogCoordinator; ++import org.chromium.chrome.browser.download.dialogs.DownloadLocationCustomView; ++import org.chromium.chrome.browser.download.DirectoryOption; ++import android.content.res.Resources; ++import org.chromium.base.task.AsyncTask; ++ ++import java.io.File; ++ + /** + * Provides the communication channel for Android to fetch and manipulate the + * bookmark model stored in native. +@@ -432,6 +471,209 @@ class BookmarkBridge { + mNativeBookmarkBridge, BookmarkBridge.this, id.getId(), id.getType()); + } + ++ /** ++ * Import bookmarks from a selected file. ++ * @param window The current window of the bookmarks activity or page. ++ */ ++ public void importBookmarks(WindowAndroid window) { ++ assert mIsNativeBookmarkModelLoaded; ++ BookmarkBridgeJni.get().importBookmarks(mNativeBookmarkBridge, BookmarkBridge.this, window); ++ } ++ ++ /** ++ * Export bookmarks to a path selected by the user. ++ * @param window The current window of the bookmarks activity or page. ++ */ ++ public void exportBookmarks(WindowAndroid window, ModalDialogManager modalDialogManager) { ++ assert mIsNativeBookmarkModelLoaded; ++ if (ChromeFeatureList.isEnabled(ChromeFeatureList.BOOKMARKS_EXPORT_USESAF) || ++ Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) ++ exportBookmarksImplUseSaf(window); ++ else ++ exportBookmarksImplUseFile(window, modalDialogManager); ++ } ++ ++ private void exportBookmarksImplUseSaf(WindowAndroid window) { ++ Context context = window.getContext().get(); ++ ++ // standard name for boorkmark file ++ final String standardBoorkmarkName = "bookmarks.html"; ++ ++ // use the fileSelector and saf asking user for the file ++ Intent fileSelector = new Intent(Intent.ACTION_CREATE_DOCUMENT); ++ fileSelector.addCategory(Intent.CATEGORY_OPENABLE); ++ fileSelector.setType("text/html"); ++ fileSelector.putExtra(Intent.EXTRA_TITLE, standardBoorkmarkName); ++ fileSelector.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | ++ Intent.FLAG_GRANT_READ_URI_PERMISSION | ++ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); ++ ++ // get last exported uri path, if any ++ SharedPreferencesManager sharedPrefs = SharedPreferencesManager.getInstance(); ++ String bookmarksPath = sharedPrefs.readString(ChromePreferenceKeys.BOOKMARKS_LAST_EXPORT_URI, standardBoorkmarkName); ++ Uri lastSelectedUri = Uri.parse(bookmarksPath); ++ ++ // prepare delegate for file selector ++ DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() { ++ @Override ++ public void onClick(DialogInterface dialog, int button) { ++ if (button == AlertDialog.BUTTON_NEGATIVE) { ++ window.showIntent(fileSelector, ++ new WindowAndroid.IntentCallback() { ++ @Override ++ public void onIntentCompleted(int resultCode, Intent data) { ++ if (data == null) return; ++ Uri filePath = data.getData(); ++ doExportBookmarksImpl(window, filePath); ++ } ++ }, ++ null); ++ } else { ++ if (dialog!=null) dialog.dismiss(); ++ doExportBookmarksImpl(window, lastSelectedUri); ++ } ++ } ++ }; ++ ++ // as a workaround for https://issuetracker.google.com/issues/37136466 ++ // ask to overwrite if is a valid uri and the file is present ++ if (DocumentsContract.isDocumentUri(context, lastSelectedUri)) { ++ AsyncTask checkUriTask = new AsyncTask() { ++ boolean uriExists = false; ++ String actualFilePath = null; ++ ++ @Override ++ protected Void doInBackground() { ++ uriExists = ContentUriUtils.contentUriExists(lastSelectedUri.toString()); ++ if (uriExists) { ++ actualFilePath = ContentUriUtils.getFilePathFromContentUri(lastSelectedUri); ++ // get real actual file name on disk ++ if (actualFilePath==null) actualFilePath = lastSelectedUri.toString(); ++ // set file name to last exported file name ++ fileSelector.putExtra(Intent.EXTRA_TITLE, ++ ContentUriUtils.getDisplayName(lastSelectedUri, context, ++ DocumentsContract.Document.COLUMN_DISPLAY_NAME)); ++ } ++ return null; ++ } ++ ++ @Override ++ protected void onPostExecute(Void result) { ++ // check for permissions ++ if (uriExists) { ++ AlertDialog.Builder alert = ++ new AlertDialog.Builder(context, R.style.ThemeOverlay_BrowserUI_AlertDialog); ++ AlertDialog alertDialog = ++ alert.setTitle(R.string.export_bookmarks_alert_title) ++ .setMessage(context.getString(R.string.export_bookmarks_alert_message, actualFilePath)) ++ .setPositiveButton( ++ R.string.export_bookmarks_alert_message_yes, onClickListener) ++ .setNegativeButton(R.string.export_bookmarks_alert_message_no, onClickListener) ++ .create(); ++ alertDialog.getDelegate().setHandleNativeActionModesEnabled(false); ++ ++ // show dialog asking for overwrite ++ alertDialog.show(); ++ return; ++ } else { ++ onClickListener.onClick(null, AlertDialog.BUTTON_NEGATIVE); ++ } ++ } ++ }; ++ checkUriTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); ++ return; ++ } ++ ++ // actually open the file selector ++ onClickListener.onClick(null, AlertDialog.BUTTON_NEGATIVE); ++ } ++ ++ private void doExportBookmarksImpl(WindowAndroid window, Uri filePath) { ++ ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver(); ++ // since we want to persist the uri in settings, ask for persistable permissions ++ resolver.takePersistableUriPermission(filePath, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | ++ Intent.FLAG_GRANT_READ_URI_PERMISSION); ++ ++ BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge, BookmarkBridge.this, ++ window, filePath.toString()); ++ } ++ ++ private void exportBookmarksImplUseFile(WindowAndroid window, ModalDialogManager modalDialogManager) { ++ Context context = window.getContext().get(); ++ ++ // standard name for boorkmark file ++ final String standardBoorkmarkName = "bookmarks.html"; ++ ++ // use the download ui and standard file saving ++ DownloadLocationDialogController controller = new DownloadLocationDialogController() { ++ @Override ++ public void onDownloadLocationDialogComplete(String returnedPath) {} ++ ++ @Override ++ public void onDownloadLocationDialogCanceled() {} ++ }; ++ ++ DownloadLocationDialogCoordinator dialog = new DownloadLocationDialogCoordinator() { ++ @Override ++ protected void onDirectoryOptionsRetrieved(ArrayList dirs) { ++ if (mDialogModel != null) return; ++ ++ // Actually show the dialog. ++ mCustomView = (DownloadLocationCustomView) LayoutInflater.from(context).inflate( ++ R.layout.download_location_dialog, null); ++ mCustomView.initialize(DownloadLocationDialogType.DEFAULT, /*totalBytes*/ 0); ++ mCustomView.setTitle(context.getString(R.string.export_bookmarks_alert_title)); ++ mCustomView.setFileName(standardBoorkmarkName); ++ mCustomView.mDontShowAgain.setVisibility(View.GONE); ++ ++ Resources resources = context.getResources(); ++ mDialogModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) ++ .with(ModalDialogProperties.CONTROLLER, this) ++ .with(ModalDialogProperties.CUSTOM_VIEW, mCustomView) ++ .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, ++ R.string.export_bookmarks) ++ .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, ++ R.string.cancel) ++ .build(); ++ ++ mModalDialogManager.showDialog(mDialogModel, ModalDialogManager.ModalDialogType.APP); ++ } ++ ++ @Override ++ public void onDismiss(PropertyModel model, int dismissalCause) { ++ switch (dismissalCause) { ++ case DialogDismissalCause.POSITIVE_BUTTON_CLICKED: ++ { ++ String fileName = mCustomView.getFileName(); ++ String directory = mCustomView.getDirectoryOption().location; ++ if (fileName != null && directory != null) { ++ File file = new File(directory, fileName); ++ ++ if (window.hasPermission(permission.WRITE_EXTERNAL_STORAGE)) { ++ BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge, ++ BookmarkBridge.this, window, file.getPath()); ++ } else { ++ String[] requestPermissions = new String[] {permission.WRITE_EXTERNAL_STORAGE}; ++ window.requestPermissions(requestPermissions, (permissions, grantResults) -> { ++ if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { ++ BookmarkBridgeJni.get().exportBookmarks(mNativeBookmarkBridge, ++ BookmarkBridge.this, window, file.getPath()); ++ } ++ }); ++ }; ++ } ++ } ++ break; ++ } ++ mDialogModel = null; ++ mCustomView = null; ++ } ++ }; ++ dialog.initialize(controller); ++ dialog.showDialog(context, modalDialogManager, /*totalBytes*/ 0, ++ DownloadLocationDialogType.DEFAULT, /*suggestedPath*/ "", /*isIncognito*/ false); ++ } ++ + /** + * Synchronously gets a list of bookmarks that match the specified search query. + * @param query Keyword used for searching bookmarks. +@@ -931,6 +1173,39 @@ class BookmarkBridge { + depthList.add(depth); + } + ++ @CalledByNative ++ public void bookmarksExported(WindowAndroid window, String bookmarksPath, boolean success) { ++ Uri uri = Uri.parse(bookmarksPath); ++ ++ if (success == false) { ++ ((Activity)window.getContext().get()).runOnUiThread(new Runnable() { ++ public void run() { ++ window.showError(R.string.saving_file_error); ++ } ++ }); ++ } else { ++ SharedPreferencesManager sharedPrefs = SharedPreferencesManager.getInstance(); ++ sharedPrefs.writeString(ChromePreferenceKeys.BOOKMARKS_LAST_EXPORT_URI, bookmarksPath); ++ ++ Context context = ContextUtils.getApplicationContext(); ++ ++ Intent intent = new Intent(Intent.ACTION_VIEW, ++ ContentUriUtils.isContentUri(bookmarksPath) ? ++ Uri.parse(bookmarksPath) : Uri.parse("file://" + bookmarksPath)); ++ intent.putExtra(Browser.EXTRA_APPLICATION_ID, ++ context.getPackageName()); ++ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ++ intent.putExtra(IntentHandler.EXTRA_PAGE_TRANSITION_TYPE, PageTransition.AUTO_BOOKMARK); ++ ++ // If the bookmark manager is shown in a tab on a phone (rather than in a separate ++ // activity) the component name may be null. Send the intent through ++ // ChromeLauncherActivity instead to avoid crashing. See crbug.com/615012. ++ intent.setClass(context, ChromeLauncherActivity.class); ++ ++ IntentHandler.startActivityForTrustedIntent(intent); ++ } ++ } ++ + private static List> createPairsList(int[] left, int[] right) { + List> pairList = new ArrayList<>(); + for (int i = 0; i < left.length; i++) { +@@ -966,6 +1241,9 @@ class BookmarkBridge { + int getChildCount(long nativeBookmarkBridge, BookmarkBridge caller, long id, int type); + void getChildIds(long nativeBookmarkBridge, BookmarkBridge caller, long id, int type, + List bookmarksList); ++ void importBookmarks(long nativeBookmarkBridge, BookmarkBridge caller, WindowAndroid window); ++ void exportBookmarks(long nativeBookmarkBridge, BookmarkBridge caller, WindowAndroid window, ++ String export_path); + BookmarkId getChildAt( + long nativeBookmarkBridge, BookmarkBridge caller, long id, int type, int index); + int getTotalBookmarkCount( +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java +index 473c4a65a54d4..20582e2552642 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java +@@ -60,6 +60,16 @@ public interface BookmarkDelegate { + */ + void openBookmarksInNewTabs(List bookmark, boolean incognito); + ++ /** ++ * Imports bookmarks from user-selected file. ++ */ ++ void importBookmarks(); ++ ++ /** ++ * Exports bookmarks to downloads directory. ++ */ ++ void exportBookmarks(); ++ + /** + * Shows the search UI. + */ +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java +index b2afb6cb450e6..bbd7cb16c3fca 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java +@@ -47,6 +47,8 @@ import org.chromium.components.image_fetcher.ImageFetcher; + import org.chromium.components.image_fetcher.ImageFetcherConfig; + import org.chromium.components.image_fetcher.ImageFetcherFactory; + import org.chromium.ui.KeyboardVisibilityDelegate; ++import org.chromium.ui.base.ActivityWindowAndroid; ++import org.chromium.ui.modaldialog.ModalDialogManager; + import org.chromium.ui.modaldialog.ModalDialogManager; + import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType; + import org.chromium.ui.modelutil.MVCListAdapter.ModelList; +@@ -225,6 +227,13 @@ public class BookmarkManagerCoordinator + + // Public API implementation. + ++ /** ++ * Sets the Android window that is used by further intents created by the bookmark activity. ++ */ ++ public void setWindow(ActivityWindowAndroid window, ModalDialogManager modalDialogManager) { ++ mMediator.setWindow(window, modalDialogManager); ++ } ++ + /** + * Destroys and cleans up itself. This must be called after done using this class. + */ +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java +index 71156d35d80c2..2cb12380fd6a4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java +@@ -51,6 +51,8 @@ import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelega + import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate.SelectionObserver; + import org.chromium.components.commerce.core.CommerceSubscription; + import org.chromium.components.commerce.core.ShoppingService; ++import org.chromium.ui.base.ActivityWindowAndroid; ++import org.chromium.ui.modaldialog.ModalDialogManager; + import org.chromium.components.favicon.LargeIconBridge; + import org.chromium.components.feature_engagement.EventConstants; + import org.chromium.components.power_bookmarks.PowerBookmarkMeta; +@@ -77,6 +79,9 @@ class BookmarkManagerMediator + + private static boolean sPreventLoadingForTesting; + ++ private ActivityWindowAndroid mWindowAndroid; ++ private ModalDialogManager mModalDialogManager; ++ + /** + * Keeps track of whether drag is enabled / active for bookmark lists. + */ +@@ -461,6 +466,14 @@ class BookmarkManagerMediator + mNativePage = nativePage; + } + ++ /** ++ * Sets the Android window that is used by further intents created by the bookmark activity. ++ */ ++ public void setWindow(ActivityWindowAndroid window, ModalDialogManager modalDialogManager) { ++ mWindowAndroid = window; ++ mModalDialogManager = modalDialogManager; ++ } ++ + /** + * See BookmarkManager(Coordinator)#updateForUrl + */ +@@ -635,6 +648,16 @@ class BookmarkManagerMediator + } + } + ++ @Override ++ public void importBookmarks() { ++ mBookmarkModel.importBookmarks(mWindowAndroid); ++ } ++ ++ @Override ++ public void exportBookmarks() { ++ mBookmarkModel.exportBookmarks(mWindowAndroid, mModalDialogManager); ++ } ++ + @Override + public void openSearchUi() { + onQueryCallback(""); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java +index ab5f86200de26..455e22a56a7c4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkPage.java +@@ -13,6 +13,9 @@ import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; + import org.chromium.chrome.browser.ui.native_page.BasicNativePage; + import org.chromium.chrome.browser.ui.native_page.NativePageHost; + import org.chromium.components.embedder_support.util.UrlConstants; ++import org.chromium.chrome.browser.app.ChromeActivity; ++import org.chromium.ui.modaldialog.ModalDialogManager; ++import org.chromium.components.browser_ui.modaldialog.AppModalPresenter; + + /** + * A native page holding a {@link BookmarkManagerCoordinator} on _tablet_. +@@ -29,7 +32,7 @@ public class BookmarkPage extends BasicNativePage { + * @param host A NativePageHost to load urls. + */ + public BookmarkPage(ComponentName componentName, SnackbarManager snackbarManager, +- boolean isIncognito, NativePageHost host) { ++ boolean isIncognito, NativePageHost host, ChromeActivity activity) { + super(host); + + mBookmarkManagerCoordinator = +@@ -37,6 +40,9 @@ public class BookmarkPage extends BasicNativePage { + snackbarManager, Profile.getLastUsedRegularProfile(), + new BookmarkUiPrefs(SharedPreferencesManager.getInstance())); + mBookmarkManagerCoordinator.setBasicNativePage(this); ++ mBookmarkManagerCoordinator.setWindow(activity.getWindowAndroid(), ++ new ModalDialogManager( ++ new AppModalPresenter(activity), ModalDialogManager.ModalDialogType.APP)); + mTitle = host.getContext().getResources().getString(R.string.bookmarks); + + initWithView(mBookmarkManagerCoordinator.getView()); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java +index 92336e2d67011..3dbea0829516f 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java +@@ -116,6 +116,17 @@ public class BookmarkToolbar extends SelectableListToolbar + setOnMenuItemClickListener(dragEnabled ? null : this); + } + ++ private Runnable mImportBookmarkRunnable; ++ private Runnable mExportBookmarkRunnable; ++ ++ void setImportBookmarkRunnable(Runnable runnable) { ++ mImportBookmarkRunnable = runnable; ++ } ++ ++ void setExportBookmarkRunnable(Runnable runnable) { ++ mExportBookmarkRunnable = runnable; ++ } ++ + void setSearchButtonVisible(boolean visible) { + // The improved bookmarks experience embeds search in the list. + if (BookmarkFeatures.isAndroidImprovedBookmarksEnabled()) return; +@@ -156,6 +167,8 @@ public class BookmarkToolbar extends SelectableListToolbar + + void setCurrentFolder(BookmarkId folder) { + mCurrentFolder = mBookmarkModel.getBookmarkById(folder); ++ getMenu().findItem(R.id.import_menu_id).setVisible(true); ++ getMenu().findItem(R.id.export_menu_id).setVisible(true); + } + + void setNavigateBackRunnable(Runnable navigateBackRunnable) { +@@ -175,6 +188,13 @@ public class BookmarkToolbar extends SelectableListToolbar + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + hideOverflowMenu(); ++ if (menuItem.getItemId() == R.id.import_menu_id) { ++ mImportBookmarkRunnable.run(); ++ return true; ++ } else if (menuItem.getItemId() == R.id.export_menu_id) { ++ mExportBookmarkRunnable.run(); ++ return true; ++ } + return mMenuIdClickedFunction.apply(menuItem.getItemId()); + } + +@@ -195,6 +215,9 @@ public class BookmarkToolbar extends SelectableListToolbar + protected void showNormalView() { + super.showNormalView(); + ++ getMenu().findItem(R.id.import_menu_id).setVisible(mCurrentFolder != null); ++ getMenu().findItem(R.id.export_menu_id).setVisible(mCurrentFolder != null); ++ + // SelectableListToolbar will show/hide the entire group. + setSearchButtonVisible(mSearchButtonVisible); + setEditButtonVisible(mEditButtonVisible); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java +index 62dba243f0ab0..4284e117b57fb 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java +@@ -81,6 +81,10 @@ class BookmarkToolbarMediator implements BookmarkUiObserver, DragListener, + mBookmarkDelegate = bookmarkDelegate; + mModel.set(BookmarkToolbarProperties.NAVIGATE_BACK_RUNNABLE, + this::openParentForCurrentFolder); ++ mModel.set( ++ BookmarkToolbarProperties.IMPORT_BOOKMARK_RUNNABLE, mBookmarkDelegate::importBookmarks); ++ mModel.set( ++ BookmarkToolbarProperties.EXPORT_BOOKMARK_RUNNABLE, mBookmarkDelegate::exportBookmarks); + mBookmarkDelegate.addUiObserver(this); + mBookmarkDelegate.notifyStateChange(this); + }); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java +index bcaae8aaf8119..96e06b4fb52f9 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java +@@ -61,11 +61,16 @@ class BookmarkToolbarProperties { + new WritableObjectPropertyKey<>(); + static final WritableObjectPropertyKey NAVIGATE_BACK_RUNNABLE = + new WritableObjectPropertyKey<>(); ++ static final WritableObjectPropertyKey IMPORT_BOOKMARK_RUNNABLE = ++ new WritableObjectPropertyKey<>(); ++ static final WritableObjectPropertyKey EXPORT_BOOKMARK_RUNNABLE = ++ new WritableObjectPropertyKey<>(); + + static final PropertyKey[] ALL_KEYS = {BOOKMARK_MODEL, BOOKMARK_OPENER, SELECTION_DELEGATE, + TITLE, BOOKMARK_UI_MODE, SOFT_KEYBOARD_VISIBLE, IS_DIALOG_UI, DRAG_ENABLED, + SEARCH_BUTTON_VISIBLE, EDIT_BUTTON_VISIBLE, NEW_FOLDER_BUTTON_VISIBLE, + NEW_FOLDER_BUTTON_ENABLED, NAVIGATION_BUTTON_STATE, CURRENT_FOLDER, + CHECKED_SORT_MENU_ID, CHECKED_VIEW_MENU_ID, MENU_ID_CLICKED_FUNCTION, +- NAVIGATE_BACK_RUNNABLE, FAKE_SELECTION_STATE_CHANGE}; ++ NAVIGATE_BACK_RUNNABLE, FAKE_SELECTION_STATE_CHANGE, ++ IMPORT_BOOKMARK_RUNNABLE, EXPORT_BOOKMARK_RUNNABLE}; + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java +index 4e69cd9c9c83a..b52ce826aae36 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java +@@ -56,6 +56,12 @@ class BookmarkToolbarViewBinder { + model.get(BookmarkToolbarProperties.CHECKED_VIEW_MENU_ID)); + } else if (key == BookmarkToolbarProperties.CURRENT_FOLDER) { + bookmarkToolbar.setCurrentFolder(model.get(BookmarkToolbarProperties.CURRENT_FOLDER)); ++ } else if (key == BookmarkToolbarProperties.IMPORT_BOOKMARK_RUNNABLE) { ++ bookmarkToolbar.setImportBookmarkRunnable( ++ model.get(BookmarkToolbarProperties.IMPORT_BOOKMARK_RUNNABLE)); ++ } else if (key == BookmarkToolbarProperties.EXPORT_BOOKMARK_RUNNABLE) { ++ bookmarkToolbar.setExportBookmarkRunnable( ++ model.get(BookmarkToolbarProperties.EXPORT_BOOKMARK_RUNNABLE)); + } else if (key == BookmarkToolbarProperties.NAVIGATE_BACK_RUNNABLE) { + bookmarkToolbar.setNavigateBackRunnable( + model.get(BookmarkToolbarProperties.NAVIGATE_BACK_RUNNABLE)); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java +index a29623be5b017..47450d033b081 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java +@@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting; + import org.chromium.base.supplier.DestroyableObservableSupplier; + import org.chromium.base.supplier.ObservableSupplier; + import org.chromium.base.supplier.Supplier; ++import org.chromium.chrome.browser.app.ChromeActivity; + import org.chromium.chrome.browser.app.download.home.DownloadPage; + import org.chromium.chrome.browser.bookmarks.BookmarkPage; + import org.chromium.chrome.browser.browser_controls.BrowserControlsMarginSupplier; +@@ -53,7 +54,7 @@ import org.chromium.ui.util.ColorUtils; + * Creates NativePage objects to show chrome-native:// URLs using the native Android view system. + */ + public class NativePageFactory { +- private final Activity mActivity; ++ private final ChromeActivity mActivity; + private final BottomSheetController mBottomSheetController; + private final BrowserControlsManager mBrowserControlsManager; + private final Supplier mCurrentTabSupplier; +@@ -69,7 +70,7 @@ public class NativePageFactory { + + private NativePageBuilder mNativePageBuilder; + +- public NativePageFactory(@NonNull Activity activity, ++ public NativePageFactory(@NonNull ChromeActivity activity, + @NonNull BottomSheetController sheetController, + @NonNull BrowserControlsManager browserControlsManager, + @NonNull Supplier currentTabSupplier, +@@ -115,7 +116,7 @@ public class NativePageFactory { + + @VisibleForTesting + static class NativePageBuilder { +- private final Activity mActivity; ++ private final ChromeActivity mActivity; + private final BottomSheetController mBottomSheetController; + private final Supplier mUma; + private final BrowserControlsManager mBrowserControlsManager; +@@ -129,7 +130,7 @@ public class NativePageFactory { + private final HomeSurfaceTracker mHomeSurfaceTracker; + private final ObservableSupplier mTabContentManagerSupplier; + +- public NativePageBuilder(Activity activity, Supplier uma, ++ public NativePageBuilder(ChromeActivity activity, Supplier uma, + BottomSheetController sheetController, + BrowserControlsManager browserControlsManager, Supplier currentTabSupplier, + Supplier snackbarManagerSupplier, +@@ -169,7 +170,7 @@ public class NativePageFactory { + protected NativePage buildBookmarksPage(Tab tab) { + return new BookmarkPage(mActivity.getComponentName(), mSnackbarManagerSupplier.get(), + mTabModelSelector.isIncognitoSelected(), +- new TabShim(tab, mBrowserControlsManager, mTabModelSelector)); ++ new TabShim(tab, mBrowserControlsManager, mTabModelSelector), mActivity); + } + + protected NativePage buildDownloadsPage(Tab tab) { +diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn +index 05e80b77ea2e4..3e503cb91f5c0 100644 +--- a/chrome/browser/BUILD.gn ++++ b/chrome/browser/BUILD.gn +@@ -204,6 +204,8 @@ static_library("browser") { + "bluetooth/chrome_bluetooth_delegate_impl_client.h", + "bookmarks/bookmark_model_factory.cc", + "bookmarks/bookmark_model_factory.h", ++ "bookmarks/bookmark_html_writer.cc", ++ "bookmarks/bookmark_html_writer.h", + "bookmarks/chrome_bookmark_client.cc", + "bookmarks/chrome_bookmark_client.h", + "bookmarks/managed_bookmark_service_factory.cc", +@@ -1880,6 +1882,13 @@ static_library("browser") { + ] + } + ++ if (is_android) { ++ sources += [ ++ "importer/profile_writer.cc", ++ "importer/profile_writer.h", ++ ] ++ } ++ + configs += [ + "//build/config/compiler:wexit_time_destructors", + "//build/config:precompiled_headers", +@@ -3543,8 +3552,6 @@ static_library("browser") { + "badging/badge_manager_factory.h", + "banners/app_banner_manager_desktop.cc", + "banners/app_banner_manager_desktop.h", +- "bookmarks/bookmark_html_writer.cc", +- "bookmarks/bookmark_html_writer.h", + "bookmarks/url_and_id.h", + "cart/cart_db.cc", + "cart/cart_db.h", +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 78c2b1a2de4e7..d7531ac080f86 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -9475,6 +9475,14 @@ const FeatureEntry kFeatureEntries[] = { + FEATURE_VALUE_TYPE(features::kForceOffTextAutosizing)}, + #endif + ++#if BUILDFLAG(IS_ANDROID) ++ {"export-bookmarks-use-saf", ++ flag_descriptions::kBookmarksExportUseSafName, ++ flag_descriptions::kBookmarksExportUseSafDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE( ++ chrome::android::kBookmarksExportUseSaf)}, ++#endif ++ + #if BUILDFLAG(IS_CHROMEOS_ASH) + {"video-conference", flag_descriptions::kVideoConferenceName, + flag_descriptions::kVideoConferenceDescription, kOsCrOS, +diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc +index 3b4213fab1ffd..b0c4c40e9c240 100644 +--- a/chrome/browser/bookmarks/android/bookmark_bridge.cc ++++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc +@@ -58,6 +58,25 @@ + #include "content/public/browser/browser_thread.h" + #include "content/public/browser/web_contents.h" + ++#include "base/android/content_uri_utils.h" ++#include "base/android/path_utils.h" ++#include "base/strings/utf_string_conversions.h" ++#include "chrome/utility/importer/bookmark_html_reader.h" ++#include "chrome/browser/bookmarks/bookmark_html_writer.h" ++#include "chrome/browser/importer/profile_writer.h" ++#include "chrome/browser/platform_util.h" ++#include "chrome/browser/ui/chrome_select_file_policy.h" ++#include "chrome/common/importer/imported_bookmark_entry.h" ++#include "chrome/common/importer/importer_data_types.h" ++#include "chrome/common/url_constants.h" ++#include "components/favicon_base/favicon_usage_data.h" ++#include "components/search_engines/template_url.h" ++#include "components/url_formatter/url_fixer.h" ++#include "ui/android/window_android.h" ++#include "base/task/task_traits.h" ++#include "base/task/thread_pool.h" ++#include "content/public/browser/browser_task_traits.h" ++ + using base::android::AttachCurrentThread; + using base::android::ConvertUTF16ToJavaString; + using base::android::ConvertUTF8ToJavaString; +@@ -75,8 +94,92 @@ using bookmarks::android::JavaBookmarkIdGetType; + using content::BrowserThread; + using power_bookmarks::PowerBookmarkMeta; + ++namespace internal { ++ ++// Returns true if |url| has a valid scheme that we allow to import. We ++// filter out the URL with a unsupported scheme. ++bool CanImportURL(const GURL& url) { ++ // The URL is not valid. ++ if (!url.is_valid()) ++ return false; ++ ++ // Filter out the URLs with unsupported schemes. ++ const char* const kInvalidSchemes[] = {"wyciwyg", "place"}; ++ for (size_t i = 0; i < std::size(kInvalidSchemes); ++i) { ++ if (url.SchemeIs(kInvalidSchemes[i])) ++ return false; ++ } ++ ++ // Check if |url| is about:blank. ++ if (url == url::kAboutBlankURL) ++ return true; ++ ++ // If |url| starts with chrome:// or about:, check if it's one of the URLs ++ // that we support. ++ if (url.SchemeIs(content::kChromeUIScheme) || ++ url.SchemeIs(url::kAboutScheme)) { ++ if (url.host_piece() == chrome::kChromeUIAboutHost) ++ return true; ++ ++ GURL fixed_url(url_formatter::FixupURL(url.spec(), std::string())); ++ for (size_t i = 0; i < chrome::kNumberOfChromeHostURLs; ++i) { ++ if (fixed_url.DomainIs(chrome::kChromeHostURLs[i])) ++ return true; ++ } ++ ++ for (size_t i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) { ++ if (fixed_url == chrome::kChromeDebugURLs[i]) ++ return true; ++ } ++ ++ // If url has either chrome:// or about: schemes but wasn't found in the ++ // above lists, it means we don't support it, so we don't allow the user ++ // to import it. ++ return false; ++ } ++ ++ // Otherwise, we assume the url has a valid (importable) scheme. ++ return true; ++} ++ ++} // internal ++ + namespace { + ++class FileBookmarksExportObserver: public BookmarksExportObserver { ++ public: ++ FileBookmarksExportObserver( ++ const JavaParamRef& obj, ++ ui::WindowAndroid* window, ++ const std::string& export_path) : ++ obj_(ScopedJavaGlobalRef(obj)), ++ window_(window), ++ export_path_(export_path) {} ++ ++ void OnExportFinished(Result result) override { ++ if (result == Result::kSuccess) { ++ LOG(INFO) << "Bookmarks exported successfully to " << export_path_; ++ } else if (result == Result::kCouldNotCreateFile) { ++ LOG(ERROR) << "Bookmarks export: could not create file " << export_path_; ++ } else if (result == Result::kCouldNotWriteHeader) { ++ LOG(ERROR) << "Bookmarks export: could not write header"; ++ } else if (result == Result::kCouldNotWriteNodes) { ++ LOG(ERROR) << "Bookmarks export: could not write nodes"; ++ } ++ ++ JNIEnv* env = AttachCurrentThread(); ++ Java_BookmarkBridge_bookmarksExported(env, obj_, window_->GetJavaObject(), ++ ConvertUTF8ToJavaString(env, export_path_), ++ result == Result::kSuccess); ++ delete this; ++ } ++ ++ private: ++ const ScopedJavaGlobalRef obj_; ++ raw_ptr window_; ++ const std::string export_path_; ++}; ++ + class BookmarkTitleComparer { + public: + explicit BookmarkTitleComparer(BookmarkBridge* bookmark_bridge, +@@ -198,6 +301,10 @@ BookmarkBridge::~BookmarkBridge() { + if (partner_bookmarks_shim_) + partner_bookmarks_shim_->RemoveObserver(this); + reading_list_manager_->RemoveObserver(this); ++ // There may be pending file dialogs, we need to tell them that we've gone ++ // away so they don't try and call back to us. ++ if (select_file_dialog_) ++ select_file_dialog_->ListenerDestroyed(); + } + + void BookmarkBridge::Destroy(JNIEnv*, const JavaParamRef&) { +@@ -619,6 +726,182 @@ jint BookmarkBridge::GetTotalBookmarkCount( + return count; + } + ++void BookmarkBridge::ImportBookmarks(JNIEnv* env, ++ const JavaParamRef& obj, ++ const JavaParamRef& java_window) { ++ DCHECK(IsLoaded()); ++ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ++ ++ ui::WindowAndroid* window = ++ ui::WindowAndroid::FromJavaWindowAndroid(java_window); ++ CHECK(window); ++ ++ select_file_dialog_ = ui::SelectFileDialog::Create( ++ this, std::make_unique(nullptr)); ++ ++ //NOTE: extension and description are not used on Android, thus not set ++ ui::SelectFileDialog::FileTypeInfo file_type_info; ++ ++ const std::vector v_accept_types = { u"text/html" }; ++ ++ // Android needs the original MIME types and an additional capture value. ++ std::pair, bool> accept_types = ++ std::make_pair(v_accept_types, /* use_media_capture */ false); ++ ++ select_file_dialog_->SelectFile( ++ ui::SelectFileDialog::SELECT_OPEN_FILE, ++ std::u16string(), ++ export_path_, ++ &file_type_info, ++ 0, ++ base::FilePath::StringType(), ++ window, ++ &accept_types ++ ); ++} ++ ++void BookmarkBridge::ExportBookmarks(JNIEnv* env, ++ const JavaParamRef& obj, ++ const JavaParamRef& java_window, ++ const JavaParamRef& j_export_path) { ++ DCHECK(IsLoaded()); ++ DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ++ ++ ui::WindowAndroid* window = ++ ui::WindowAndroid::FromJavaWindowAndroid(java_window); ++ CHECK(window); ++ ++ std::u16string export_path = ++ base::android::ConvertJavaStringToUTF16(env, j_export_path); ++ ++ export_path_ = base::FilePath::FromUTF16Unsafe(export_path); ++ ++ if (export_path_.empty()) { ++ if (!base::android::GetDownloadsDirectory(&export_path_)) { ++ LOG(ERROR) << "Could not retrieve downloads directory for bookmarks export"; ++ return; ++ } ++ export_path_ = export_path_.Append(FILE_PATH_LITERAL("bookmarks.html")); ++ } ++ ++ observer_ = new FileBookmarksExportObserver(obj, window, export_path_.MaybeAsASCII()); ++ bookmark_html_writer::WriteBookmarks(profile_, export_path_, observer_); ++} ++ ++// Attempts to create a TemplateURL from the provided data. |title| is optional. ++// If TemplateURL creation fails, returns null. ++std::unique_ptr CreateTemplateURL(const std::u16string& url, ++ const std::u16string& keyword, ++ const std::u16string& title) { ++ if (url.empty() || keyword.empty()) ++ return nullptr; ++ TemplateURLData data; ++ data.SetKeyword(keyword); ++ // We set short name by using the title if it exists. ++ // Otherwise, we use the shortcut. ++ data.SetShortName(title.empty() ? keyword : title); ++ data.SetURL(TemplateURLRef::DisplayURLToURLRef(url)); ++ return std::make_unique(data); ++} ++ ++void BookmarkBridge::FileSelected(const base::FilePath& path, int index, ++ void* params) { ++ base::ThreadPool::PostTaskAndReplyWithResult( ++ FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()}, ++ base::BindOnce(&BookmarkBridge::FileSelectedImpl, ++ base::Unretained(this), ++ path), ++ base::BindOnce(&BookmarkBridge::FileSelectedImplOnUIThread, ++ base::Unretained(this), ++ path)); ++} ++ ++const std::string BookmarkBridge::FileSelectedImpl(const base::FilePath& path) { ++ base::File file; ++ if (path.IsContentUri()) { ++ file = base::OpenContentUriForRead(path); ++ } else { ++ file.Initialize(path, base::File::FLAG_OPEN | base::File::FLAG_READ); ++ } ++ if (!file.IsValid()) { ++ select_file_dialog_->ShowToast("Cannot open bookmarks file for import"); ++ return ""; ++ } ++ ++ auto fileLength = file.GetLength(); ++ if (-1 == fileLength) { ++ select_file_dialog_->ShowToast("Cannot read bookmarks file length"); ++ return ""; ++ } ++ ++ if (fileLength > 10 * 1024 * 1024) { ++ select_file_dialog_->ShowToast("Bookmark file is bigger than 10MB"); ++ return ""; ++ } ++ ++ std::vector buffer(fileLength); ++ if (-1 == file.ReadAtCurrentPos(buffer.data(), fileLength)) { ++ select_file_dialog_->ShowToast("Could not read bookmarks file"); ++ return ""; ++ } ++ ++ if (buffer.empty()) { ++ select_file_dialog_->ShowToast("Empty bookmarks file"); ++ return ""; ++ } ++ ++ std::string contents(buffer.begin(), buffer.end()); ++ return contents; ++} ++ ++void BookmarkBridge::FileSelectedImplOnUIThread(const base::FilePath& path, ++ const std::string& contents) { ++ if (contents.empty()) ++ return; ++ ++ // the following import logic comes from BookmarksFileImporter class ++ std::vector bookmarks; ++ std::vector search_engines; ++ favicon_base::FaviconUsageDataList favicons; ++ ++ bookmark_html_reader::ImportBookmarksFile( ++ base::RepeatingCallback(), ++ base::BindRepeating(internal::CanImportURL), ++ contents, ++ &bookmarks, ++ &search_engines, ++ &favicons); ++ ++ auto *writer = new ProfileWriter(profile_); ++ ++ if (!bookmarks.empty()) { ++ // adding bookmarks will begin extensive changes to the model ++ writer->AddBookmarksWithModel(bookmark_model_, bookmarks, u"Imported"); ++ } ++ if (!search_engines.empty()) { ++ TemplateURLService::OwnedTemplateURLVector owned_template_urls; ++ for (const auto& search_engine : search_engines) { ++ std::unique_ptr owned_template_url = CreateTemplateURL( ++ search_engine.url, search_engine.keyword, search_engine.display_name); ++ if (owned_template_url) ++ owned_template_urls.push_back(std::move(owned_template_url)); ++ } ++ writer->AddKeywords(std::move(owned_template_urls), false); ++ } ++ ++ std::stringstream message; ++ message << "Imported " << bookmarks.size() << " bookmarks and " << ++ search_engines.size() << " search engines from " << path.MaybeAsASCII(); ++ auto result = message.str(); ++ ++ select_file_dialog_->ShowToast(result); ++ ++ LOG(INFO) << result; ++} ++ ++void BookmarkBridge::FileSelectionCanceled(void* params) { ++} ++ + void BookmarkBridge::SetBookmarkTitle(JNIEnv* env, + const JavaParamRef& obj, + jlong id, +diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.h b/chrome/browser/bookmarks/android/bookmark_bridge.h +index 44388de31493e..f194fe45a09f5 100644 +--- a/chrome/browser/bookmarks/android/bookmark_bridge.h ++++ b/chrome/browser/bookmarks/android/bookmark_bridge.h +@@ -19,6 +19,7 @@ + #include "base/supports_user_data.h" + #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h" + #include "chrome/browser/image_service/image_service_factory.h" ++#include "chrome/browser/bookmarks/bookmark_html_writer.h" + #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/profiles/profile_observer.h" + #include "chrome/browser/reading_list/android/reading_list_manager.h" +@@ -27,6 +28,9 @@ + #include "components/prefs/pref_change_registrar.h" + #include "url/android/gurl_android.h" + ++#include "components/search_engines/template_url.h" ++#include "ui/shell_dialogs/select_file_dialog.h" ++ + namespace bookmarks { + class BookmarkModel; + class ManagedBookmarkService; +@@ -44,7 +48,8 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + public PartnerBookmarksShim::Observer, + public ReadingListManager::Observer, + public ProfileObserver, +- public base::SupportsUserData::Data { ++ public base::SupportsUserData::Data, ++ public ui::SelectFileDialog::Listener { + public: + BookmarkBridge(Profile* profile, + bookmarks::BookmarkModel* model, +@@ -74,6 +79,12 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + bool IsDoingExtensiveChanges(JNIEnv* env, + const base::android::JavaParamRef& obj); + ++ // SelectFileDialog::Listener implementation. ++ void FileSelected(const base::FilePath& path, ++ int index, ++ void* params) override; ++ void FileSelectionCanceled(void* params) override; ++ + jboolean IsEditBookmarksEnabled(JNIEnv* env); + + void LoadEmptyPartnerBookmarkShimForTesting( +@@ -171,6 +182,15 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + jlong id, + jint type); + ++ void ImportBookmarks(JNIEnv* env, ++ const base::android::JavaParamRef& obj, ++ const base::android::JavaParamRef& java_window); ++ ++ void ExportBookmarks(JNIEnv* env, ++ const base::android::JavaParamRef& obj, ++ const base::android::JavaParamRef& java_window, ++ const base::android::JavaParamRef& j_export_path); ++ + void SetBookmarkTitle(JNIEnv* env, + const base::android::JavaParamRef& obj, + jlong id, +@@ -363,12 +383,16 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + void DestroyJavaObject(); + + raw_ptr profile_; ++ base::FilePath export_path_; ++ raw_ptr observer_; // weak ++ + base::android::ScopedJavaGlobalRef java_bookmark_model_; + raw_ptr bookmark_model_; // weak + raw_ptr managed_bookmark_service_; // weak + std::unique_ptr + grouped_bookmark_actions_; + PrefChangeRegistrar pref_change_registrar_; ++ scoped_refptr select_file_dialog_; + + // Information about the Partner bookmarks (must check for IsLoaded()). + // This is owned by profile. +@@ -382,6 +406,10 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + // Observes the profile destruction and creation. + base::ScopedObservation profile_observation_{this}; + ++ const std::string FileSelectedImpl(const base::FilePath& path); ++ void FileSelectedImplOnUIThread(const base::FilePath& path, ++ const std::string& contents); ++ + // Weak pointers for creating callbacks that won't call into a destroyed + // object. + base::WeakPtrFactory weak_ptr_factory_; +diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc +index 7451b2923dcb6..2f458ada42c60 100644 +--- a/chrome/browser/bookmarks/bookmark_html_writer.cc ++++ b/chrome/browser/bookmarks/bookmark_html_writer.cc +@@ -27,6 +27,9 @@ + #include "base/task/thread_pool.h" + #include "base/time/time.h" + #include "base/values.h" ++#if BUILDFLAG(IS_ANDROID) ++#include "base/android/content_uri_utils.h" ++#endif + #include "chrome/browser/bookmarks/bookmark_model_factory.h" + #include "chrome/browser/favicon/favicon_service_factory.h" + #include "chrome/browser/profiles/profile.h" +@@ -229,7 +232,15 @@ class Writer : public base::RefCountedThreadSafe { + // Opens the file, returning true on success. + bool OpenFile() { + int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE; ++#if BUILDFLAG(IS_ANDROID) ++ if (path_.IsContentUri()) { ++ file_ = std::make_unique(base::OpenContentUriForWrite(path_)); ++ } else { ++ file_ = std::make_unique(path_, flags); ++ } ++#else + file_ = std::make_unique(path_, flags); ++#endif + if (!file_->IsValid()) { + PLOG(ERROR) << "Could not create " << path_; + return false; +diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java +index 46b03763a17f8..c6c56e5bf5063 100644 +--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java ++++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java +@@ -49,7 +49,7 @@ public class DownloadLocationCustomView + private TextView mFileSize; + private Spinner mFileLocation; + private TextView mLocationAvailableSpace; +- private CheckBox mDontShowAgain; ++ public CheckBox mDontShowAgain; + private @DownloadLocationDialogType int mDialogType; + private long mTotalBytes; + +@@ -72,7 +72,7 @@ public class DownloadLocationCustomView + mDontShowAgain = findViewById(R.id.show_again_checkbox); + } + +- void initialize(@DownloadLocationDialogType int dialogType, long totalBytes) { ++ public void initialize(@DownloadLocationDialogType int dialogType, long totalBytes) { + // TODO(xingliu): Remove this function, currently used by smart suggestion. + mDialogType = dialogType; + mTotalBytes = totalBytes; +@@ -125,7 +125,7 @@ public class DownloadLocationCustomView + * @return The text that the user inputted as the name of the file. + */ + @Nullable +- String getFileName() { ++ public String getFileName() { + if (mFileName == null || mFileName.getText() == null) return null; + return mFileName.getText().toString(); + } +@@ -134,7 +134,7 @@ public class DownloadLocationCustomView + * @return The file path based on what the user selected as the location of the file. + */ + @Nullable +- DirectoryOption getDirectoryOption() { ++ public DirectoryOption getDirectoryOption() { + if (mFileLocation == null) return null; + DirectoryOption selected = (DirectoryOption) mFileLocation.getSelectedItem(); + return selected; +diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java +index 32c96b9119073..5cb127a6b02b4 100644 +--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java ++++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java +@@ -36,12 +36,12 @@ import java.util.ArrayList; + public class DownloadLocationDialogCoordinator implements ModalDialogProperties.Controller { + @NonNull + private DownloadLocationDialogController mController; +- private PropertyModel mDialogModel; ++ protected PropertyModel mDialogModel; + private PropertyModel mDownloadLocationDialogModel; + private PropertyModelChangeProcessor + mPropertyModelChangeProcessor; +- private DownloadLocationCustomView mCustomView; +- private ModalDialogManager mModalDialogManager; ++ protected DownloadLocationCustomView mCustomView; ++ protected ModalDialogManager mModalDialogManager; + private long mTotalBytes; + private @DownloadLocationDialogType int mDialogType; + private String mSuggestedPath; +@@ -130,7 +130,7 @@ public class DownloadLocationDialogCoordinator implements ModalDialogProperties. + * Called after retrieved the download directory options. + * @param dirs An list of available download directories. + */ +- private void onDirectoryOptionsRetrieved(ArrayList dirs) { ++ protected void onDirectoryOptionsRetrieved(ArrayList dirs) { + // Already showing the dialog. + if (mDialogModel != null) return; + +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index e647063331153..41f7c6e21de77 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -7525,6 +7525,11 @@ const char kThirdPartyProfileManagementDescription[] = + "Enables profile management triggered by third-party sign-ins."; + #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + ++const char kBookmarksExportUseSafName[] = "Use saf for bookmarks export"; ++const char kBookmarksExportUseSafDescription[] = ++ "When enabled user can choose where save the exported bookmarks " ++ "file."; ++ + // ============================================================================ + // Don't just add flags to the end, put them in the right section in + // alphabetical order just like the header file. +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 1557928890d3a..36b991b2f4c7c 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -4353,6 +4353,9 @@ extern const char kThirdPartyProfileManagementName[]; + extern const char kThirdPartyProfileManagementDescription[]; + #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + ++extern const char kBookmarksExportUseSafName[]; ++extern const char kBookmarksExportUseSafDescription[]; ++ + // ============================================================================ + // Don't just add flags to the end, put them in the right section in + // alphabetical order. See top instructions for more. +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +index e58da3d35ab47..1ba904380664c 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -180,6 +180,7 @@ const base::Feature* const kFeaturesExposedToJava[] = { + &kClearOmniboxFocusAfterNavigation, + &kCloseTabSuggestions, + &kCloseTabSaveTabList, ++ &kBookmarksExportUseSaf, + &kCriticalPersistedTabData, + &kCreateNewTabInitializeRenderer, + &kCCTBackgroundTab, +@@ -1173,5 +1174,10 @@ BASE_FEATURE(kWebApkTrampolineOnInitialIntent, + "WebApkTrampolineOnInitialIntent", + base::FEATURE_ENABLED_BY_DEFAULT); + ++// disabled by default because of an issue on Android 6.0 ++BASE_FEATURE(kBookmarksExportUseSaf, ++ "BookmarksExportUseSaf", ++ base::FEATURE_DISABLED_BY_DEFAULT); ++ + } // namespace android + } // namespace chrome +diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h +index 44589bd86f5fa..2da7d473a4652 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.h ++++ b/chrome/browser/flags/android/chrome_feature_list.h +@@ -185,6 +185,7 @@ BASE_DECLARE_FEATURE(kTabStripRedesign); + BASE_DECLARE_FEATURE(kTabletToolbarReordering); + BASE_DECLARE_FEATURE(kTabStripStartupRefactoring); + BASE_DECLARE_FEATURE(kTabToGTSAnimation); ++BASE_DECLARE_FEATURE(kBookmarksExportUseSaf); + BASE_DECLARE_FEATURE(kTestDefaultDisabled); + BASE_DECLARE_FEATURE(kTestDefaultEnabled); + BASE_DECLARE_FEATURE(kThumbnailPlaceholder); +diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +index 723da0089d718..b44789bcae8fb 100644 +--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java ++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +@@ -478,6 +478,7 @@ public abstract class ChromeFeatureList { + public static final String USE_LIBUNWINDSTACK_NATIVE_UNWINDER_ANDROID = + "UseLibunwindstackNativeUnwinderAndroid"; + public static final String USER_BYPASS_UI = "UserBypassUI"; ++ public static final String BOOKMARKS_EXPORT_USESAF = "BookmarksExportUseSaf"; + public static final String VOICE_BUTTON_IN_TOP_TOOLBAR = "VoiceButtonInTopToolbar"; + public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy"; + public static final String WEBNOTES_STYLIZE = "WebNotesStylize"; +diff --git a/chrome/browser/importer/profile_writer.cc b/chrome/browser/importer/profile_writer.cc +index 8196f333154dc..7904c2e93ff63 100644 +--- a/chrome/browser/importer/profile_writer.cc ++++ b/chrome/browser/importer/profile_writer.cc +@@ -106,12 +106,14 @@ void ProfileWriter::AddHistoryPage(const history::URLRows& page, + HistoryServiceFactory::GetForProfile(profile_, + ServiceAccessType::EXPLICIT_ACCESS) + ->AddPagesWithDetails(page, visit_source); ++#if !BUILDFLAG(IS_ANDROID) + // Measure the size of the history page after Auto Import on first run. + if (first_run::IsChromeFirstRun() && + visit_source == history::SOURCE_IE_IMPORTED) { + UMA_HISTOGRAM_COUNTS_1M("Import.ImportedHistorySize.AutoImportFromIE", + page.size()); + } ++#endif + } + + void ProfileWriter::AddHomepage(const GURL& home_page) { +@@ -132,6 +134,16 @@ void ProfileWriter::AddBookmarks( + return; + + BookmarkModel* model = BookmarkModelFactory::GetForBrowserContext(profile_); ++ AddBookmarksWithModel(model, bookmarks, top_level_folder_name); ++} ++ ++void ProfileWriter::AddBookmarksWithModel( ++ BookmarkModel* model, ++ const std::vector& bookmarks, ++ const std::u16string& top_level_folder_name) { ++ if (bookmarks.empty()) ++ return; ++ + DCHECK(model->loaded()); + + // If the bookmark bar is currently empty, we should import directly to it. +diff --git a/chrome/browser/importer/profile_writer.h b/chrome/browser/importer/profile_writer.h +index fd2612fcc9e3f..febf804edc41f 100644 +--- a/chrome/browser/importer/profile_writer.h ++++ b/chrome/browser/importer/profile_writer.h +@@ -11,6 +11,7 @@ + #include "base/memory/raw_ptr.h" + #include "base/memory/ref_counted.h" + #include "build/build_config.h" ++#include "components/bookmarks/browser/bookmark_model.h" + #include "components/favicon_base/favicon_usage_data.h" + #include "components/history/core/browser/history_types.h" + #include "components/search_engines/template_url_service.h" +@@ -71,6 +72,11 @@ class ProfileWriter : public base::RefCountedThreadSafe { + virtual void AddBookmarks(const std::vector& bookmarks, + const std::u16string& top_level_folder_name); + ++ virtual void AddBookmarksWithModel( ++ bookmarks::BookmarkModel* model, ++ const std::vector& bookmarks, ++ const std::u16string& top_level_folder_name); ++ + virtual void AddFavicons(const favicon_base::FaviconUsageDataList& favicons); + + // Adds the TemplateURLs in |template_urls| to the local store. +diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +index c7618ba0989cb..8596c3022dcb3 100644 +--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java ++++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +@@ -108,6 +108,8 @@ public final class ChromePreferenceKeys { + "enhanced_bookmark_last_used_parent_folder"; + public static final String BOOKMARKS_SORT_ORDER = "Chrome.Bookmarks.BookmarkRowSortOrder"; + public static final String BOOKMARKS_VISUALS_PREF = "Chrome.Bookmarks.BookmarkRowDisplay"; ++ public static final String BOOKMARKS_LAST_EXPORT_URI = ++ "Chrome.Bookmarks.Last_Export_Uri"; + + /** + * Whether Chrome is set as the default browser. +@@ -982,6 +984,7 @@ public final class ChromePreferenceKeys { + AUTOFILL_ASSISTANT_PROACTIVE_HELP_ENABLED, + APP_LAUNCH_LAST_KNOWN_ACTIVE_TAB_STATE, + APP_LAUNCH_SEARCH_ENGINE_HAD_LOGO, ++ BOOKMARKS_LAST_EXPORT_URI, + APPLICATION_OVERRIDE_LANGUAGE, + BLUETOOTH_NOTIFICATION_IDS, + BOOKMARKS_SORT_ORDER, +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index 1a25be398a062..bf095f7de2c72 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -245,6 +245,24 @@ CHAR_LIMIT guidelines: + + Sites + ++ ++ Import ++ ++ ++ Export ++ ++ ++ Export bookmarks to file ++ ++ ++ Do you want to overwrite %s? ++ ++ ++ Yes ++ ++ ++ Choose another file ++ + + Virtual Reality + +diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn +index 8a88d4bbde3f0..d7a0c22ed17e4 100644 +--- a/chrome/common/BUILD.gn ++++ b/chrome/common/BUILD.gn +@@ -397,6 +397,9 @@ static_library("common_lib") { + sources += [ + "media/chrome_media_drm_bridge_client.cc", + "media/chrome_media_drm_bridge_client.h", ++ ## Bromite dependencies for bookmark import functionality ++ "importer/imported_bookmark_entry.cc", ++ "importer/imported_bookmark_entry.h", + ] + } else { + # Non-Android. +diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn +index bf29364c22715..648aed296e74e 100644 +--- a/chrome/utility/BUILD.gn ++++ b/chrome/utility/BUILD.gn +@@ -85,8 +85,6 @@ static_library("utility") { + + if (!is_android) { + sources += [ +- "importer/bookmark_html_reader.cc", +- "importer/bookmark_html_reader.h", + "importer/bookmarks_file_importer.cc", + "importer/bookmarks_file_importer.h", + "importer/external_process_importer_bridge.cc", +@@ -215,6 +213,11 @@ static_library("utility") { + ] + } + ++ sources += [ ++ "importer/bookmark_html_reader.cc", ++ "importer/bookmark_html_reader.h", ++ ] ++ + # NSS decryptor is not needed on ChromeOS. + if (!is_chromeos && use_nss_certs) { + sources += [ +diff --git a/chrome/utility/importer/bookmark_html_reader.cc b/chrome/utility/importer/bookmark_html_reader.cc +index 81985a8417569..d819074feffdf 100644 +--- a/chrome/utility/importer/bookmark_html_reader.cc ++++ b/chrome/utility/importer/bookmark_html_reader.cc +@@ -17,7 +17,9 @@ + #include "base/strings/utf_string_conversions.h" + #include "base/time/time.h" + #include "chrome/common/importer/imported_bookmark_entry.h" ++#if !BUILDFLAG(IS_ANDROID) + #include "chrome/utility/importer/favicon_reencode.h" ++#endif + #include "components/search_engines/search_terms_data.h" + #include "components/search_engines/template_url.h" + #include "net/base/data_url.h" +@@ -55,6 +57,7 @@ bool GetAttribute(const std::string& attribute_list, + return true; + } + ++#if !BUILDFLAG(IS_ANDROID) + // Given the URL of a page and a favicon data URL, adds an appropriate record + // to the given favicon usage vector. + void DataURLToFaviconUsage(const GURL& link_url, +@@ -85,6 +88,7 @@ void DataURLToFaviconUsage(const GURL& link_url, + + favicons->push_back(usage); + } ++#endif + + } // namespace + +@@ -105,14 +109,28 @@ static std::string stripDt(const std::string& lineDt) { + } + + void ImportBookmarksFile( +- base::RepeatingCallback cancellation_callback, +- base::RepeatingCallback valid_url_callback, ++ const base::RepeatingCallback cancellation_callback, ++ const base::RepeatingCallback valid_url_callback, + const base::FilePath& file_path, + std::vector* bookmarks, + std::vector* search_engines, + favicon_base::FaviconUsageDataList* favicons) { + std::string content; +- base::ReadFileToString(file_path, &content); ++ if (!base::ReadFileToString(file_path, &content)) { ++ LOG(ERROR) << "Could not directly read bookmarks import file"; ++ return; ++ } ++ ++ ImportBookmarksFile(cancellation_callback, valid_url_callback, content, bookmarks, search_engines, favicons); ++} ++ ++void ImportBookmarksFile( ++ base::RepeatingCallback cancellation_callback, ++ base::RepeatingCallback valid_url_callback, ++ const std::string& content, ++ std::vector* bookmarks, ++ std::vector* search_engines, ++ favicon_base::FaviconUsageDataList* favicons) { + std::vector lines = base::SplitString( + content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + +@@ -125,6 +143,7 @@ void ImportBookmarksFile( + std::vector path; + size_t toolbar_folder_index = 0; + std::string charset = "UTF-8"; // If no charset is specified, assume utf-8. ++ + for (size_t i = 0; + i < lines.size() && + (cancellation_callback.is_null() || !cancellation_callback.Run()); +@@ -217,10 +236,12 @@ void ImportBookmarksFile( + } + bookmarks->push_back(entry); + ++#if !BUILDFLAG(IS_ANDROID) + // Save the favicon. DataURLToFaviconUsage will handle the case where + // there is no favicon. + if (favicons) + DataURLToFaviconUsage(url, favicon, favicons); ++#endif + + continue; + } +diff --git a/chrome/utility/importer/bookmark_html_reader.h b/chrome/utility/importer/bookmark_html_reader.h +index cc2532b6e0c46..8f29e4b2fc6d0 100644 +--- a/chrome/utility/importer/bookmark_html_reader.h ++++ b/chrome/utility/importer/bookmark_html_reader.h +@@ -50,6 +50,14 @@ void ImportBookmarksFile( + std::vector* search_engines, + favicon_base::FaviconUsageDataList* favicons); + ++void ImportBookmarksFile( ++ const base::RepeatingCallback cancellation_callback, ++ const base::RepeatingCallback valid_url_callback, ++ const std::string& content, ++ std::vector* bookmarks, ++ std::vector* search_engines, ++ favicon_base::FaviconUsageDataList* favicons); ++ + // Returns true if |url| should be imported as a search engine, i.e. because it + // has replacement terms. Chrome treats such bookmarks as search engines rather + // than true bookmarks. +diff --git a/components/headless/select_file_dialog/headless_select_file_dialog.cc b/components/headless/select_file_dialog/headless_select_file_dialog.cc +index e65d1b291dc2f..63fdce51456d6 100644 +--- a/components/headless/select_file_dialog/headless_select_file_dialog.cc ++++ b/components/headless/select_file_dialog/headless_select_file_dialog.cc +@@ -58,6 +58,10 @@ class HeadlessSelectFileDialog : public ui::SelectFileDialog { + // ui::SelectFileDialog: + bool HasMultipleFileTypeChoicesImpl() override { return false; } + ++ void ShowToast(const std::string& message) override { ++ // nothing to do, used only on android ++ } ++ + SelectFileDialogCallback callback_; + }; + +diff --git a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java +index 2124835b7aeaa..523fe81551eaf 100644 +--- a/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java ++++ b/ui/android/java/src/org/chromium/ui/base/SelectFileDialog.java +@@ -45,6 +45,7 @@ import org.chromium.base.task.AsyncTask; + import org.chromium.base.task.PostTask; + import org.chromium.base.task.TaskTraits; + import org.chromium.ui.R; ++import org.chromium.ui.widget.Toast; + import org.chromium.ui.UiUtils; + + import java.io.File; +@@ -67,6 +68,7 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + private static final String TAG = "SelectFileDialog"; + private static final String IMAGE_TYPE = "image"; + private static final String VIDEO_TYPE = "video"; ++ private static final String HTML_TYPE = "html"; + private static final String AUDIO_TYPE = "audio"; + private static final String ALL_TYPES = "*/*"; + +@@ -264,6 +266,11 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + ResettersForTesting.register(() -> mFileTypes = oldValue); + } + ++ @CalledByNative ++ private void showToast(String message) { ++ Toast.makeText(ContextUtils.getApplicationContext(), message, Toast.LENGTH_LONG).show(); ++ } ++ + /** + * Creates and starts an intent based on the passed fileTypes and capture value. + * @param fileTypes MIME types requested (i.e. "image/*") +@@ -290,7 +297,7 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + List missingPermissions = new ArrayList<>(); + String storagePermission = Manifest.permission.READ_EXTERNAL_STORAGE; + boolean shouldUsePhotoPicker = shouldUsePhotoPicker(); +- if (shouldUsePhotoPicker) { ++ if (shouldUsePhotoPicker || shouldShowHtmlTypes()) { + // The permission scenario for accessing media has evolved a bit over the years: + // Early on, READ_EXTERNAL_STORAGE was required to access media, but that permission was + // later deprecated. In its place (starting with Android T) READ_MEDIA_IMAGES and +@@ -339,7 +346,7 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + } + + // TODO(finnur): Remove once we figure out the cause of crbug.com/950024. +- if (shouldUsePhotoPicker) { ++ if (shouldUsePhotoPicker || shouldShowHtmlTypes()) { + if (permissions.length != requestPermissions.length) { + throw new RuntimeException( + String.format("Permissions arrays misaligned: %d != %d", +@@ -353,7 +360,7 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + } + } + +- if (shouldUsePhotoPicker) { ++ if (shouldUsePhotoPicker || shouldShowHtmlTypes()) { + if (permissions[i].equals(storagePermission) + || permissions[i].equals(Manifest.permission.READ_MEDIA_IMAGES) + || permissions[i].equals( +@@ -630,6 +637,7 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + } + if (!mimeTypes.contains(mimeType)) mimeTypes.add(mimeType); + } ++ if (mimeTypes.size() == 0) return null; + return mimeTypes; + } + +@@ -958,6 +966,10 @@ public class SelectFileDialog implements WindowAndroid.IntentCallback, PhotoPick + return countAcceptTypesFor(superType) == mFileTypes.size(); + } + ++ private boolean shouldShowHtmlTypes() { ++ return countAcceptTypesFor(HTML_TYPE) > 0; ++ } ++ + /** + * Checks whether the list of accepted types effectively describes only a single + * type, which might be wildcard. For example: +diff --git a/ui/android/java/strings/android_ui_strings.grd b/ui/android/java/strings/android_ui_strings.grd +index 57c783e27d97f..a1c563158daa6 100644 +--- a/ui/android/java/strings/android_ui_strings.grd ++++ b/ui/android/java/strings/android_ui_strings.grd +@@ -184,6 +184,9 @@ + + Unable to select media due to denied permissions + ++ ++ Failed to save selected file ++ + + + +diff --git a/ui/shell_dialogs/select_file_dialog.h b/ui/shell_dialogs/select_file_dialog.h +index 780665236e418..283119e777075 100644 +--- a/ui/shell_dialogs/select_file_dialog.h ++++ b/ui/shell_dialogs/select_file_dialog.h +@@ -211,6 +211,8 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog + const GURL* caller = nullptr); + bool HasMultipleFileTypeChoices(); + ++ virtual void ShowToast(const std::string& message) = 0; ++ + protected: + friend class base::RefCountedThreadSafe; + +diff --git a/ui/shell_dialogs/select_file_dialog_android.cc b/ui/shell_dialogs/select_file_dialog_android.cc +index f6384f36a7436..1ac2e911cea8b 100644 +--- a/ui/shell_dialogs/select_file_dialog_android.cc ++++ b/ui/shell_dialogs/select_file_dialog_android.cc +@@ -142,6 +142,12 @@ void SelectFileDialogImpl::SelectFileImpl( + owning_window->GetJavaObject()); + } + ++void SelectFileDialogImpl::ShowToast(const std::string& message) { ++ JNIEnv* env = base::android::AttachCurrentThread(); ++ ++ Java_SelectFileDialog_showToast(env, java_object_, base::android::ConvertUTF8ToJavaString(env, message)); ++} ++ + SelectFileDialogImpl::~SelectFileDialogImpl() { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_SelectFileDialog_nativeDestroyed(env, java_object_); +diff --git a/ui/shell_dialogs/select_file_dialog_android.h b/ui/shell_dialogs/select_file_dialog_android.h +index c5bc6ac23f582..fe6acdfdcbd15 100644 +--- a/ui/shell_dialogs/select_file_dialog_android.h ++++ b/ui/shell_dialogs/select_file_dialog_android.h +@@ -58,6 +58,8 @@ class SelectFileDialogImpl : public SelectFileDialog { + void* params, + const GURL* caller) override; + ++ void ShowToast(const std::string& message) override; ++ + protected: + ~SelectFileDialogImpl() override; + +diff --git a/ui/shell_dialogs/select_file_dialog_win.cc b/ui/shell_dialogs/select_file_dialog_win.cc +index 04aa920d91675..1f5560ccabdca 100644 +--- a/ui/shell_dialogs/select_file_dialog_win.cc ++++ b/ui/shell_dialogs/select_file_dialog_win.cc +@@ -193,6 +193,7 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, + int index); + + bool HasMultipleFileTypeChoicesImpl() override; ++ void ShowToast(const std::string& message) override; + + // Returns the filter to be used while displaying the open/save file dialog. + // This is computed from the extensions for the file types being opened. +@@ -271,6 +272,10 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { + return has_multiple_file_type_choices_; + } + ++void SelectFileDialogImpl::ShowToast(const std::string& message) { ++ // nothing to do, used only on android ++} ++ + bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow owning_window) const { + if (!owning_window->GetRootWindow()) + return false; +-- +2.34.1 + diff --git a/build/bromite_patches/Add-custom-tab-intents-privacy-option.patch b/build/bromite_patches/Add-custom-tab-intents-privacy-option.patch new file mode 100644 index 0000000000000000000000000000000000000000..a56b63019425453d3b797c81c701036b066eaf27 --- /dev/null +++ b/build/bromite_patches/Add-custom-tab-intents-privacy-option.patch @@ -0,0 +1,262 @@ +From dacc97850347a564244d243d0ceda0be3043f3c4 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Wed, 29 Aug 2018 11:03:44 +0200 +Subject: [PATCH 067/192] Add custom tab intents privacy option + +Add custom tab intents privacy option and force +open external links in incognito flag. + +Flags are mutually exclusive. + +See also: https://github.com/bromite/bromite/issues/1474 + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../java/res/xml/privacy_preferences.xml | 10 ++++++++ + .../browser/LaunchIntentDispatcher.java | 21 ++++++++++++++++ + .../IncognitoCustomTabIntentDataProvider.java | 6 +++++ + .../privacy/settings/PrivacySettings.java | 24 +++++++++++++++++++ + .../flags/android/chrome_feature_list.cc | 6 ++--- + .../chrome/browser/tab/TabAssociatedApp.java | 6 ++++- + .../strings/android_chrome_strings.grd | 14 +++++++++++ + .../core/common/language_experiments.cc | 4 ++-- + 8 files changed, 85 insertions(+), 6 deletions(-) + +diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml +index 68b5042d47935..bb0c084bca718 100644 +--- a/chrome/android/java/res/xml/privacy_preferences.xml ++++ b/chrome/android/java/res/xml/privacy_preferences.xml +@@ -68,6 +68,16 @@ found in the LICENSE file. + android:fragment="org.chromium.chrome.browser.privacy.settings.DoNotTrackSettings" + android:key="do_not_track" + android:title="@string/do_not_track_title"/> ++ ++ + ", "", servicesLink)); + } + ++ public static final String PREF_ALLOW_CUSTOM_TAB_INTENTS = "allow_custom_tab_intents"; ++ public static final String PREF_OPEN_EXTERNAL_LINKS_INCOGNITO = "open_external_links_incognito"; ++ + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String key = preference.getKey(); +@@ -260,6 +266,14 @@ public class PrivacySettings extends PreferenceFragmentCompat + } else if (PREF_SEARCH_SUGGESTIONS.equals(key)) { + UserPrefs.get(Profile.getLastUsedRegularProfile()) + .setBoolean(Pref.SEARCH_SUGGEST_ENABLED, (boolean) newValue); ++ } else if (PREF_ALLOW_CUSTOM_TAB_INTENTS.equals(key)) { ++ SharedPreferences.Editor sharedPreferencesEditor = ContextUtils.getAppSharedPreferences().edit(); ++ sharedPreferencesEditor.putBoolean(PREF_ALLOW_CUSTOM_TAB_INTENTS, (boolean)newValue); ++ sharedPreferencesEditor.apply(); ++ } else if (PREF_OPEN_EXTERNAL_LINKS_INCOGNITO.equals(key)) { ++ SharedPreferences.Editor sharedPreferencesEditor = ContextUtils.getAppSharedPreferences().edit(); ++ sharedPreferencesEditor.putBoolean(PREF_OPEN_EXTERNAL_LINKS_INCOGNITO, (boolean)newValue); ++ sharedPreferencesEditor.apply(); + } + return true; + } +@@ -283,6 +297,16 @@ public class PrivacySettings extends PreferenceFragmentCompat + UserPrefs.get(mProfile).getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED)); + } + ++ allowCustomTabIntentsPref = ++ (ChromeSwitchPreference) findPreference(PREF_ALLOW_CUSTOM_TAB_INTENTS); ++ allowCustomTabIntentsPref.setOnPreferenceChangeListener(this); ++ allowCustomTabIntentsPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate); ++ ++ openExternalLinksPref = ++ (ChromeSwitchPreference) findPreference(PREF_OPEN_EXTERNAL_LINKS_INCOGNITO); ++ openExternalLinksPref.setOnPreferenceChangeListener(this); ++ openExternalLinksPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate); ++ + Preference doNotTrackPref = findPreference(PREF_DO_NOT_TRACK); + if (doNotTrackPref != null) { + doNotTrackPref.setSummary(UserPrefs.get(mProfile).getBoolean(Pref.ENABLE_DO_NOT_TRACK) +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +index cf06093694030..7d851dcccf177 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -558,8 +558,8 @@ BASE_FEATURE(kCCTIncognitoAvailableToThirdParty, + base::FEATURE_ENABLED_BY_DEFAULT); // in Bromite + + BASE_FEATURE(kCCTIntentFeatureOverrides, +- "CCTIntentFeatureOverrides", +- base::FEATURE_ENABLED_BY_DEFAULT); ++ "CCTIntentFeatureOverrides", // must be disabled ++ base::FEATURE_DISABLED_BY_DEFAULT); // in Bromite + + BASE_FEATURE(kCCTPageInsightsHub, + "CCTPageInsightsHub", +@@ -567,7 +567,7 @@ BASE_FEATURE(kCCTPageInsightsHub, + + BASE_FEATURE(kCCTPostMessageAPI, + "CCTPostMessageAPI", +- base::FEATURE_ENABLED_BY_DEFAULT); ++ base::FEATURE_DISABLED_BY_DEFAULT); + + BASE_FEATURE(kCCTPrefetchDelayShowOnStart, + "CCTPrefetchDelayShowOnStart", +diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAssociatedApp.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAssociatedApp.java +index 9ea6ff6d6d85f..16c6a1fd44976 100644 +--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAssociatedApp.java ++++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabAssociatedApp.java +@@ -84,7 +84,11 @@ public final class TabAssociatedApp extends TabWebContentsUserData implements Im + public static boolean isOpenedFromExternalApp(Tab tab) { + TabAssociatedApp app = get(tab); + if (app == null) return false; +- ++ if (ContextUtils.getAppSharedPreferences() ++ .getBoolean("open_external_links_incognito", false) && ++ tab.isIncognito() && ++ tab.getLaunchType() == TabLaunchType.FROM_EXTERNAL_APP) ++ return true; + String packageName = ContextUtils.getApplicationContext().getPackageName(); + return tab.getLaunchType() == TabLaunchType.FROM_EXTERNAL_APP + && !TextUtils.equals(app.getAppId(), packageName); +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index efe12c746f899..81cf2c14f3339 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -5087,6 +5087,20 @@ To change this setting, <resetlink>reset sync

+ Resume + ++ ++ ++ Allow custom tab intents ++ ++ ++ Allow applications to open custom tab intents, similar to webview. ++ ++ ++ ++ Open external links in incognito ++ ++ ++ Force the opening of all external links in incognito mode ++ + + + Show your Chrome activity in Digital Wellbeing? +diff --git a/components/language/core/common/language_experiments.cc b/components/language/core/common/language_experiments.cc +index 8846600ff1bd5..300f4bae8f166 100644 +--- a/components/language/core/common/language_experiments.cc ++++ b/components/language/core/common/language_experiments.cc +@@ -18,8 +18,8 @@ BASE_FEATURE(kContentLanguagesInLanguagePicker, + "ContentLanguagesInLanguagePicker", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kCctAutoTranslate, +- "CCTAutoTranslate", +- base::FEATURE_ENABLED_BY_DEFAULT); ++ "CCTAutoTranslate", // Always disabled ++ base::FEATURE_DISABLED_BY_DEFAULT); // in Bromite + + // Params: + const char kContentLanguagesDisableObserversParam[] = "disable_observers"; +-- +2.34.1 + diff --git a/build/bromite_patches/Add-exit-menu-item.patch b/build/bromite_patches/Add-exit-menu-item.patch new file mode 100644 index 0000000000000000000000000000000000000000..7397d49a9c34ed8b9bdc67168195f01c0c48446c --- /dev/null +++ b/build/bromite_patches/Add-exit-menu-item.patch @@ -0,0 +1,141 @@ +From 8d1a9ef8978784037f4667ca43c3c47f724c2d96 Mon Sep 17 00:00:00 2001 +From: Serg +Date: Tue, 31 Jan 2017 22:12:27 -0500 +Subject: [PATCH 054/192] Add exit menu item + +Corrected Exit functionality + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/java/res/menu/main_menu.xml | 6 ++++++ + .../org/chromium/chrome/browser/ApplicationLifetime.java | 2 ++ + .../org/chromium/chrome/browser/ChromeTabbedActivity.java | 4 ++++ + .../src/org/chromium/chrome/browser/app/ChromeActivity.java | 6 ++++++ + .../chrome/browser/init/ChromeLifetimeController.java | 6 +++++- + .../browser/ui/android/strings/android_chrome_strings.grd | 3 +++ + 6 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml +index 89e85421d844e..ff60ec8ddf5f5 100644 +--- a/chrome/android/java/res/menu/main_menu.xml ++++ b/chrome/android/java/res/menu/main_menu.xml +@@ -161,6 +161,9 @@ found in the LICENSE file. + ++ + + + +@@ -187,6 +190,9 @@ found in the LICENSE file. + ++ + + + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ApplicationLifetime.java b/chrome/android/java/src/org/chromium/chrome/browser/ApplicationLifetime.java +index 38987c86b46d9..7ba324967588f 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/ApplicationLifetime.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ApplicationLifetime.java +@@ -6,6 +6,7 @@ package org.chromium.chrome.browser; + + import org.chromium.base.ObserverList; + import org.chromium.base.annotations.CalledByNative; ++import org.chromium.chrome.browser.incognito.IncognitoNotificationManager; + + /** + * Watches for when Chrome is told to restart itself. +@@ -42,6 +43,7 @@ public class ApplicationLifetime { + + @CalledByNative + public static void terminate(boolean restart) { ++ IncognitoNotificationManager.dismissIncognitoNotification(); + for (Observer observer : sObservers) { + observer.onTerminate(restart); + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +index 2aa6ab6379d63..267f8c441ea51 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +@@ -229,6 +229,8 @@ import java.util.Locale; + import java.util.Set; + import java.util.concurrent.atomic.AtomicBoolean; + ++import org.chromium.chrome.browser.ApplicationLifetime; ++ + /** + * This is the main activity for ChromeMobile when not running in document mode. All the tabs + * are accessible via a chrome specific tab switching UI. +@@ -2265,6 +2267,8 @@ public class ChromeTabbedActivity extends ChromeActivity + return true; + } + ++ if (id == R.id.exit_id) { ++ ApplicationLifetime.terminate(false); ++ return true; ++ } ++ + if (id == R.id.update_menu_id) { + UpdateMenuItemHelper.getInstance().onMenuItemClicked(this); + return true; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeLifetimeController.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeLifetimeController.java +index 85548428958f9..93379024a0d94 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeLifetimeController.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeLifetimeController.java +@@ -82,7 +82,11 @@ class ChromeLifetimeController implements ApplicationLifetime.Observer, + + // Kick off a timer to kill the process after a delay, which fires only if the Activities + // take too long to be finished. +- mHandler.postDelayed(mRestartRunnable, WATCHDOG_DELAY_MS); ++ if (restart) { ++ mHandler.postDelayed(mRestartRunnable, WATCHDOG_DELAY_MS); ++ } else { ++ fireBrowserRestartActivityIntent(); ++ } + } + + @Override +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index 2e03f7dacec79..1a25be398a062 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -3741,6 +3741,9 @@ To change this setting, <resetlink>reset sync

+ Dark theme + ++ ++ Exit ++ + + Appearance + +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-for-omnibox-autocomplete-filtering.patch b/build/bromite_patches/Add-flag-for-omnibox-autocomplete-filtering.patch new file mode 100644 index 0000000000000000000000000000000000000000..3529b284452da8fda6fd36ee9fd9c219a31ab74f --- /dev/null +++ b/build/bromite_patches/Add-flag-for-omnibox-autocomplete-filtering.patch @@ -0,0 +1,146 @@ +From 1fcad47a0f26587b8b6150685dd34335fad1cc6c Mon Sep 17 00:00:00 2001 +From: Blaise +Date: Sat, 22 Aug 2020 08:52:40 -0500 +Subject: [PATCH 132/192] Add flag for omnibox autocomplete filtering + +Adds a flag that restricts whether search history, clipboard, bookmarks +and internal chrome:// pages will be used for the autocomplete results. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 22 ++++++++++++++++++- + .../browser/autocomplete_controller.cc | 10 +++++++++ + .../omnibox/browser/history_url_provider.cc | 3 +++ + components/omnibox/browser/search_provider.cc | 4 ++++ + components/url_formatter/url_fixer.cc | 4 ++++ + 5 files changed, 42 insertions(+), 1 deletion(-) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index cde019cdb2851..75e4850949020 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -589,6 +589,22 @@ const FeatureEntry::FeatureVariation kReaderModeDiscoverabilityVariations[] = { + #endif // BUILDFLAG(IS_ANDROID) + + #if BUILDFLAG(IS_ANDROID) ++const FeatureEntry::Choice kOmniboxAutocompleteFiltering[] = { ++ {flags_ui::kGenericExperimentChoiceDefault, "", ""}, ++ {"Search suggestions only", ++ "omnibox-autocomplete-filtering", ++ "search"}, ++ {"Search suggestions and bookmarks", ++ "omnibox-autocomplete-filtering", ++ "search-bookmarks"}, ++ {"Search suggestions and internal chrome pages", ++ "omnibox-autocomplete-filtering", ++ "search-chrome"}, ++ {"Search suggestions, bookmarks, and internal chrome pages", ++ "omnibox-autocomplete-filtering", ++ "search-bookmarks-chrome"}, ++}; ++ + const FeatureEntry::FeatureParam kAdaptiveButton_AlwaysNone[] = { + {"mode", "always-none"}}; + const FeatureEntry::FeatureParam kAdaptiveButton_AlwaysNewTab[] = { +@@ -4701,7 +4717,11 @@ const FeatureEntry kFeatureEntries[] = { + kOsMac | kOsLinux | kOsWin, + FEATURE_VALUE_TYPE(features::kSystemNotifications)}, + #endif // BUILDFLAG(ENABLE_SYSTEM_NOTIFICATIONS) && !BUILDFLAG(IS_CHROMEOS_ASH) +-#if BUILDFLAG(IS_ANDROID) ++#if BUILDFLAG(IS_ANDROID) // Bromite autocomplete filtering ++ {"omnibox-autocomplete-filtering", ++ "Omnibox Autocomplete Filtering", ++ "Restrict omnibox autocomplete results to a combination of search suggestions (if enabled), bookmarks, and internal chrome pages.", ++ kOsAll, MULTI_VALUE_TYPE(kOmniboxAutocompleteFiltering)}, + {"adaptive-button-in-top-toolbar", + flag_descriptions::kAdaptiveButtonInTopToolbarName, + flag_descriptions::kAdaptiveButtonInTopToolbarDescription, kOsAndroid, +diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc +index 1af55724187f0..dd1d6b8713493 100644 +--- a/components/omnibox/browser/autocomplete_controller.cc ++++ b/components/omnibox/browser/autocomplete_controller.cc +@@ -18,6 +18,7 @@ + #include "base/barrier_callback.h" + #include "base/check_op.h" + #include "base/containers/contains.h" ++#include "base/command_line.h" + #include "base/feature_list.h" + #include "base/format_macros.h" + #include "base/functional/bind.h" +@@ -366,6 +367,15 @@ AutocompleteController::AutocompleteController( + provider_client_->GetOmniboxTriggeredFeatureService()), + steady_state_omnibox_position_( + metrics::OmniboxEventProto::UNKNOWN_POSITION) { ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("omnibox-autocomplete-filtering")) { ++ const std::string flag_value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("omnibox-autocomplete-filtering"); ++ provider_types &= AutocompleteProvider::TYPE_KEYWORD | AutocompleteProvider::TYPE_SEARCH | ++ AutocompleteProvider::TYPE_HISTORY_URL | AutocompleteProvider::TYPE_BOOKMARK | AutocompleteProvider::TYPE_BUILTIN; ++ if (!base::Contains(flag_value, "bookmarks")) ++ provider_types &= ~AutocompleteProvider::TYPE_BOOKMARK; ++ if (!base::Contains(flag_value, "chrome")) ++ provider_types &= ~AutocompleteProvider::TYPE_BUILTIN; ++ } + provider_types &= ~OmniboxFieldTrial::GetDisabledProviderTypes(); + + // Providers run in the order they're added. Async providers should run first +diff --git a/components/omnibox/browser/history_url_provider.cc b/components/omnibox/browser/history_url_provider.cc +index 471a49da4421e..e586f793a2459 100644 +--- a/components/omnibox/browser/history_url_provider.cc ++++ b/components/omnibox/browser/history_url_provider.cc +@@ -478,6 +478,9 @@ void HistoryURLProvider::Start(const AutocompleteInput& input, + if (fixed_up_input.type() != metrics::OmniboxInputType::QUERY) + matches_.push_back(what_you_typed_match); + ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("omnibox-autocomplete-filtering")) ++ return; ++ + // We'll need the history service to run both passes, so try to obtain it. + history::HistoryService* const history_service = + client()->GetHistoryService(); +diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc +index a385686403d8a..c686720ef0ade 100644 +--- a/components/omnibox/browser/search_provider.cc ++++ b/components/omnibox/browser/search_provider.cc +@@ -12,6 +12,7 @@ + #include "base/base64.h" + #include "base/feature_list.h" + #include "base/functional/bind.h" ++#include "base/command_line.h" + #include "base/functional/callback.h" + #include "base/i18n/break_iterator.h" + #include "base/i18n/case_conversion.h" +@@ -649,6 +650,9 @@ void SearchProvider::Run(bool query_is_private) { + } + + void SearchProvider::DoHistoryQuery(bool minimal_changes) { ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("omnibox-autocomplete-filtering")) ++ return; ++ + // The history query results are synchronous, so if minimal_changes is true, + // we still have the last results and don't need to do anything. + if (minimal_changes) +diff --git a/components/url_formatter/url_fixer.cc b/components/url_formatter/url_fixer.cc +index 3db922698161e..1fa79fc8eef3a 100644 +--- a/components/url_formatter/url_fixer.cc ++++ b/components/url_formatter/url_fixer.cc +@@ -7,6 +7,8 @@ + #include + + #include "base/check_op.h" ++#include "base/containers/contains.h" ++#include "base/command_line.h" + #include "base/files/file_path.h" + #include "base/files/file_util.h" + #include "base/i18n/char_iterator.h" +@@ -608,6 +610,8 @@ GURL FixupURL(const std::string& text, const std::string& desired_tld) { + + FixupHost(trimmed, parts.host, parts.scheme.is_valid(), desired_tld, &url); + if (chrome_url && !parts.host.is_valid()) ++ if (!base::CommandLine::ForCurrentProcess()->HasSwitch("omnibox-autocomplete-filtering") || ++ base::Contains(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("omnibox-autocomplete-filtering"), "chrome")) + url.append(kChromeUIDefaultHost); + FixupPort(trimmed, parts.port, &url); + FixupPath(trimmed, parts.path, &url); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-for-save-data-header.patch b/build/bromite_patches/Add-flag-for-save-data-header.patch new file mode 100644 index 0000000000000000000000000000000000000000..b8d3eda9d25f5c615b98e25219c0360be3958317 --- /dev/null +++ b/build/bromite_patches/Add-flag-for-save-data-header.patch @@ -0,0 +1,120 @@ +From e0497105e85dbffca54704f8a885267c6432cf45 Mon Sep 17 00:00:00 2001 +From: Wengling Chen +Date: Mon, 1 Feb 2021 19:18:55 +0200 +Subject: [PATCH 159/192] Add flag for save-data-header + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 5 ++++- + chrome/browser/flag_descriptions.cc | 4 ++++ + chrome/browser/flag_descriptions.h | 3 +++ + .../browser/loader/browser_initiated_resource_request.cc | 8 ++++++++ + services/network/public/cpp/features.cc | 5 +++++ + services/network/public/cpp/features.h | 2 ++ + 6 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 9f33b38a77a7f..0006633c72b7b 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -5308,7 +5308,10 @@ const FeatureEntry kFeatureEntries[] = { + {"force-text-direction", flag_descriptions::kForceTextDirectionName, + flag_descriptions::kForceTextDirectionDescription, kOsAll, + MULTI_VALUE_TYPE(kForceTextDirectionChoices)}, +-#if BUILDFLAG(IS_ANDROID) ++#if BUILDFLAG(IS_ANDROID) // Bromite save data header ++ {"enable-save-data-header", flag_descriptions::kEnableSaveDataHeaderName, ++ flag_descriptions::kEnableSaveDataHeaderDescription, kOsAndroid, ++ FEATURE_VALUE_TYPE(network::features::kEnableSaveDataHeader)}, + {"force-update-menu-type", flag_descriptions::kUpdateMenuTypeName, + flag_descriptions::kUpdateMenuTypeDescription, kOsAndroid, + MULTI_VALUE_TYPE(kForceUpdateMenuTypeChoices)}, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 1a2ef005ff6a8..7b2a885c9a4cc 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -1386,6 +1386,10 @@ const char kDeprecateAltBasedSixPackDescription[] = + "Show deprecation notifications and disable functionality for Alt based " + "six pack deprecations. The Search based versions continue to work."; + ++const char kEnableSaveDataHeaderName[] = "Enable save-data header"; ++const char kEnableSaveDataHeaderDescription[] = ++ "Enable save-data header without enabling Data Saver."; ++ + const char kDeprecateOldKeyboardShortcutsAcceleratorName[] = + "Enable deprecation notifications for Ctrl+Alt+/ to open Keyboard " + "shortcuts app"; +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 7b062e1f597fd..fb4a3613f4249 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -715,6 +715,9 @@ extern const char kDeprecateAltBasedSixPackDescription[]; + extern const char kDeprecateOldKeyboardShortcutsAcceleratorName[]; + extern const char kDeprecateOldKeyboardShortcutsAcceleratorDescription[]; + ++extern const char kEnableSaveDataHeaderName[]; ++extern const char kEnableSaveDataHeaderDescription[]; ++ + extern const char kMemlogName[]; + extern const char kMemlogDescription[]; + extern const char kMemlogModeMinimal[]; +diff --git a/content/browser/loader/browser_initiated_resource_request.cc b/content/browser/loader/browser_initiated_resource_request.cc +index eccf66c36501d..3a517f4d9f103 100644 +--- a/content/browser/loader/browser_initiated_resource_request.cc ++++ b/content/browser/loader/browser_initiated_resource_request.cc +@@ -4,6 +4,9 @@ + + #include "content/browser/loader/browser_initiated_resource_request.h" + ++#include "base/feature_list.h" ++#include "services/network/public/cpp/features.h" ++ + #include "content/public/browser/browser_context.h" + #include "content/public/browser/browser_thread.h" + #include "content/public/browser/content_browser_client.h" +@@ -37,8 +40,13 @@ void UpdateAdditionalHeadersForBrowserInitiatedRequest( + // Save-Data was previously included in hints for workers, thus we cannot + // remove it for the time being. If you're reading this, consider building + // permissions policies for workers and/or deprecating this inclusion. ++ bool setHeader = false; + if (is_for_worker_script && + GetContentClient()->browser()->IsDataSaverEnabled(browser_context)) { ++ setHeader = true; ++ } ++ setHeader |= base::FeatureList::IsEnabled(network::features::kEnableSaveDataHeader); ++ if (setHeader) { + if (should_update_existing_headers) { + headers->RemoveHeader("Save-Data"); + } +diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc +index 0538a4f7f55b0..452c66c24ab84 100644 +--- a/services/network/public/cpp/features.cc ++++ b/services/network/public/cpp/features.cc +@@ -13,6 +13,11 @@ + + namespace network::features { + ++// Enable save-data header separately (without enabled data reduction service). ++BASE_FEATURE(kEnableSaveDataHeader, ++ "EnableSaveDataHeader", ++ base::FEATURE_DISABLED_BY_DEFAULT); ++ + BASE_FEATURE(kNetworkErrorLogging, + "NetworkErrorLogging", // disabled by default + base::FEATURE_DISABLED_BY_DEFAULT); // in bromite +diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h +index a24fd57094b72..d4d6967aa8833 100644 +--- a/services/network/public/cpp/features.h ++++ b/services/network/public/cpp/features.h +@@ -89,6 +89,8 @@ extern uint32_t GetLoaderChunkSize(); + COMPONENT_EXPORT(NETWORK_CPP) + BASE_DECLARE_FEATURE(kCorsNonWildcardRequestHeadersSupport); + ++COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kEnableSaveDataHeader); ++ + COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kNetworkServiceMemoryCache); + + COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kOmitCorsClientCert); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-to-configure-maximum-connections-per-host.patch b/build/bromite_patches/Add-flag-to-configure-maximum-connections-per-host.patch new file mode 100644 index 0000000000000000000000000000000000000000..6cbfdeee7fcab343c36d72ef66593c48e0d420f4 --- /dev/null +++ b/build/bromite_patches/Add-flag-to-configure-maximum-connections-per-host.patch @@ -0,0 +1,168 @@ +From f9d09645f620097086fb8d70e944c539995ed6e4 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sun, 8 Jul 2018 22:42:04 +0200 +Subject: [PATCH 058/192] Add flag to configure maximum connections per host + +With the introduction of this flag it is possible to increase the maximum +allowed connections per host; this can however be detrimental to devices +with limited CPU/memory resources and it is disabled by default. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 8 ++++++++ + chrome/browser/flag_descriptions.cc | 4 ++++ + chrome/browser/flag_descriptions.h | 3 +++ + .../common/network_features.cc | 3 +++ + .../common/network_features.h | 4 ++++ + .../common/network_switch_list.h | 4 ++++ + .../spoof_checks/top_domains/BUILD.gn | 1 + + net/socket/client_socket_pool_manager.cc | 17 +++++++++++++++++ + 8 files changed, 44 insertions(+) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 3f5f94ed63936..78c2b1a2de4e7 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -1236,6 +1236,11 @@ const FeatureEntry::Choice kForceColorProfileChoices[] = { + switches::kForceDisplayColorProfile, "hdr10"}, + }; + ++const FeatureEntry::Choice kMaxConnectionsPerHostChoices[] = { ++ {features::kMaxConnectionsPerHostChoiceDefault, "", ""}, ++ {features::kMaxConnectionsPerHostChoice15, switches::kMaxConnectionsPerHost, "15"}, ++}; ++ + const FeatureEntry::Choice kMemlogModeChoices[] = { + {flags_ui::kGenericExperimentChoiceDisabled, "", ""}, + {flag_descriptions::kMemlogModeMinimal, heap_profiling::kMemlogMode, +@@ -5210,6 +5215,9 @@ const FeatureEntry kFeatureEntries[] = { + FEATURE_VALUE_TYPE(chrome::android::kShareSheetCustomActionsPolish)}, + + #endif // BUILDFLAG(IS_ANDROID) ++ {"max-connections-per-host", flag_descriptions::kMaxConnectionsPerHostName, ++ flag_descriptions::kMaxConnectionsPerHostDescription, kOsAll, ++ MULTI_VALUE_TYPE(kMaxConnectionsPerHostChoices)}, + {"disallow-doc-written-script-loads", + flag_descriptions::kDisallowDocWrittenScriptsUiName, + flag_descriptions::kDisallowDocWrittenScriptsUiDescription, kOsAll, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 9b9d00359008a..e647063331153 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -2157,6 +2157,10 @@ const char kUnthrottledNestedTimeoutDescription[] = + "websites abusing the API will still eventually have their setTimeouts " + "clamped."; + ++const char kMaxConnectionsPerHostName[] = "Maximum connections per host"; ++const char kMaxConnectionsPerHostDescription[] = ++ "Customize maximum allowed connections per host."; ++ + const char kMediaRouterCastAllowAllIPsName[] = + "Connect to Cast devices on all IP addresses"; + const char kMediaRouterCastAllowAllIPsDescription[] = +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index e6dc3d91746f3..1557928890d3a 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -1219,6 +1219,9 @@ extern const char kUndoAutofillDescription[]; + extern const char kUnthrottledNestedTimeoutName[]; + extern const char kUnthrottledNestedTimeoutDescription[]; + ++extern const char kMaxConnectionsPerHostName[]; ++extern const char kMaxConnectionsPerHostDescription[]; ++ + extern const char kMediaRouterCastAllowAllIPsName[]; + extern const char kMediaRouterCastAllowAllIPsDescription[]; + +diff --git a/components/network_session_configurator/common/network_features.cc b/components/network_session_configurator/common/network_features.cc +index c1bd42fa99e7b..f6387f1cd9362 100644 +--- a/components/network_session_configurator/common/network_features.cc ++++ b/components/network_session_configurator/common/network_features.cc +@@ -8,4 +8,7 @@ + + namespace features { + ++const char kMaxConnectionsPerHostChoiceDefault[] = "6", ++ kMaxConnectionsPerHostChoice15[] = "15"; ++ + } // namespace features +diff --git a/components/network_session_configurator/common/network_features.h b/components/network_session_configurator/common/network_features.h +index 84d71f126019d..0cbcd7f5b0d17 100644 +--- a/components/network_session_configurator/common/network_features.h ++++ b/components/network_session_configurator/common/network_features.h +@@ -10,6 +10,10 @@ + + namespace features { + ++NETWORK_SESSION_CONFIGURATOR_EXPORT extern const char kMaxConnectionsPerHostChoiceDefault[], ++ kMaxConnectionsPerHostChoice6[], ++ kMaxConnectionsPerHostChoice15[]; ++ + } // namespace features + + #endif // COMPONENTS_NETWORK_SESSION_CONFIGURATOR_COMMON_NETWORK_FEATURES_H_ +diff --git a/components/network_session_configurator/common/network_switch_list.h b/components/network_session_configurator/common/network_switch_list.h +index 9f759da5d665a..0d152ec53fb1e 100644 +--- a/components/network_session_configurator/common/network_switch_list.h ++++ b/components/network_session_configurator/common/network_switch_list.h +@@ -19,6 +19,10 @@ NETWORK_SWITCH(kEnableUserAlternateProtocolPorts, + // Enables the QUIC protocol. This is a temporary testing flag. + NETWORK_SWITCH(kEnableQuic, "enable-quic") + ++// Allows specifying a higher number of maximum connections per host ++// (15 instead of 6, mirroring the value Mozilla uses). ++NETWORK_SWITCH(kMaxConnectionsPerHost, "max-connections-per-host") ++ + // Ignores certificate-related errors. + NETWORK_SWITCH(kIgnoreCertificateErrors, "ignore-certificate-errors") + +diff --git a/components/url_formatter/spoof_checks/top_domains/BUILD.gn b/components/url_formatter/spoof_checks/top_domains/BUILD.gn +index 97c28c796637b..c3083e3dc11c0 100644 +--- a/components/url_formatter/spoof_checks/top_domains/BUILD.gn ++++ b/components/url_formatter/spoof_checks/top_domains/BUILD.gn +@@ -89,6 +89,7 @@ executable("make_top_domain_list_variables") { + "//base:i18n", + "//components/url_formatter/spoof_checks/common_words:common", + "//third_party/icu", ++ "//components/network_session_configurator/common" + ] + if (is_ios) { + frameworks = [ "UIKit.framework" ] +diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc +index 521ebc8f5023e..41692c9212b16 100644 +--- a/net/socket/client_socket_pool_manager.cc ++++ b/net/socket/client_socket_pool_manager.cc +@@ -19,6 +19,10 @@ + #include "net/socket/client_socket_handle.h" + #include "net/socket/client_socket_pool.h" + #include "net/socket/connect_job.h" ++#include "components/network_session_configurator/common/network_switches.h" ++ ++#include "base/command_line.h" ++#include "base/strings/string_number_conversions.h" + #include "net/ssl/ssl_config.h" + #include "third_party/abseil-cpp/absl/types/optional.h" + #include "url/gurl.h" +@@ -172,6 +176,19 @@ void ClientSocketPoolManager::set_max_sockets_per_pool( + int ClientSocketPoolManager::max_sockets_per_group( + HttpNetworkSession::SocketPoolType pool_type) { + DCHECK_LT(pool_type, HttpNetworkSession::NUM_SOCKET_POOL_TYPES); ++ ++ if (pool_type == HttpNetworkSession::NORMAL_SOCKET_POOL) { ++ int maxConnectionsPerHost = 0; ++ auto value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kMaxConnectionsPerHost); ++ if (!value.empty() && !base::StringToInt(value, &maxConnectionsPerHost)) { ++ LOG(DFATAL) << "--" << switches::kMaxConnectionsPerHost << " only accepts integers as arguments (\"" << value << "\" is invalid)"; ++ } ++ if (maxConnectionsPerHost != 0) { ++ return maxConnectionsPerHost; ++ } ++ // fallthrough for default value ++ } ++ + return g_max_sockets_per_group[pool_type]; + } + +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-to-control-video-playback-resume-feature.patch b/build/bromite_patches/Add-flag-to-control-video-playback-resume-feature.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ace952c83e1acbbea6fd53aec1ec2d5db08ba51 --- /dev/null +++ b/build/bromite_patches/Add-flag-to-control-video-playback-resume-feature.patch @@ -0,0 +1,82 @@ +From d6e9d7342d348956f362623156bd2358360416e1 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Thu, 25 Oct 2018 23:13:34 +0200 +Subject: [PATCH 053/192] Add flag to control video playback resume feature + +Disable it by default on Android as it is everywhere else + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 4 ++++ + chrome/browser/flag_descriptions.cc | 5 +++++ + chrome/browser/flag_descriptions.h | 3 +++ + media/base/media_switches.cc | 8 ++------ + 4 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 9f77b18dcbb73..82741e95ba8a4 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -3940,6 +3940,10 @@ const FeatureEntry kFeatureEntries[] = { + chrome::android::kContextualSearchSuppressShortView, + kContextualSearchSuppressShortViewVariations, + "ContextualSearchSuppressShortView")}, ++ {"resume-background-video", ++ flag_descriptions::kResumeBackgroundVideoName, ++ flag_descriptions::kResumeBackgroundVideoDescription, kOsAll, ++ FEATURE_VALUE_TYPE(media::kResumeBackgroundVideo)}, + {"related-searches", flag_descriptions::kRelatedSearchesName, + flag_descriptions::kRelatedSearchesDescription, kOsAndroid, + FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kRelatedSearches, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index c0f92c31f481b..c631c713f425c 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -1462,6 +1462,11 @@ const char kEnableManagedConfigurationWebApiDescription[] = + "Allows website to access a managed configuration provided by the device " + "administrator for the origin."; + ++const char kResumeBackgroundVideoName[] = "Resume background video."; ++const char kResumeBackgroundVideoDescription[] = ++ "Resume background video playback when tab re-gains focus; additionally, " ++ "it will pause video playback every single time you switch tabs."; ++ + const char kEnablePixelCanvasRecordingName[] = "Enable pixel canvas recording"; + const char kEnablePixelCanvasRecordingDescription[] = + "Pixel canvas recording allows the compositor to raster contents aligned " +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index e408454306339..302a8e99a898e 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -800,6 +800,9 @@ extern const char kEnablePenetratingImageSelectionDescription[]; + extern const char kEnablePerfettoSystemTracingName[]; + extern const char kEnablePerfettoSystemTracingDescription[]; + ++extern const char kResumeBackgroundVideoName[]; ++extern const char kResumeBackgroundVideoDescription[]; ++ + extern const char kEnablePeripheralCustomizationName[]; + extern const char kEnablePeripheralCustomizationDescription[]; + +diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc +index bb277d181b482..2b16d6450bbcf 100644 +--- a/media/base/media_switches.cc ++++ b/media/base/media_switches.cc +@@ -353,12 +353,8 @@ BASE_FEATURE(kPreloadMetadataLazyLoad, + // Let videos be resumed via remote controls (for example, the notification) + // when in background. + BASE_FEATURE(kResumeBackgroundVideo, +- "resume-background-video", +-#if BUILDFLAG(IS_ANDROID) +- base::FEATURE_ENABLED_BY_DEFAULT +-#else +- base::FEATURE_DISABLED_BY_DEFAULT +-#endif ++ "resume-background-video", // disabled by default ++ base::FEATURE_DISABLED_BY_DEFAULT // in Bromite + ); + + // When enabled, MediaCapabilities will check with GPU Video Accelerator +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-to-disable-IPv6-probes.patch b/build/bromite_patches/Add-flag-to-disable-IPv6-probes.patch new file mode 100644 index 0000000000000000000000000000000000000000..04b3b38046208971c044e895c6633325d14634ee --- /dev/null +++ b/build/bromite_patches/Add-flag-to-disable-IPv6-probes.patch @@ -0,0 +1,170 @@ +From c53479d9c03c31df8167cb9af0da335c2f08beb5 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sun, 18 Nov 2018 13:06:49 +0100 +Subject: [PATCH 074/192] Add flag to disable IPv6 probes + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 4 ++++ + chrome/browser/flag_descriptions.cc | 4 ++++ + chrome/browser/flag_descriptions.h | 3 +++ + net/BUILD.gn | 1 + + net/base/features.cc | 4 ++++ + net/base/features.h | 3 +++ + net/dns/host_resolver_manager.cc | 11 +++++++++++ + services/network/public/cpp/features.cc | 5 +++++ + services/network/public/cpp/features.h | 1 + + 9 files changed, 36 insertions(+) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index de0c0efdddf7d..53968d336c2cd 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -6581,6 +6581,10 @@ const FeatureEntry kFeatureEntries[] = { + {"enable-reader-mode-in-cct", flag_descriptions::kReaderModeInCCTName, + flag_descriptions::kReaderModeInCCTDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kReaderModeInCCT)}, ++ {"ipv6-probing", ++ flag_descriptions::kIPv6ProbingName, ++ flag_descriptions::kIPv6ProbingDescription, kOsAll, ++ FEATURE_VALUE_TYPE(net::features::kIPv6Probing)}, + #endif // BUILDFLAG(IS_ANDROID) + + {"shopping-collection", +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 09846683d9e1e..dc0270b5ac63d 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -3883,6 +3883,10 @@ const char kCCTTextFragmentLookupApiEnabledName[] = + const char kCCTTextFragmentLookupApiEnabledDescription[] = + "Enable CCT API to lookup text fragments"; + ++const char kIPv6ProbingName[] = "Enable IPv6 probing."; ++const char kIPv6ProbingDescription[] = ++ "Send IPv6 probes to a RIPE DNS address to verify IPv6 connectivity."; ++ + const char kChimeAlwaysShowNotificationDescription[] = + "A debug flag to always show Chime notification after receiving a payload."; + const char kChimeAlwaysShowNotificationName[] = +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index fff998efaf7cd..65f03f17d0566 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -880,6 +880,9 @@ extern const char kEnableWasmStringrefName[]; + extern const char kEnableWasmStringrefDescription[]; + + extern const char kEnableWasmTieringName[]; ++extern const char kIPv6ProbingName[]; ++extern const char kIPv6ProbingDescription[]; ++ + extern const char kEnableWasmTieringDescription[]; + + extern const char kEvDetailsInPageInfoName[]; +diff --git a/net/BUILD.gn b/net/BUILD.gn +index a57b48653375f..8883c27b66b2a 100644 +--- a/net/BUILD.gn ++++ b/net/BUILD.gn +@@ -1094,6 +1094,7 @@ component("net") { + "//build:chromeos_buildflags", + "//net/data/ssl/chrome_root_store:gen_root_store_inc", + "//net/http:transport_security_state_generated_files", ++ "//components/network_session_configurator/common" + ] + + public_deps = [ +diff --git a/net/base/features.cc b/net/base/features.cc +index 83e2e83e4e41e..508e4dfdf52f1 100644 +--- a/net/base/features.cc ++++ b/net/base/features.cc +@@ -41,6 +41,10 @@ BASE_FEATURE(kUseDnsHttpsSvcb, + const base::FeatureParam kUseDnsHttpsSvcbEnforceSecureResponse{ + &kUseDnsHttpsSvcb, "UseDnsHttpsSvcbEnforceSecureResponse", false}; + ++BASE_FEATURE(kIPv6Probing, ++ "IPv6Probing", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + const base::FeatureParam kUseDnsHttpsSvcbInsecureExtraTimeMax{ + &kUseDnsHttpsSvcb, "UseDnsHttpsSvcbInsecureExtraTimeMax", + base::Milliseconds(50)}; +diff --git a/net/base/features.h b/net/base/features.h +index ffd83d9a3cef3..31e74ad0d6f2f 100644 +--- a/net/base/features.h ++++ b/net/base/features.h +@@ -52,6 +52,9 @@ NET_EXPORT BASE_DECLARE_FEATURE(kUseDnsHttpsSvcb); + NET_EXPORT extern const base::FeatureParam + kUseDnsHttpsSvcbEnforceSecureResponse; + ++// Enable IPv6 ping probes to RIPE DNS. ++NET_EXPORT BASE_DECLARE_FEATURE(kIPv6Probing); ++ + // If we are still waiting for an HTTPS transaction after all the + // other transactions in an insecure DnsTask have completed, we will compute a + // timeout for the remaining transaction. The timeout will be +diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc +index 6bac84e8ed049..e46d04ff04f4f 100644 +--- a/net/dns/host_resolver_manager.cc ++++ b/net/dns/host_resolver_manager.cc +@@ -110,6 +110,7 @@ + #include "net/log/net_log_event_type.h" + #include "net/log/net_log_source.h" + #include "net/log/net_log_source_type.h" ++#include "services/network/public/cpp/features.h" + #include "net/log/net_log_with_source.h" + #include "net/socket/client_socket_factory.h" + #include "net/url_request/url_request_context.h" +@@ -685,6 +686,9 @@ class HostResolverManager::RequestImpl + // request source is LOCAL_ONLY. This is due to LOCAL_ONLY requiring a + // synchronous response, so it cannot wait on an async probe result and + // cannot make assumptions about reachability. ++ if (!base::FeatureList::IsEnabled(features::kIPv6Probing)) { ++ return OK; ++ } + if (parameters_.source == HostResolverSource::LOCAL_ONLY) { + int rv = resolver_->StartIPv6ReachabilityCheck( + source_net_log_, GetClientSocketFactory(), +@@ -3903,6 +3907,13 @@ int HostResolverManager::StartIPv6ReachabilityCheck( + return OK; + } + ++ if (!base::FeatureList::IsEnabled(net::features::kIPv6Probing)) { ++ probing_ipv6_ = false; ++ last_ipv6_probe_result_ = false; ++ last_ipv6_probe_time_ = base::TimeTicks(); ++ return OK; ++ } ++ + if (probing_ipv6_) { + ipv6_request_callbacks_.push_back(std::move(callback)); + return ERR_IO_PENDING; +diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc +index 7bbe56c972ea8..e743370a28421 100644 +--- a/services/network/public/cpp/features.cc ++++ b/services/network/public/cpp/features.cc +@@ -31,6 +31,11 @@ BASE_FEATURE(kThrottleDelayable, + "ThrottleDelayable", + base::FEATURE_ENABLED_BY_DEFAULT); + ++// Enable IPv6 ping probes to RIPE DNS. ++BASE_FEATURE(kIPv6Probing, ++ "IPv6Probing", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + // When kPriorityRequestsDelayableOnSlowConnections is enabled, HTTP + // requests fetched from a SPDY/QUIC/H2 proxies can be delayed by the + // ResourceScheduler just as HTTP/1.1 resources are. However, requests from such +diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h +index ec6703eadc6ca..a24fd57094b72 100644 +--- a/services/network/public/cpp/features.h ++++ b/services/network/public/cpp/features.h +@@ -17,6 +17,7 @@ namespace features { + COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kNetworkErrorLogging); + COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kReporting); + COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kThrottleDelayable); ++COMPONENT_EXPORT(NETWORK_CPP) BASE_DECLARE_FEATURE(kIPv6Probing); + COMPONENT_EXPORT(NETWORK_CPP) + BASE_DECLARE_FEATURE(kDelayRequestsOnMultiplexedConnections); + COMPONENT_EXPORT(NETWORK_CPP) +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-to-disable-external-intent-requests.patch b/build/bromite_patches/Add-flag-to-disable-external-intent-requests.patch new file mode 100644 index 0000000000000000000000000000000000000000..671e4ff335d2247e6f9966de88678a96c8277530 --- /dev/null +++ b/build/bromite_patches/Add-flag-to-disable-external-intent-requests.patch @@ -0,0 +1,65 @@ +From 2d095a3f4867f4ed1a77f895af42419ff17bc3b5 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Tue, 25 May 2021 19:46:14 +0200 +Subject: [PATCH 134/192] Add flag to disable external intent requests + +Adds a new flag that allows to control the switch with same name; +when flag is disabled no external intent will ever be allowed, for any URL. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 5 +++++ + chrome/browser/flag_descriptions.cc | 6 ++++++ + chrome/browser/flag_descriptions.h | 3 +++ + 3 files changed, 14 insertions(+) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 238830494377d..f56710802c16a 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -3881,6 +3881,11 @@ const FeatureEntry kFeatureEntries[] = { + {"ui-disable-partial-swap", flag_descriptions::kUiPartialSwapName, + flag_descriptions::kUiPartialSwapDescription, kOsAll, + SINGLE_DISABLE_VALUE_TYPE(switches::kUIDisablePartialSwap)}, ++#if BUILDFLAG(IS_ANDROID) ++ {"allow-external-intent-requests", flag_descriptions::kDisableExternalIntentRequestsName, ++ flag_descriptions::kDisableExternalIntentRequestsDescription, kOsAll, ++ SINGLE_DISABLE_VALUE_TYPE("disable-external-intent-requests")}, ++#endif + {"disable-webrtc-hw-decoding", flag_descriptions::kWebrtcHwDecodingName, + flag_descriptions::kWebrtcHwDecodingDescription, kOsAndroid | kOsCrOS, + SINGLE_DISABLE_VALUE_TYPE(switches::kDisableWebRtcHWDecoding)}, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 1a7fef357fdf0..8deefef5e23a4 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -4184,6 +4184,12 @@ const char kPolicyLogsPageAndroidDescription[] = + "Enable the new chrome://policy-logs page containing logs for debugging " + "policy related issues on Android."; + ++const char kDisableExternalIntentRequestsName[] = ++ "Allow forward URL requests to external intents"; ++const char kDisableExternalIntentRequestsDescription[] = ++ "If disabled, URL requests will never" ++ "allow for redirecting to an external intent."; ++ + const char kQueryTilesName[] = "Show query tiles"; + const char kQueryTilesDescription[] = "Shows query tiles in Chrome"; + const char kQueryTilesNTPName[] = "Show query tiles in NTP"; +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index ebc797abde0fc..978a9c4dcd81c 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -2467,6 +2467,9 @@ extern const char kQueryTilesNTPDescription[]; + extern const char kQueryTilesOnStartName[]; + extern const char kQueryTilesOnStartDescription[]; + extern const char kQueryTilesSingleTierName[]; ++extern const char kDisableExternalIntentRequestsName[]; ++extern const char kDisableExternalIntentRequestsDescription[]; ++ + extern const char kQueryTilesSingleTierDescription[]; + extern const char kQueryTilesEnableQueryEditingName[]; + extern const char kQueryTilesEnableQueryEditingDescription[]; +-- +2.34.1 + diff --git a/build/bromite_patches/Add-flag-to-disable-vibration.patch b/build/bromite_patches/Add-flag-to-disable-vibration.patch new file mode 100644 index 0000000000000000000000000000000000000000..645d1d674648188d8a934084f094998a541d0a62 --- /dev/null +++ b/build/bromite_patches/Add-flag-to-disable-vibration.patch @@ -0,0 +1,168 @@ +From 5f15917126eeded5dc07c69d5cedfaff35455d1b Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sun, 27 Jun 2021 17:35:39 +0200 +Subject: [PATCH 137/192] Add flag to disable vibration + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 4 ++++ + chrome/browser/flag_descriptions.cc | 3 +++ + chrome/browser/flag_descriptions.h | 2 ++ + content/child/runtime_features.cc | 1 + + content/public/common/content_features.cc | 5 +++++ + content/public/common/content_features.h | 2 ++ + third_party/blink/public/platform/web_runtime_features.h | 1 + + .../blink/renderer/modules/vibration/vibration_controller.cc | 3 +++ + .../blink/renderer/platform/exported/web_runtime_features.cc | 4 ++++ + .../blink/renderer/platform/runtime_enabled_features.json5 | 4 ++++ + 10 files changed, 29 insertions(+) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index adaf8a1350291..feaa4584c8a98 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -8115,6 +8115,10 @@ const FeatureEntry kFeatureEntries[] = { + FEATURE_VALUE_TYPE(features::kNewMacNotificationAPI)}, + #endif + ++ {"enable-vibration", flag_descriptions::kEnableVibrationName, ++ flag_descriptions::kEnableVibrationDescription, kOsAll, ++ FEATURE_VALUE_TYPE(features::kVibration)}, ++ + #if BUILDFLAG(IS_CHROMEOS_ASH) + {"exo-consumed-by-ime-by-flag", + flag_descriptions::kExoConsumedByImeByFlagName, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index a0754d0ed5723..4fd26779df83e 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -7123,6 +7123,9 @@ const char kLacrosMergeIcuDataFileDescription[] = + "Enables sharing common areas of icudtl.dat between Ash and Lacros."; + #endif // #if BUILDFLAG(IS_CHROMEOS_LACROS) + ++const char kEnableVibrationName[] = "Vibration"; ++const char kEnableVibrationDescription[] = "Enable vibration API; an user gesture will still be needed."; ++ + #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) + const char kGetAllScreensMediaName[] = "GetAllScreensMedia API"; + const char kGetAllScreensMediaDescription[] = +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 4bc4ddb5ce607..e21404e9bc26f 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -4341,6 +4341,8 @@ extern const char kWebUITabStripTabDragIntegrationName[]; + extern const char kWebUITabStripTabDragIntegrationDescription[]; + #endif // BUILDFLAG(ENABLE_WEBUI_TAB_STRIP) && BUILDFLAG(IS_CHROMEOS_ASH) + ++extern const char kEnableVibrationName[]; ++extern const char kEnableVibrationDescription[]; + #if defined(TOOLKIT_VIEWS) || BUILDFLAG(IS_ANDROID) + + extern const char kAutofillCreditCardUploadName[]; +diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc +index bf469ba421e52..66fc5dbfbbc8b 100644 +--- a/content/child/runtime_features.cc ++++ b/content/child/runtime_features.cc +@@ -101,6 +101,7 @@ void SetRuntimeFeatureDefaultsForPlatform( + if (command_line.HasSwitch(switches::kDisableMediaSessionAPI)) { + WebRuntimeFeatures::EnableMediaSession(false); + } ++ WebRuntimeFeatures::EnableVibration(base::FeatureList::IsEnabled(features::kVibration)); + #endif + + #if BUILDFLAG(IS_ANDROID) +diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc +index 8c354153db71c..6440081a1eed0 100644 +--- a/content/public/common/content_features.cc ++++ b/content/public/common/content_features.cc +@@ -169,6 +169,11 @@ BASE_FEATURE(kBackForwardCacheMemoryControls, + #endif + ); + ++// Enables vibration; an user gesture will still be required if enabled. ++BASE_FEATURE(kVibration, ++ "VibrationEnabled", ++ base::FEATURE_DISABLED_BY_DEFAULT); ++ + // When this feature is enabled, private network requests initiated from + // non-secure contexts in the `public` address space are blocked. + // +diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h +index 01ce3746b5f15..6f9ef9a746c6e 100644 +--- a/content/public/common/content_features.h ++++ b/content/public/common/content_features.h +@@ -348,6 +348,8 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kWarmUpNetworkProcess); + CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebNfc); + + #endif // BUILDFLAG(IS_ANDROID) ++CONTENT_EXPORT BASE_DECLARE_FEATURE(kVibration); ++ + + #if BUILDFLAG(IS_MAC) + CONTENT_EXPORT BASE_DECLARE_FEATURE(kDeviceMonitorMac); +diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h +index 23e1afbedeb56..141981145abeb 100644 +--- a/third_party/blink/public/platform/web_runtime_features.h ++++ b/third_party/blink/public/platform/web_runtime_features.h +@@ -70,6 +70,7 @@ class BLINK_PLATFORM_EXPORT WebRuntimeFeatures : public WebRuntimeFeaturesBase { + static void EnableFingerprintingCanvasMeasureTextNoise(bool); + static void EnableFingerprintingCanvasImageDataNoise(bool); + static void EnableFluentScrollbars(bool); ++ static void EnableVibration(bool); + + WebRuntimeFeatures() = delete; + }; +diff --git a/third_party/blink/renderer/modules/vibration/vibration_controller.cc b/third_party/blink/renderer/modules/vibration/vibration_controller.cc +index f8e6a5495a14d..64e8b9eb0587b 100644 +--- a/third_party/blink/renderer/modules/vibration/vibration_controller.cc ++++ b/third_party/blink/renderer/modules/vibration/vibration_controller.cc +@@ -29,6 +29,7 @@ + #include "third_party/blink/renderer/core/frame/local_frame.h" + #include "third_party/blink/renderer/core/frame/navigator.h" + #include "third_party/blink/renderer/core/page/page.h" ++#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + + // Maximum number of entries in a vibration pattern. + const unsigned kVibrationPatternLengthMax = 99; +@@ -152,6 +153,8 @@ bool VibrationController::vibrate(Navigator& navigator, + // reference to |window| or |navigator| was retained in another window. + if (!navigator.DomWindow()) + return false; ++ if (!RuntimeEnabledFeatures::VibrationEnabled()) ++ return false; + return From(navigator).Vibrate(pattern); + } + +diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc +index 69af1a7b9f0be..b3e720a08ea8f 100644 +--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc ++++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc +@@ -52,6 +52,10 @@ void WebRuntimeFeatures::EnableTestOnlyFeatures(bool enable) { + RuntimeEnabledFeatures::SetTestFeaturesEnabled(enable); + } + ++void WebRuntimeFeatures::EnableVibration(bool enable) { ++ RuntimeEnabledFeatures::SetVibrationEnabled(enable); ++} ++ + void WebRuntimeFeatures::EnableOriginTrialControlledFeatures(bool enable) { + RuntimeEnabledFeatures::SetOriginTrialControlledFeaturesEnabled(enable); + } +diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 +index f5d8699b53384..bfcca5ab88e88 100644 +--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 ++++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5 +@@ -2461,6 +2461,10 @@ + status: {"Android": "stable"}, + base_feature: "none", + }, ++ { ++ name: "Vibration", ++ status: "stable", ++ }, + { + name: "OriginIsolationHeader", + status: "stable", +-- +2.34.1 + diff --git a/build/bromite_patches/Add-lifetime-options-for-permissions.patch b/build/bromite_patches/Add-lifetime-options-for-permissions.patch new file mode 100644 index 0000000000000000000000000000000000000000..28f7e364da1bbbdf5c98f54d72cd1e11a4ac2c6b --- /dev/null +++ b/build/bromite_patches/Add-lifetime-options-for-permissions.patch @@ -0,0 +1,1486 @@ +From 2ab682f0b3b3122ac50953aef842a13e11ea5cba Mon Sep 17 00:00:00 2001 +From: uazo +Date: Fri, 8 Apr 2022 11:04:04 +0000 +Subject: [PATCH 164/192] Add lifetime options for permissions + +Indicate the session mode for content-settings by using the constraint `content_settings::SessionModel` as +UserSession when setting the value, and also make use of an expiration time value. +This is used in Chromium for `ClientHints` but it is generally possible to use this functionality when a +specific value needs to be persisted by origin. +All content settings of this type are not saved on disk (except for the `Forever` option), allowing user to +reset the status each time application is restarted. + +There are 4 main areas affected to introduce the functionality: +* components/content_settings + A new `content_settings::LifetimeMode` enum value is defined to specify the user's + choice (Always, OnlyThisTime, UntilOriginClosed, UntilBrowserClosed). + Enumeration is also generated for java by adding it in `content_settings_enums_javagen` (gn). + This is mainly used in `content_settings_utils.cc` to create a specialised `content_settings::ContentSettingConstraints` + that is then used in `SetContentSettingDefaultScope()` by `PermissionContextBase::UpdateContentSetting`. + Existing Chromium data structures do not provide a specific property to define a choice which is instead encoded through + the `ContentSettingConstraints`; this approach is already used in other parts of the Chromium codebase so it is not + novel here. + Therefore, `content_settings::GetConstraintSessionExpiration()` and `content_settings::IsConstraintSessionExpiration()` + manage the lifetime modes of the session content-settings. + The modificaiton also adds the session pattern to the ContentSettingPatternSource so that it is available for the UI. +* components/permissions + Lifetime support is added to the permissions; most of the changes are caused by the fact that it is necessary to report + the value selected by the user from the Java UI managed by `components/browser_ui` up to + `PermissionContextBase::UpdateContentSetting()`, without necessarily having to modify all requests that are not + related to geolocation/camera/microphone. The approach used is a new + `PermissionRequest::PermissionDecidedCallbackWithLifetime` used by an overload of + `PermissionContextBase::CreatePermissionRequest` so that options are present only for the specific content-settings + (see `PermissionDialogModel.java`). + For other permissions no behaviour is changed (see `PermissionDialogDelegate::Accept`); for geolocation it was + necessary to act directly in the specific context, because, unlike microphone/camera, the content-setting value is + inserted in its specific method (`FinishNotifyPermissionSet`, that calls the callback), even if the class always + derives from `PermissionContextBase`. +* components/page_info + Some changes needed to see in the summary of the `page_info` the text "(only this session)" + (aka `page_info_android_permission_session_permission`) through adding a new property "is_user_session" in + `PageInfoPermissionEntry` (Java). +* components/browser_ui + Changes to the Settings UI to show "(only this session)" in the specific content-setting. + The same view is used both in the settings and in the page_info. + +For the management of `UntilOriginClosed` the logic used by flag `kOneTimeGeolocationPermission` was used; this flag +is active only in the desktop (files `last_tab_standing_tracker_*`). It is a class that manages a list of the active +origins and allows to perform operations when all the tabs relating to that origin have been closed, in this case +deleting the session content settings of `UntilOriginClosed`. + +See also: https://github.com/bromite/bromite/issues/1549 + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../permissions/last_tab_standing_tracker.cc | 32 ++++++++ + .../one_time_permissions_tracker.cc | 33 +++++++- + .../one_time_permissions_tracker.h | 5 +- + .../one_time_permissions_tracker_factory.cc | 2 +- + .../permission_prompt_bubble_base_view.cc | 2 +- + .../site_settings/PermissionInfo.java | 14 +++- + .../site_settings/SingleWebsiteSettings.java | 10 +++ + .../WebsitePreferenceBridge.java | 6 +- + .../android/website_preference_bridge.cc | 7 +- + .../strings/android/browser_ui_strings.grd | 5 ++ + components/content_settings/android/BUILD.gn | 1 + + .../core/browser/content_settings_utils.cc | 36 +++++++++ + .../core/browser/content_settings_utils.h | 6 ++ + .../common/content_settings_constraints.h | 11 +++ + .../page_info/PageInfoController.java | 4 +- + .../PermissionParamsListBuilder.java | 13 ++- + .../android/page_info_controller_android.cc | 10 ++- + components/page_info/page_info.cc | 2 + + components/page_info/page_info.h | 1 + + .../permissions/PermissionDialogDelegate.java | 13 +++ + .../permissions/PermissionDialogModel.java | 81 ++++++++++++++++++- + .../permission_dialog_delegate.cc | 19 +++++ + .../permission_dialog_delegate.h | 1 + + .../permission_prompt_android.cc | 8 ++ + .../permission_prompt_android.h | 2 + + .../android/permissions_android_strings.grd | 17 ++++ + .../geolocation_permission_context_android.cc | 35 ++++++-- + .../geolocation_permission_context_android.h | 14 +++- + .../permissions/permission_context_base.cc | 55 +++++++++++-- + .../permissions/permission_context_base.h | 25 +++++- + components/permissions/permission_prompt.h | 3 +- + components/permissions/permission_request.cc | 34 +++++++- + components/permissions/permission_request.h | 18 ++++- + .../permissions/permission_request_manager.cc | 45 +++++++---- + .../permissions/permission_request_manager.h | 11 ++- + 35 files changed, 521 insertions(+), 60 deletions(-) + +diff --git a/chrome/browser/permissions/last_tab_standing_tracker.cc b/chrome/browser/permissions/last_tab_standing_tracker.cc +index 5df1981731e0e..247155065ce68 100644 +--- a/chrome/browser/permissions/last_tab_standing_tracker.cc ++++ b/chrome/browser/permissions/last_tab_standing_tracker.cc +@@ -7,6 +7,32 @@ + #include "base/observer_list.h" + #include "url/gurl.h" + ++#include "components/content_settings/core/browser/host_content_settings_map.h" ++#include "components/content_settings/core/common/content_settings_utils.h" ++#include "components/permissions/permissions_client.h" ++ ++namespace { ++ // Remove all sessions content setting by origin and type ++ void RemoveSessionSettings(HostContentSettingsMap* content_settings, ++ const url::Origin& origin, ++ ContentSettingsType type) { ++ ContentSettingsForOneType session_settings = ++ content_settings->GetSettingsForOneType( ++ type, content_settings::SessionModel::UserSession); ++ ++ GURL url = origin.GetURL(); ++ for (ContentSettingPatternSource& entry : session_settings) { ++ if (content_settings::IsConstraintSessionExpiration(entry, ++ content_settings::LifetimeMode::UntilOriginClosed) && ++ entry.primary_pattern.Matches(url)) { ++ content_settings->SetWebsiteSettingCustomScope( ++ entry.primary_pattern, entry.secondary_pattern, ++ type, base::Value()); ++ } ++ } ++ } ++} ++ + LastTabStandingTracker::LastTabStandingTracker(content::BrowserContext* context) + : context_(context) {} + +@@ -56,4 +82,10 @@ void LastTabStandingTracker::WebContentsUnloadedOrigin( + for (auto& observer : observer_list_) { + observer.OnLastPageFromOriginClosed(origin); + } ++ HostContentSettingsMap* content_settings = ++ permissions::PermissionsClient::Get()->GetSettingsMap(context_); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::GEOLOCATION); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::MEDIASTREAM_MIC); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::MEDIASTREAM_CAMERA); ++ } + } +diff --git a/chrome/browser/permissions/one_time_permissions_tracker.cc b/chrome/browser/permissions/one_time_permissions_tracker.cc +index f400a95c022fa..31354261bca8a 100644 +--- a/chrome/browser/permissions/one_time_permissions_tracker.cc ++++ b/chrome/browser/permissions/one_time_permissions_tracker.cc +@@ -18,8 +18,34 @@ + #include "components/permissions/features.h" + #include "content/public/browser/visibility.h" + #include "url/gurl.h" ++#include "components/content_settings/core/browser/host_content_settings_map.h" ++#include "components/content_settings/core/common/content_settings_utils.h" ++#include "components/permissions/permissions_client.h" ++ ++namespace { ++ // Remove all sessions content setting by origin and type ++ void RemoveSessionSettings(HostContentSettingsMap* content_settings, ++ const url::Origin& origin, ++ ContentSettingsType type) { ++ ContentSettingsForOneType session_settings = ++ content_settings->GetSettingsForOneType( ++ type, content_settings::SessionModel::UserSession); ++ ++ GURL url = origin.GetURL(); ++ for (ContentSettingPatternSource& entry : session_settings) { ++ if (content_settings::IsConstraintSessionExpiration(entry, ++ content_settings::LifetimeMode::UntilOriginClosed) && ++ entry.primary_pattern.Matches(url)) { ++ content_settings->SetWebsiteSettingCustomScope( ++ entry.primary_pattern, entry.secondary_pattern, ++ type, base::Value()); ++ } ++ } ++ } ++} + +-OneTimePermissionsTracker::OneTimePermissionsTracker() = default; ++OneTimePermissionsTracker::OneTimePermissionsTracker(content::BrowserContext* context) ++ : context_(context) {} + OneTimePermissionsTracker::~OneTimePermissionsTracker() = default; + + OneTimePermissionsTracker::OriginTrackEntry::OriginTrackEntry() = default; +@@ -93,6 +119,11 @@ void OneTimePermissionsTracker::WebContentsUnloadedOrigin( + observer.OnLastPageFromOriginClosed(origin); + } + } ++ HostContentSettingsMap* content_settings = ++ permissions::PermissionsClient::Get()->GetSettingsMap(context_); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::GEOLOCATION); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::MEDIASTREAM_MIC); ++ RemoveSessionSettings(content_settings, origin, ContentSettingsType::MEDIASTREAM_CAMERA); + } + } + +diff --git a/chrome/browser/permissions/one_time_permissions_tracker.h b/chrome/browser/permissions/one_time_permissions_tracker.h +index 168f8e1344c2e..e223b3e6a8244 100644 +--- a/chrome/browser/permissions/one_time_permissions_tracker.h ++++ b/chrome/browser/permissions/one_time_permissions_tracker.h +@@ -12,6 +12,7 @@ + #include "base/timer/timer.h" + #include "chrome/browser/permissions/one_time_permissions_tracker_observer.h" + #include "components/content_settings/core/common/content_settings_pattern.h" ++#include "chrome/browser/profiles/profile.h" + #include "components/content_settings/core/common/content_settings_types.h" + #include "components/keyed_service/core/keyed_service.h" + #include "content/public/browser/visibility.h" +@@ -24,7 +25,7 @@ class OneTimePermissionsTracker : public KeyedService { + void (OneTimePermissionsTracker::*)(const url::Origin&); + + public: +- OneTimePermissionsTracker(); ++ OneTimePermissionsTracker(content::BrowserContext* context); + ~OneTimePermissionsTracker() override; + + OneTimePermissionsTracker(const OneTimePermissionsTracker&) = delete; +@@ -127,7 +128,7 @@ class OneTimePermissionsTracker : public KeyedService { + base::ObserverList observer_list_; + + std::map origin_tracker_; +- ++ raw_ptr context_; + base::WeakPtrFactory weak_factory_{this}; + }; + +diff --git a/chrome/browser/permissions/one_time_permissions_tracker_factory.cc b/chrome/browser/permissions/one_time_permissions_tracker_factory.cc +index cab98b40bf1de..30b1b56f87f75 100644 +--- a/chrome/browser/permissions/one_time_permissions_tracker_factory.cc ++++ b/chrome/browser/permissions/one_time_permissions_tracker_factory.cc +@@ -40,5 +40,5 @@ bool OneTimePermissionsTrackerFactory::ServiceIsCreatedWithBrowserContext() + + KeyedService* OneTimePermissionsTrackerFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { +- return new OneTimePermissionsTracker(); ++ return new OneTimePermissionsTracker(context); + } +diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc +index d64456f3a7681..c220b0448f287 100644 +--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc ++++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.cc +@@ -336,7 +336,7 @@ void PermissionPromptBubbleBaseView::RunButtonCallbacks( + delegate_->Accept(); + return; + case PermissionDialogButton::kAcceptOnce: +- delegate_->AcceptThisTime(); ++ delegate_->AcceptThisTime(content_settings::LifetimeMode::Always); + return; + case PermissionDialogButton::kDeny: + delegate_->Deny(); +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java +index b396373609a2b..c9500b68f5678 100644 +--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/PermissionInfo.java +@@ -9,6 +9,7 @@ import androidx.annotation.Nullable; + import org.chromium.components.content_settings.ContentSettingValues; + import org.chromium.components.content_settings.ContentSettingsType; + import org.chromium.content_public.browser.BrowserContextHandle; ++import org.chromium.components.content_settings.SessionModel; + + import java.io.Serializable; + +@@ -20,19 +21,30 @@ public class PermissionInfo implements Serializable { + private final String mEmbedder; + private final String mOrigin; + private final @ContentSettingsType int mContentSettingsType; ++ private final @SessionModel int mSessionModel; + + public PermissionInfo(@ContentSettingsType int type, String origin, String embedder) { + this(type, origin, embedder, false); + } + ++ public PermissionInfo(@ContentSettingsType int type, String origin, String embedder, boolean isEmbargoed) { ++ this(type, origin, embedder, isEmbargoed, 0); ++ } ++ + public PermissionInfo( +- @ContentSettingsType int type, String origin, String embedder, boolean isEmbargoed) { ++ @ContentSettingsType int type, String origin, String embedder, boolean isEmbargoed, ++ @SessionModel int sessionModel) { + assert WebsitePermissionsFetcher.getPermissionsType(type) + == WebsitePermissionsFetcher.WebsitePermissionsType.PERMISSION_INFO; + mOrigin = origin; + mEmbedder = embedder; + mContentSettingsType = type; + mIsEmbargoed = isEmbargoed; ++ mSessionModel = sessionModel; ++ } ++ ++ public @SessionModel int getSessionModel() { ++ return mSessionModel; + } + + public @ContentSettingsType int getContentSettingsType() { +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java +index bca33587c1c7d..ae9bd5ad63ca9 100644 +--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java +@@ -38,6 +38,7 @@ import org.chromium.components.browser_ui.settings.TextMessagePreference; + import org.chromium.components.browsing_data.DeleteBrowsingDataAction; + import org.chromium.components.content_settings.ContentSettingValues; + import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.components.content_settings.SessionModel; + import org.chromium.components.embedder_support.util.Origin; + import org.chromium.content_public.browser.BrowserContextHandle; + +@@ -546,6 +547,11 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment + } + } + ++ private boolean isSessionPermission(@ContentSettingsType int type) { ++ return mSite.getPermissionInfo(type) != null && ++ mSite.getPermissionInfo(type).getSessionModel() == SessionModel.USER_SESSION; ++ } ++ + private void setUpClearDataPreference() { + ClearWebsiteStorage preference = findPreference(PREF_CLEAR_DATA); + long usage = mSite.getTotalUsage(); +@@ -975,6 +981,10 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment + if (contentType == mHighlightedPermission) { + switchPreference.setBackgroundColor(mHighlightColor); + } ++ if (isSessionPermission(contentType)) { ++ switchPreference.setSummary(switchPreference.getSummary() + " " + ++ getString(R.string.page_info_android_permission_session_permission)); ++ } + } + + /** +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java +index 5a076d74e90ee..56c555a49c34d 100644 +--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java +@@ -9,6 +9,7 @@ import org.chromium.base.annotations.CalledByNative; + import org.chromium.base.annotations.NativeMethods; + import org.chromium.components.content_settings.ContentSettingValues; + import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.components.content_settings.SessionModel; + import org.chromium.components.location.LocationUtils; + import org.chromium.content_public.browser.BrowserContextHandle; + import org.chromium.url.GURL; +@@ -55,7 +56,8 @@ public class WebsitePreferenceBridge { + + @CalledByNative + private static void insertPermissionInfoIntoList(@ContentSettingsType int type, +- ArrayList list, String origin, String embedder, boolean isEmbargoed) { ++ ArrayList list, String origin, String embedder, boolean isEmbargoed, ++ @SessionModel int sessionModel) { + if (type == ContentSettingsType.MEDIASTREAM_CAMERA + || type == ContentSettingsType.MEDIASTREAM_MIC) { + for (PermissionInfo info : list) { +@@ -64,7 +66,7 @@ public class WebsitePreferenceBridge { + } + } + } +- list.add(new PermissionInfo(type, origin, embedder, isEmbargoed)); ++ list.add(new PermissionInfo(type, origin, embedder, isEmbargoed, sessionModel)); + } + + @CalledByNative +diff --git a/components/browser_ui/site_settings/android/website_preference_bridge.cc b/components/browser_ui/site_settings/android/website_preference_bridge.cc +index 0fa4e79185eea..9f88a0b4a7a0c 100644 +--- a/components/browser_ui/site_settings/android/website_preference_bridge.cc ++++ b/components/browser_ui/site_settings/android/website_preference_bridge.cc +@@ -131,7 +131,8 @@ typedef void (*InfoListInsertionFunction)( + const base::android::JavaRef&, + const base::android::JavaRef&, + const base::android::JavaRef&, +- jboolean); ++ jboolean, ++ JniIntWrapper); + + void GetOrigins(JNIEnv* env, + const JavaParamRef& jbrowser_context_handle, +@@ -173,7 +174,7 @@ void GetOrigins(JNIEnv* env, + seen_origins.push_back(origin); + insertionFunc(env, static_cast(content_type), list, + ConvertOriginToJavaString(env, origin), jembedder, +- /*is_embargoed=*/false); ++ /*is_embargoed=*/false, static_cast(settings_it.metadata.session_model())); + } + + // Add any origins which have a default content setting value (thus skipped +@@ -195,7 +196,7 @@ void GetOrigins(JNIEnv* env, + seen_origins.push_back(origin); + insertionFunc(env, static_cast(content_type), list, + ConvertOriginToJavaString(env, origin), jembedder, +- /*is_embargoed=*/true); ++ /*is_embargoed=*/true, 0); + } + } + } +diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd +index becda89edd71c..c0ecc4f410d5e 100644 +--- a/components/browser_ui/strings/android/browser_ui_strings.grd ++++ b/components/browser_ui/strings/android/browser_ui_strings.grd +@@ -642,6 +642,11 @@ + + URL truncated + ++ ++ (only this session) ++ ++ + + Ad personalization + +diff --git a/components/content_settings/android/BUILD.gn b/components/content_settings/android/BUILD.gn +index 1501e0fdf459f..f9ef7f7b0b4b7 100644 +--- a/components/content_settings/android/BUILD.gn ++++ b/components/content_settings/android/BUILD.gn +@@ -60,6 +60,7 @@ java_cpp_enum("content_settings_enums_javagen") { + "../core/common/cookie_controls_breakage_confidence_level.h", + "../core/common/cookie_controls_enforcement.h", + "../core/common/cookie_controls_status.h", ++ "../core/common/content_settings_constraints.h", + ] + visibility = [ ":*" ] # Depend on through :content_settings_enums_java + } +diff --git a/components/content_settings/core/browser/content_settings_utils.cc b/components/content_settings/core/browser/content_settings_utils.cc +index ab48ce7c18779..6ddea11a0388f 100644 +--- a/components/content_settings/core/browser/content_settings_utils.cc ++++ b/components/content_settings/core/browser/content_settings_utils.cc +@@ -186,6 +186,42 @@ bool IsConstraintPersistent(const ContentSettingConstraints& constraints) { + return constraints.session_model() == SessionModel::Durable; + } + ++ContentSettingConstraints GetConstraintSessionExpiration(LifetimeMode lifetime_mode) { ++ int lifetime; ++ base::Time now; ++ if (lifetime_mode == LifetimeMode::OnlyThisTime) { ++ // note: this content settings will be discarded immediately ++ // 1h is used as a magic constant to identify the one-time lifetime mode ++ lifetime = 1; ++ } else if (lifetime_mode == LifetimeMode::UntilOriginClosed) { ++ now = base::Time::Now(); ++ lifetime = 24; ++ } else { ++ lifetime = 0; ++ } ++ content_settings::ContentSettingConstraints c(now); ++ c.set_lifetime(base::Hours(lifetime)); ++ c.set_session_model(content_settings::SessionModel::UserSession); ++ return c; ++} ++ ++bool IsConstraintSessionExpiration(const ContentSettingPatternSource& source, ++ LifetimeMode lifetime_mode) { ++ if (source.metadata.session_model() != content_settings::SessionModel::UserSession) ++ return false; ++ ++ LifetimeMode type; ++ if (source.metadata.lifetime() == base::Hours(24)) { ++ type = LifetimeMode::UntilOriginClosed; ++ } else if (source.metadata.expiration() == (base::Time() + base::Hours(1))) { ++ type = LifetimeMode::OnlyThisTime; ++ } else { ++ type = LifetimeMode::UntilBrowserClosed; ++ } ++ ++ return lifetime_mode == type; ++} ++ + bool CanTrackLastVisit(ContentSettingsType type) { + // Last visit is not tracked for notification permission as it shouldn't be + // auto-revoked. +diff --git a/components/content_settings/core/browser/content_settings_utils.h b/components/content_settings/core/browser/content_settings_utils.h +index 978fcb1b8f2ff..90db87a40c683 100644 +--- a/components/content_settings/core/browser/content_settings_utils.h ++++ b/components/content_settings/core/browser/content_settings_utils.h +@@ -73,6 +73,12 @@ bool IsConstraintPersistent(const ContentSettingConstraints& constraints); + // Returns whether the given type supports tracking last_visit timestamps. + bool CanTrackLastVisit(ContentSettingsType type); + ++ContentSettingConstraints GetConstraintSessionExpiration(LifetimeMode lifetime_mode); ++ ++bool IsConstraintSessionExpiration( ++ const ContentSettingPatternSource& source, ++ LifetimeMode lifetime_mode); ++ + // Get a timestamp with week-precision. + base::Time GetCoarseVisitedTime(base::Time time); + +diff --git a/components/content_settings/core/common/content_settings_constraints.h b/components/content_settings/core/common/content_settings_constraints.h +index dfb4fdf0f25be..e9b26eb99af07 100644 +--- a/components/content_settings/core/common/content_settings_constraints.h ++++ b/components/content_settings/core/common/content_settings_constraints.h +@@ -23,6 +23,8 @@ namespace content_settings { + // a crash or update related restart. + // OneTime: Settings will persist for the current "tab session", meaning + // until the last tab from the origin is closed. ++// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.content_settings ++// GENERATED_JAVA_CLASS_NAME_OVERRIDE: SessionModel + enum class SessionModel { + Durable = 0, + UserSession = 1, +@@ -31,6 +33,15 @@ enum class SessionModel { + kMaxValue = OneTime, + }; + ++// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.content_settings ++// GENERATED_JAVA_CLASS_NAME_OVERRIDE: LifetimeMode ++enum class LifetimeMode { ++ Always = 99, ++ OnlyThisTime = 1, ++ UntilOriginClosed = 2, ++ UntilBrowserClosed = 0, ++}; ++ + // Constraints to be applied when setting a content setting. + class ContentSettingConstraints { + public: +diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java +index 153dfc08bfc26..347ad127f07ef 100644 +--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java ++++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java +@@ -336,9 +336,9 @@ public class PageInfoController implements PageInfoMainController, ModalDialogPr + */ + @CalledByNative + private void addPermissionSection(String name, String nameMidSentence, int type, +- @ContentSettingValues int currentSettingValue) { ++ @ContentSettingValues int currentSettingValue, boolean is_user_session) { + mPermissionParamsListBuilder.addPermissionEntry( +- name, nameMidSentence, type, currentSettingValue); ++ name, nameMidSentence, type, currentSettingValue, is_user_session); + } + + /** +diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PermissionParamsListBuilder.java b/components/page_info/android/java/src/org/chromium/components/page_info/PermissionParamsListBuilder.java +index e2c68173872eb..c93058f16d59d 100644 +--- a/components/page_info/android/java/src/org/chromium/components/page_info/PermissionParamsListBuilder.java ++++ b/components/page_info/android/java/src/org/chromium/components/page_info/PermissionParamsListBuilder.java +@@ -44,8 +44,9 @@ public class PermissionParamsListBuilder { + } + + public void addPermissionEntry( +- String name, String nameMidSentence, int type, @ContentSettingValues int value) { +- mEntries.add(new PageInfoPermissionEntry(name, nameMidSentence, type, value)); ++ String name, String nameMidSentence, int type, @ContentSettingValues int value, ++ boolean is_user_session) { ++ mEntries.add(new PageInfoPermissionEntry(name, nameMidSentence, type, value, is_user_session)); + } + + public void clearPermissionEntries() { +@@ -86,6 +87,10 @@ public class PermissionParamsListBuilder { + permissionParams.warningTextResource = + R.string.page_info_android_permission_blocked; + } ++ if (permission.is_user_session) { ++ permissionParams.warningTextResource = ++ R.string.page_info_android_permission_session_permission; ++ } + } + } + +@@ -124,13 +129,15 @@ public class PermissionParamsListBuilder { + public final String nameMidSentence; + public final int type; + public final @ContentSettingValues int setting; ++ public final boolean is_user_session; + + PageInfoPermissionEntry( +- String name, String nameMidSentence, int type, @ContentSettingValues int setting) { ++ String name, String nameMidSentence, int type, @ContentSettingValues int setting, boolean is_user_session) { + this.name = name; + this.nameMidSentence = nameMidSentence; + this.type = type; + this.setting = setting; ++ this.is_user_session = is_user_session; + } + + @Override +diff --git a/components/page_info/android/page_info_controller_android.cc b/components/page_info/android/page_info_controller_android.cc +index 257104ea67a99..1c07407525d66 100644 +--- a/components/page_info/android/page_info_controller_android.cc ++++ b/components/page_info/android/page_info_controller_android.cc +@@ -158,6 +158,8 @@ void PageInfoControllerAndroid::SetPermissionInfo( + + std::map + user_specified_settings_to_display; ++ std::map ++ user_specified_settings_is_user_session; + + for (const auto& permission : permission_info_list) { + if (base::Contains(permissions_to_display, permission.type)) { +@@ -166,6 +168,8 @@ void PageInfoControllerAndroid::SetPermissionInfo( + if (setting_to_display) { + user_specified_settings_to_display[permission.type] = + *setting_to_display; ++ user_specified_settings_is_user_session[permission.type] = ++ permission.is_user_session; + } + } + } +@@ -182,7 +186,8 @@ void PageInfoControllerAndroid::SetPermissionInfo( + ConvertUTF16ToJavaString(env, setting_title), + ConvertUTF16ToJavaString(env, setting_title_mid_sentence), + static_cast(permission), +- static_cast(user_specified_settings_to_display[permission])); ++ static_cast(user_specified_settings_to_display[permission]), ++ user_specified_settings_is_user_session[permission]); + } + } + +@@ -195,7 +200,8 @@ void PageInfoControllerAndroid::SetPermissionInfo( + env, controller_jobject_, ConvertUTF16ToJavaString(env, object_title), + ConvertUTF16ToJavaString(env, object_title), + static_cast(chosen_object->ui_info->content_settings_type), +- static_cast(CONTENT_SETTING_ALLOW)); ++ static_cast(CONTENT_SETTING_ALLOW), ++ /* is_user_session */ false); + } + + Java_PageInfoController_updatePermissionDisplay(env, controller_jobject_); +diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc +index 0be5287fa5328..282aa89f992b5 100644 +--- a/components/page_info/page_info.cc ++++ b/components/page_info/page_info.cc +@@ -1172,6 +1172,8 @@ void PageInfo::PopulatePermissionInfo(PermissionInfo& permission_info, + permission_info.source = info.source; + permission_info.is_one_time = (info.metadata.session_model() == + content_settings::SessionModel::OneTime); ++ permission_info.is_user_session = ++ (info.metadata.session_model() == content_settings::SessionModel::UserSession); + + auto* page_specific_content_settings = GetPageSpecificContentSettings(); + if (page_specific_content_settings && setting == CONTENT_SETTING_ALLOW) { +diff --git a/components/page_info/page_info.h b/components/page_info/page_info.h +index 2a10d89b46a4d..8925c7ba766dd 100644 +--- a/components/page_info/page_info.h ++++ b/components/page_info/page_info.h +@@ -199,6 +199,7 @@ class PageInfo : private content_settings::CookieControlsObserver, + content_settings::SETTING_SOURCE_NONE; + // Whether the permission is a one-time grant. + bool is_one_time = false; ++ bool is_user_session = false; + // Only set for settings that can have multiple permissions for different + // embedded origins. + absl::optional requesting_origin; +diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogDelegate.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogDelegate.java +index ad45ed899c63f..b4fc982b5b57d 100644 +--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogDelegate.java ++++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogDelegate.java +@@ -8,6 +8,7 @@ import org.chromium.base.annotations.CalledByNative; + import org.chromium.base.annotations.JNINamespace; + import org.chromium.base.annotations.NativeMethods; + import org.chromium.ui.base.WindowAndroid; ++import org.chromium.components.content_settings.LifetimeMode; + + /** + * Delegate class for modal permission dialogs. Contains all of the data displayed in a prompt, +@@ -43,6 +44,9 @@ public class PermissionDialogDelegate { + /** The {@link ContentSettingsType}s requested in this dialog. */ + private int[] mContentSettingsTypes; + ++ /** Lifetime option selected by the user. */ ++ private int mSelectedLifetimeOption = LifetimeMode.ALWAYS; ++ + public WindowAndroid getWindow() { + return mWindow; + } +@@ -72,6 +76,15 @@ public class PermissionDialogDelegate { + PermissionDialogDelegateJni.get().accept(mNativeDelegatePtr, PermissionDialogDelegate.this); + } + ++ public void setSelectedLifetimeOption(int idx) { ++ mSelectedLifetimeOption = idx; ++ } ++ ++ @CalledByNative ++ public int getSelectedLifetimeOption() { ++ return mSelectedLifetimeOption; ++ } ++ + public void onCancel() { + assert mNativeDelegatePtr != 0; + PermissionDialogDelegateJni.get().cancel(mNativeDelegatePtr, PermissionDialogDelegate.this); +diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogModel.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogModel.java +index 9b67228efb01d..ef57c286a5dec 100644 +--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogModel.java ++++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogModel.java +@@ -16,6 +16,17 @@ import org.chromium.ui.UiUtils; + import org.chromium.ui.modaldialog.ModalDialogProperties; + import org.chromium.ui.modelutil.PropertyModel; + ++import java.util.Arrays; ++import java.util.List; ++import android.view.ViewGroup.LayoutParams; ++import android.widget.LinearLayout; ++import android.widget.RadioButton; ++import android.widget.RadioGroup; ++import org.chromium.base.ApiCompatibilityUtils; ++import org.chromium.ui.base.ViewUtils; ++import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.components.content_settings.LifetimeMode; ++ + /** + * This class creates the model for permission dialog. + */ +@@ -34,7 +45,7 @@ class PermissionDialogModel { + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds( + messageTextView, delegate.getDrawableId(), 0, 0, 0); + +- return new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) ++ PropertyModel pm = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) + .with(ModalDialogProperties.CONTROLLER, controller) + .with(ModalDialogProperties.CUSTOM_VIEW, customView) + .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, delegate.getPrimaryButtonText()) +@@ -45,6 +56,74 @@ class PermissionDialogModel { + .with(ModalDialogProperties.BUTTON_TAP_PROTECTION_PERIOD_MS, + UiUtils.PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS) + .build(); ++ ++ int[] types = delegate.getContentSettingsTypes(); ++ if (contains(types, ContentSettingsType.GEOLOCATION) || ++ contains(types, ContentSettingsType.MEDIASTREAM_MIC) || ++ contains(types, ContentSettingsType.MEDIASTREAM_CAMERA)) ++ { ++ LinearLayout layout = (LinearLayout) customView; ++ ++ // Create a text label before the lifetime selector. ++ TextView lifetimeOptionsText = new TextView(context); ++ lifetimeOptionsText.setText(context.getString( ++ org.chromium.components.permissions.R.string.session_permissions_title)); ++ ApiCompatibilityUtils.setTextAppearance( ++ lifetimeOptionsText, R.style.TextAppearance_TextMedium_Primary); ++ ++ LinearLayout.LayoutParams lifetimeOptionsTextLayoutParams = ++ new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); ++ lifetimeOptionsTextLayoutParams.setMargins(0, 0, 0, ViewUtils.dpToPx(context, 8)); ++ lifetimeOptionsText.setLayoutParams(lifetimeOptionsTextLayoutParams); ++ layout.addView(lifetimeOptionsText); ++ ++ // Create radio buttons with lifetime options. ++ RadioGroup radioGroup = new RadioGroup(context); ++ ++ RadioButton radioButon = new RadioButton(context); ++ radioButon.setText(context.getString( ++ org.chromium.components.permissions.R.string.session_permissions_only_this_this)); ++ radioButon.setId(LifetimeMode.ONLY_THIS_TIME); ++ radioGroup.addView(radioButon); ++ ++ radioButon = new RadioButton(context); ++ radioButon.setText(context.getString( ++ org.chromium.components.permissions.R.string.session_permissions_until_page_close)); ++ radioButon.setId(LifetimeMode.UNTIL_ORIGIN_CLOSED); ++ radioGroup.addView(radioButon); ++ ++ radioButon = new RadioButton(context); ++ radioButon.setText(context.getString( ++ org.chromium.components.permissions.R.string.session_permissions_until_browser_close)); ++ radioButon.setId(LifetimeMode.UNTIL_BROWSER_CLOSED); ++ radioGroup.addView(radioButon); ++ ++ radioButon = new RadioButton(context); ++ radioButon.setText(context.getString( ++ org.chromium.components.permissions.R.string.session_permissions_forever)); ++ radioButon.setId(LifetimeMode.ALWAYS); ++ radioGroup.addView(radioButon); ++ ++ radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { ++ @Override ++ public void onCheckedChanged(RadioGroup group, int checkedId) { ++ delegate.setSelectedLifetimeOption(checkedId); ++ } ++ }); ++ radioGroup.check(1); ++ layout.addView(radioGroup); ++ } ++ ++ return pm; ++ } ++ ++ private static boolean contains(final int[] array, final int key) { ++ int length = array.length; ++ for(int i = 0; i < length; i++) { ++ if (array[i] == key) ++ return true; ++ } ++ return false; + } + + private static View loadDialogView(Context context) { +diff --git a/components/permissions/android/permission_prompt/permission_dialog_delegate.cc b/components/permissions/android/permission_prompt/permission_dialog_delegate.cc +index 6e8491786f6d0..5931748e9bd81 100644 +--- a/components/permissions/android/permission_prompt/permission_dialog_delegate.cc ++++ b/components/permissions/android/permission_prompt/permission_dialog_delegate.cc +@@ -67,6 +67,11 @@ void PermissionDialogJavaDelegate::DismissDialog() { + Java_PermissionDialogDelegate_dismissFromNative(env, j_delegate_); + } + ++int PermissionDialogJavaDelegate::GetSelectedLifetimeOption() { ++ JNIEnv* env = base::android::AttachCurrentThread(); ++ return Java_PermissionDialogDelegate_getSelectedLifetimeOption(env, j_delegate_); ++} ++ + // static + void PermissionDialogDelegate::Create( + content::WebContents* web_contents, +@@ -95,12 +100,26 @@ PermissionDialogDelegate* PermissionDialogDelegate::CreateForTesting( + void PermissionDialogDelegate::Accept(JNIEnv* env, + const JavaParamRef& obj) { + DCHECK(permission_prompt_); ++ content_settings::LifetimeMode lifetimeOption = ++ static_cast( ++ java_delegate_->GetSelectedLifetimeOption()); ++ if (lifetimeOption != content_settings::LifetimeMode::Always) { ++ permission_prompt_->AcceptThisTime(lifetimeOption); ++ return; ++ } + permission_prompt_->Accept(); + } + + void PermissionDialogDelegate::Cancel(JNIEnv* env, + const JavaParamRef& obj) { + DCHECK(permission_prompt_); ++ content_settings::LifetimeMode lifetimeOption = ++ static_cast( ++ java_delegate_->GetSelectedLifetimeOption()); ++ if (lifetimeOption != content_settings::LifetimeMode::Always) { ++ permission_prompt_->DenyThisTime(lifetimeOption); ++ return; ++ } + permission_prompt_->Deny(); + } + +diff --git a/components/permissions/android/permission_prompt/permission_dialog_delegate.h b/components/permissions/android/permission_prompt/permission_dialog_delegate.h +index d0f1df73b7536..76c5685b7d613 100644 +--- a/components/permissions/android/permission_prompt/permission_dialog_delegate.h ++++ b/components/permissions/android/permission_prompt/permission_dialog_delegate.h +@@ -35,6 +35,7 @@ class PermissionDialogJavaDelegate { + PermissionDialogDelegate* owner); + virtual void CreateDialog(); + virtual void DismissDialog(); ++ virtual int GetSelectedLifetimeOption(); + + private: + base::android::ScopedJavaGlobalRef j_delegate_; +diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.cc b/components/permissions/android/permission_prompt/permission_prompt_android.cc +index 40f263ea1e33f..59ec7c86e6866 100644 +--- a/components/permissions/android/permission_prompt/permission_prompt_android.cc ++++ b/components/permissions/android/permission_prompt/permission_prompt_android.cc +@@ -46,6 +46,14 @@ void PermissionPromptAndroid::Accept() { + delegate_->Accept(); + } + ++void PermissionPromptAndroid::AcceptThisTime(content_settings::LifetimeMode lifetimeOption) { ++ delegate_->AcceptThisTime(lifetimeOption); ++} ++ ++void PermissionPromptAndroid::DenyThisTime(content_settings::LifetimeMode lifetimeOption) { ++ delegate_->DenyThisTime(lifetimeOption); ++} ++ + void PermissionPromptAndroid::Deny() { + delegate_->Deny(); + } +diff --git a/components/permissions/android/permission_prompt/permission_prompt_android.h b/components/permissions/android/permission_prompt/permission_prompt_android.h +index 39e962f4e11d2..ae4252d2d5893 100644 +--- a/components/permissions/android/permission_prompt/permission_prompt_android.h ++++ b/components/permissions/android/permission_prompt/permission_prompt_android.h +@@ -46,7 +46,9 @@ class PermissionPromptAndroid : public PermissionPrompt { + + void Closing(); + void Accept(); ++ void AcceptThisTime(content_settings::LifetimeMode lifetimeOption); + void Deny(); ++ void DenyThisTime(content_settings::LifetimeMode lifetimeOption); + void SetManageClicked(); + void SetLearnMoreClicked(); + bool ShouldCurrentRequestUseQuietUI(); +diff --git a/components/permissions/android/permissions_android_strings.grd b/components/permissions/android/permissions_android_strings.grd +index 32d18773dfb46..1b610345f9f72 100644 +--- a/components/permissions/android/permissions_android_strings.grd ++++ b/components/permissions/android/permissions_android_strings.grd +@@ -264,6 +264,23 @@ + Unknown or unsupported device (%1$sA1:B2:C3:D4:E5:F6) + + ++ ++ ++ Remeber my decision ++ ++ ++ Only this time ++ ++ ++ Until all pages of this origin are closed ++ ++ ++ Until Bromite is closed ++ ++ ++ Forever ++ ++ + + + %1$sitem_name (%2$sitem id) +diff --git a/components/permissions/contexts/geolocation_permission_context_android.cc b/components/permissions/contexts/geolocation_permission_context_android.cc +index f347db796304f..64b20371a62bc 100644 +--- a/components/permissions/contexts/geolocation_permission_context_android.cc ++++ b/components/permissions/contexts/geolocation_permission_context_android.cc +@@ -161,7 +161,20 @@ void GeolocationPermissionContextAndroid::NotifyPermissionSet( + bool is_final_decision) { + DCHECK(!is_one_time); + DCHECK(is_final_decision); ++ NotifyPermissionSetWithLifetime(id, requesting_origin, embedding_origin, ++ std::move(callback), persist, content_setting, is_one_time, is_final_decision, ++ content_settings::LifetimeMode::Always); ++} + ++void GeolocationPermissionContextAndroid::NotifyPermissionSetWithLifetime( ++ const PermissionRequestID& id, ++ const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ BrowserPermissionCallback callback, ++ bool persist, ++ ContentSetting content_setting, ++ bool is_one_time, bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option) { + bool is_default_search = IsRequestingOriginDSE(requesting_origin); + if (content_setting == CONTENT_SETTING_ALLOW && + !location_settings_->IsSystemLocationSettingEnabled()) { +@@ -174,7 +187,8 @@ void GeolocationPermissionContextAndroid::NotifyPermissionSet( + if (IsInLocationSettingsBackOff(is_default_search)) { + FinishNotifyPermissionSet(id, requesting_origin, embedding_origin, + std::move(callback), false /* persist */, +- CONTENT_SETTING_BLOCK); ++ CONTENT_SETTING_BLOCK, ++ is_one_time, lifetime_option); + return; + } + +@@ -192,7 +206,8 @@ void GeolocationPermissionContextAndroid::NotifyPermissionSet( + !location_settings_dialog_callback_.is_null()) { + FinishNotifyPermissionSet(id, requesting_origin, embedding_origin, + std::move(callback), false /* persist */, +- CONTENT_SETTING_BLOCK); ++ CONTENT_SETTING_BLOCK, ++ is_one_time, lifetime_option); + return; + } + +@@ -204,12 +219,13 @@ void GeolocationPermissionContextAndroid::NotifyPermissionSet( + base::BindOnce( + &GeolocationPermissionContextAndroid::OnLocationSettingsDialogShown, + weak_factory_.GetWeakPtr(), requesting_origin, embedding_origin, +- persist, content_setting)); ++ persist, content_setting, is_one_time, lifetime_option)); + return; + } + + FinishNotifyPermissionSet(id, requesting_origin, embedding_origin, +- std::move(callback), persist, content_setting); ++ std::move(callback), persist, content_setting, ++ is_one_time, lifetime_option); + } + + PermissionResult +@@ -376,6 +392,7 @@ void GeolocationPermissionContextAndroid::OnLocationSettingsDialogShown( + const GURL& embedding_origin, + bool persist, + ContentSetting content_setting, ++ bool is_one_time, content_settings::LifetimeMode lifetime_option, + LocationSettingsDialogOutcome prompt_outcome) { + bool is_default_search = IsRequestingOriginDSE(requesting_origin); + if (prompt_outcome == GRANTED) { +@@ -393,7 +410,8 @@ void GeolocationPermissionContextAndroid::OnLocationSettingsDialogShown( + + FinishNotifyPermissionSet( + location_settings_dialog_request_id_, requesting_origin, embedding_origin, +- std::move(location_settings_dialog_callback_), persist, content_setting); ++ std::move(location_settings_dialog_callback_), persist, content_setting, ++ is_one_time, lifetime_option); + + location_settings_dialog_request_id_ = + PermissionRequestID(content::GlobalRenderFrameHostId(0, 0), +@@ -406,10 +424,11 @@ void GeolocationPermissionContextAndroid::FinishNotifyPermissionSet( + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool persist, +- ContentSetting content_setting) { +- GeolocationPermissionContext::NotifyPermissionSet( ++ ContentSetting content_setting, ++ bool is_one_time, content_settings::LifetimeMode lifetime_option) { ++ GeolocationPermissionContext::NotifyPermissionSetWithLifetime( + id, requesting_origin, embedding_origin, std::move(callback), persist, +- content_setting, /*is_one_time=*/false, /*is_final_decision=*/true); ++ content_setting, is_one_time, /*is_final_decision=*/true, lifetime_option); + } + + void GeolocationPermissionContextAndroid::SetLocationSettingsForTesting( +diff --git a/components/permissions/contexts/geolocation_permission_context_android.h b/components/permissions/contexts/geolocation_permission_context_android.h +index c05093c1e2328..0ba67e184b7db 100644 +--- a/components/permissions/contexts/geolocation_permission_context_android.h ++++ b/components/permissions/contexts/geolocation_permission_context_android.h +@@ -91,6 +91,15 @@ class GeolocationPermissionContextAndroid + ContentSetting content_setting, + bool is_one_time, + bool is_final_decision) override; ++ void NotifyPermissionSetWithLifetime(const PermissionRequestID& id, ++ const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ BrowserPermissionCallback callback, ++ bool persist, ++ ContentSetting content_setting, ++ bool is_one_time, ++ bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option) override; + PermissionResult UpdatePermissionStatusWithDeviceStatus( + PermissionResult result, + const GURL& requesting_origin, +@@ -133,6 +142,7 @@ class GeolocationPermissionContextAndroid + const GURL& embedding_origin, + bool persist, + ContentSetting content_setting, ++ bool is_one_time, content_settings::LifetimeMode lifetime_option, + LocationSettingsDialogOutcome prompt_outcome); + + void FinishNotifyPermissionSet(const PermissionRequestID& id, +@@ -140,7 +150,9 @@ class GeolocationPermissionContextAndroid + const GURL& embedding_origin, + BrowserPermissionCallback callback, + bool persist, +- ContentSetting content_setting); ++ ContentSetting content_setting, ++ bool is_one_time, ++ content_settings::LifetimeMode lifetime_option); + + std::unique_ptr location_settings_; + +diff --git a/components/permissions/permission_context_base.cc b/components/permissions/permission_context_base.cc +index 9bc4af3e1110f..7dcae884eb337 100644 +--- a/components/permissions/permission_context_base.cc ++++ b/components/permissions/permission_context_base.cc +@@ -249,6 +249,20 @@ PermissionContextBase::CreatePermissionRequest( + std::move(delete_callback)); + } + ++std::unique_ptr ++PermissionContextBase::CreatePermissionRequest( ++ const GURL& request_origin, ++ ContentSettingsType content_settings_type, ++ bool has_gesture, ++ content::WebContents* web_contents, ++ PermissionRequest::PermissionDecidedCallbackWithLifetime permission_decided_callback, ++ base::OnceClosure delete_callback) const { ++ return std::make_unique( ++ request_origin, ContentSettingsTypeToRequestType(content_settings_type), ++ has_gesture, std::move(permission_decided_callback), ++ std::move(delete_callback)); ++} ++ + PermissionResult PermissionContextBase::GetPermissionStatus( + content::RenderFrameHost* render_frame_host, + const GURL& requesting_origin, +@@ -428,7 +442,8 @@ void PermissionContextBase::PermissionDecided(const PermissionRequestID& id, + const GURL& embedding_origin, + ContentSetting content_setting, + bool is_one_time, +- bool is_final_decision) { ++ bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option) { + DCHECK(content_setting == CONTENT_SETTING_ALLOW || + content_setting == CONTENT_SETTING_BLOCK || + content_setting == CONTENT_SETTING_DEFAULT); +@@ -443,13 +458,14 @@ void PermissionContextBase::PermissionDecided(const PermissionRequestID& id, + // missing if a permission prompt was preignored and we already notified an + // origin about it. + if (request->second.second) { +- NotifyPermissionSet(id, requesting_origin, embedding_origin, ++ NotifyPermissionSetWithLifetime(id, requesting_origin, embedding_origin, + std::move(request->second.second), persist, +- content_setting, is_one_time, is_final_decision); ++ content_setting, is_one_time, is_final_decision, ++ lifetime_option); + } else { +- NotifyPermissionSet(id, requesting_origin, embedding_origin, ++ NotifyPermissionSetWithLifetime(id, requesting_origin, embedding_origin, + base::DoNothing(), persist, content_setting, +- is_one_time, is_final_decision); ++ is_one_time, is_final_decision, lifetime_option); + } + } + +@@ -501,11 +517,27 @@ void PermissionContextBase::NotifyPermissionSet( + ContentSetting content_setting, + bool is_one_time, + bool is_final_decision) { ++ DCHECK(is_one_time == false); ++ NotifyPermissionSetWithLifetime(id, requesting_origin, embedding_origin, std::move(callback), ++ persist, content_setting, is_one_time, is_final_decision, ++ content_settings::LifetimeMode::Always); ++} ++ ++void PermissionContextBase::NotifyPermissionSetWithLifetime( ++ const PermissionRequestID& id, ++ const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ BrowserPermissionCallback callback, ++ bool persist, ++ ContentSetting content_setting, ++ bool is_one_time, ++ bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (persist) { + UpdateContentSetting(requesting_origin, embedding_origin, content_setting, +- is_one_time); ++ is_one_time, lifetime_option); + } + + if (is_final_decision) { +@@ -535,6 +567,15 @@ void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin, + const GURL& embedding_origin, + ContentSetting content_setting, + bool is_one_time) { ++ UpdateContentSetting(requesting_origin, embedding_origin, content_setting, ++ is_one_time, content_settings::LifetimeMode::Always); ++} ++ ++void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ ContentSetting content_setting, ++ bool is_one_time, ++ content_settings::LifetimeMode lifetime_option) { + DCHECK_EQ(requesting_origin, requesting_origin.DeprecatedGetOriginAsURL()); + DCHECK_EQ(embedding_origin, embedding_origin.DeprecatedGetOriginAsURL()); + DCHECK(content_setting == CONTENT_SETTING_ALLOW || +@@ -544,6 +585,8 @@ void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin, + constraints.set_session_model(is_one_time + ? content_settings::SessionModel::OneTime + : content_settings::SessionModel::Durable); ++ if (is_one_time) ++ constraints = content_settings::GetConstraintSessionExpiration(lifetime_option); + + #if !BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( +diff --git a/components/permissions/permission_context_base.h b/components/permissions/permission_context_base.h +index 35971997d12c7..a10e5b6fcebd2 100644 +--- a/components/permissions/permission_context_base.h ++++ b/components/permissions/permission_context_base.h +@@ -143,6 +143,15 @@ class PermissionContextBase : public content_settings::Observer { + + // Updates stored content setting if persist is set, updates tab indicators + // and runs the callback to finish the request. ++ virtual void NotifyPermissionSetWithLifetime(const PermissionRequestID& id, ++ const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ BrowserPermissionCallback callback, ++ bool persist, ++ ContentSetting content_setting, ++ bool is_one_time, ++ bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option); + virtual void NotifyPermissionSet(const PermissionRequestID& id, + const GURL& requesting_origin, + const GURL& embedding_origin, +@@ -164,6 +173,11 @@ class PermissionContextBase : public content_settings::Observer { + // Store the decided permission as a content setting. + // virtual since the permission might be stored with different restrictions + // (for example for desktop notifications). ++ void UpdateContentSetting(const GURL& requesting_origin, ++ const GURL& embedding_origin, ++ ContentSetting content_setting, ++ bool is_one_time, ++ content_settings::LifetimeMode lifetime_option); + virtual void UpdateContentSetting(const GURL& requesting_origin, + const GURL& embedding_origin, + ContentSetting content_setting, +@@ -196,6 +210,14 @@ class PermissionContextBase : public content_settings::Observer { + PermissionRequest::PermissionDecidedCallback permission_decided_callback, + base::OnceClosure delete_callback) const; + ++ virtual std::unique_ptr CreatePermissionRequest( ++ const GURL& request_origin, ++ ContentSettingsType content_settings_type, ++ bool has_gesture, ++ content::WebContents* web_contents, ++ PermissionRequest::PermissionDecidedCallbackWithLifetime permission_decided_callback, ++ base::OnceClosure delete_callback) const; ++ + ContentSettingsType content_settings_type() const { + return content_settings_type_; + } +@@ -222,7 +244,8 @@ class PermissionContextBase : public content_settings::Observer { + const GURL& embedding_origin, + ContentSetting content_setting, + bool is_one_time, +- bool is_final_decision); ++ bool is_final_decision, ++ content_settings::LifetimeMode lifetime_option); + + raw_ptr browser_context_; + const ContentSettingsType content_settings_type_; +diff --git a/components/permissions/permission_prompt.h b/components/permissions/permission_prompt.h +index d61ebaee55c5d..8cec8785f4a08 100644 +--- a/components/permissions/permission_prompt.h ++++ b/components/permissions/permission_prompt.h +@@ -65,8 +65,9 @@ class PermissionPrompt { + virtual GURL GetEmbeddingOrigin() const = 0; + + virtual void Accept() = 0; +- virtual void AcceptThisTime() = 0; ++ virtual void AcceptThisTime(content_settings::LifetimeMode lifetime_option) = 0; + virtual void Deny() = 0; ++ virtual void DenyThisTime(content_settings::LifetimeMode lifetime_option) = 0; + virtual void Dismiss() = 0; + virtual void Ignore() = 0; + +diff --git a/components/permissions/permission_request.cc b/components/permissions/permission_request.cc +index 8baca976c0ac3..9cd4ccd4d33e6 100644 +--- a/components/permissions/permission_request.cc ++++ b/components/permissions/permission_request.cc +@@ -28,6 +28,18 @@ PermissionRequest::PermissionRequest( + permission_decided_callback_(std::move(permission_decided_callback)), + delete_callback_(std::move(delete_callback)) {} + ++PermissionRequest::PermissionRequest( ++ const GURL& requesting_origin, ++ RequestType request_type, ++ bool has_gesture, ++ PermissionDecidedCallbackWithLifetime permission_decided_callback, ++ base::OnceClosure delete_callback) ++ : requesting_origin_(requesting_origin), ++ request_type_(request_type), ++ has_gesture_(has_gesture), ++ permission_decided_callback_withlifetime_(std::move(permission_decided_callback)), ++ delete_callback_(std::move(delete_callback)) {} ++ + PermissionRequest::~PermissionRequest() { + DCHECK(delete_callback_.is_null()); + } +@@ -257,19 +269,37 @@ bool PermissionRequest::ShouldUseTwoOriginPrompt() const { + permissions::features::kPermissionStorageAccessAPI); + } + +-void PermissionRequest::PermissionGranted(bool is_one_time) { ++void PermissionRequest::PermissionGranted(bool is_one_time, ++ content_settings::LifetimeMode lifetime_option) { ++ if (permission_decided_callback_withlifetime_) { ++ std::move(permission_decided_callback_withlifetime_) ++ .Run(CONTENT_SETTING_ALLOW, is_one_time, /*is_final_decision=*/true, lifetime_option); ++ return; ++ } + std::move(permission_decided_callback_) + .Run(CONTENT_SETTING_ALLOW, is_one_time, + /*is_final_decision=*/true); + } + +-void PermissionRequest::PermissionDenied() { ++void PermissionRequest::PermissionDenied(bool is_one_time, ++ content_settings::LifetimeMode lifetime_option) { ++ if (permission_decided_callback_withlifetime_) { ++ std::move(permission_decided_callback_withlifetime_) ++ .Run(CONTENT_SETTING_BLOCK, is_one_time, /*is_final_decision=*/true, lifetime_option); ++ return; ++ } + std::move(permission_decided_callback_) + .Run(CONTENT_SETTING_BLOCK, /*is_one_time=*/false, + /*is_final_decision=*/true); + } + + void PermissionRequest::Cancelled(bool is_final_decision) { ++ if (permission_decided_callback_withlifetime_) { ++ std::move(permission_decided_callback_withlifetime_) ++ .Run(CONTENT_SETTING_DEFAULT, false, is_final_decision, ++ content_settings::LifetimeMode::Always); ++ return; ++ } + permission_decided_callback_.Run(CONTENT_SETTING_DEFAULT, + /*is_one_time=*/false, is_final_decision); + } +diff --git a/components/permissions/permission_request.h b/components/permissions/permission_request.h +index aa585003b8458..e8e6ec6c55ae1 100644 +--- a/components/permissions/permission_request.h ++++ b/components/permissions/permission_request.h +@@ -43,6 +43,11 @@ class PermissionRequest { + bool /*is_one_time*/, + bool /*is_final_decision*/)>; + ++ using PermissionDecidedCallbackWithLifetime = ++ base::OnceCallback; ++ + // `permission_decided_callback` is called when the permission request is + // resolved by the user (see comment on PermissionDecidedCallback above). + // `delete_callback` is called when the permission request is no longer needed +@@ -58,6 +63,12 @@ class PermissionRequest { + PermissionDecidedCallback permission_decided_callback, + base::OnceClosure delete_callback); + ++ PermissionRequest(const GURL& requesting_origin, ++ RequestType request_type, ++ bool has_gesture, ++ PermissionDecidedCallbackWithLifetime permission_decided_callback, ++ base::OnceClosure delete_callback); ++ + PermissionRequest(const PermissionRequest&) = delete; + PermissionRequest& operator=(const PermissionRequest&) = delete; + +@@ -119,10 +130,10 @@ class PermissionRequest { + // If |is_one_time| is true the permission will last until all tabs of + // |origin| are closed or navigated away from, and then the permission will + // automatically expire after 1 day. +- void PermissionGranted(bool is_one_time); ++ void PermissionGranted(bool is_one_time, content_settings::LifetimeMode lifetime_option); + + // Called when the user has denied the requested permission. +- void PermissionDenied(); ++ void PermissionDenied(bool is_one_time, content_settings::LifetimeMode lifetime_option); + + // Called when the user has cancelled the permission request. This + // corresponds to a denial, but is segregated in case the context needs to +@@ -170,6 +181,9 @@ class PermissionRequest { + // Called once a decision is made about the permission. + PermissionDecidedCallback permission_decided_callback_; + ++ // Called once a decision is made about the permission (with lifetime option). ++ PermissionDecidedCallbackWithLifetime permission_decided_callback_withlifetime_; ++ + // Called when the request is no longer in use so it can be deleted by the + // caller. + base::OnceClosure delete_callback_; +diff --git a/components/permissions/permission_request_manager.cc b/components/permissions/permission_request_manager.cc +index b77e176dd1b9a..2e4d5de971300 100644 +--- a/components/permissions/permission_request_manager.cc ++++ b/components/permissions/permission_request_manager.cc +@@ -155,7 +155,7 @@ void PermissionRequestManager::AddRequest( + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDenyPermissionPrompts)) { +- request->PermissionDenied(); ++ request->PermissionDenied(/*is_one_time*/false, content_settings::LifetimeMode::Always); + request->RequestFinished(); + return; + } +@@ -231,7 +231,7 @@ void PermissionRequestManager::AddRequest( + if (auto_approval_origin) { + if (url::Origin::Create(request->requesting_origin()) == + auto_approval_origin.value()) { +- request->PermissionGranted(/*is_one_time=*/false); ++ request->PermissionGranted(/*is_one_time=*/false, content_settings::LifetimeMode::Always); + } + request->RequestFinished(); + return; +@@ -568,7 +568,8 @@ void PermissionRequestManager::Accept() { + (*requests_iter)->request_type(), + PermissionAction::GRANTED); + PermissionGrantedIncludingDuplicates(*requests_iter, +- /*is_one_time=*/false); ++ /*is_one_time=*/false, ++ content_settings::LifetimeMode::Always); + + #if !BUILDFLAG(IS_ANDROID) + absl::optional content_settings_type = +@@ -586,7 +587,7 @@ void PermissionRequestManager::Accept() { + FinalizeCurrentRequests(PermissionAction::GRANTED); + } + +-void PermissionRequestManager::AcceptThisTime() { ++void PermissionRequestManager::AcceptThisTime(content_settings::LifetimeMode mode) { + if (ignore_callbacks_from_prompt_) + return; + DCHECK(view_); +@@ -597,7 +598,8 @@ void PermissionRequestManager::AcceptThisTime() { + (*requests_iter)->request_type(), + PermissionAction::GRANTED_ONCE); + PermissionGrantedIncludingDuplicates(*requests_iter, +- /*is_one_time=*/true); ++ /*is_one_time=*/true, ++ mode); + } + + NotifyRequestDecided(PermissionAction::GRANTED_ONCE); +@@ -605,6 +607,15 @@ void PermissionRequestManager::AcceptThisTime() { + } + + void PermissionRequestManager::Deny() { ++ Deny_(/*is_one_time*/ false, content_settings::LifetimeMode::Always); ++} ++ ++void PermissionRequestManager::DenyThisTime(content_settings::LifetimeMode mode) { ++ Deny_(/*is_one_time*/ true, mode); ++} ++ ++void PermissionRequestManager::Deny_(bool is_one_time, ++ content_settings::LifetimeMode lifetime_option) { + if (ignore_callbacks_from_prompt_) + return; + DCHECK(view_); +@@ -627,7 +638,7 @@ void PermissionRequestManager::Deny() { + StorePermissionActionForUMA((*requests_iter)->requesting_origin(), + (*requests_iter)->request_type(), + PermissionAction::DENIED); +- PermissionDeniedIncludingDuplicates(*requests_iter); ++ PermissionDeniedIncludingDuplicates(*requests_iter, is_one_time, lifetime_option); + } + + NotifyRequestDecided(PermissionAction::DENIED); +@@ -1162,32 +1173,32 @@ PermissionRequestManager::VisitDuplicateRequests( + + void PermissionRequestManager::PermissionGrantedIncludingDuplicates( + PermissionRequest* request, +- bool is_one_time) { ++ bool is_one_time, content_settings::LifetimeMode lifetime_option) { + DCHECK_EQ(1ul, base::ranges::count(requests_, request) + + pending_permission_requests_.Count(request)) + << "Only requests in [pending_permission_]requests_ can have duplicates"; +- request->PermissionGranted(is_one_time); ++ request->PermissionGranted(is_one_time, lifetime_option); + VisitDuplicateRequests( + base::BindRepeating( +- [](bool is_one_time, ++ [](bool is_one_time, content_settings::LifetimeMode lifetime_option, + const base::WeakPtr& weak_request) { +- weak_request->PermissionGranted(is_one_time); ++ weak_request->PermissionGranted(is_one_time, lifetime_option); + }, +- is_one_time), ++ is_one_time, lifetime_option), + request); + } + + void PermissionRequestManager::PermissionDeniedIncludingDuplicates( +- PermissionRequest* request) { ++ PermissionRequest* request, bool is_one_time, content_settings::LifetimeMode lifetime_option) { + DCHECK_EQ(1ul, base::ranges::count(requests_, request) + + pending_permission_requests_.Count(request)) + << "Only requests in [pending_permission_]requests_ can have duplicates"; +- request->PermissionDenied(); ++ request->PermissionDenied(is_one_time, lifetime_option); + VisitDuplicateRequests( + base::BindRepeating( +- [](const base::WeakPtr& weak_request) { +- weak_request->PermissionDenied(); +- }), ++ [](bool is_one_time, content_settings::LifetimeMode lifetime_option, const base::WeakPtr& weak_request) { ++ weak_request->PermissionDenied(is_one_time, lifetime_option); ++ }, is_one_time, lifetime_option), + request); + } + +@@ -1436,7 +1447,7 @@ void PermissionRequestManager::LogWarningToConsole(const char* message) { + void PermissionRequestManager::DoAutoResponseForTesting() { + switch (auto_response_for_test_) { + case ACCEPT_ONCE: +- AcceptThisTime(); ++ AcceptThisTime(content_settings::LifetimeMode::Always); + break; + case ACCEPT_ALL: + Accept(); +diff --git a/components/permissions/permission_request_manager.h b/components/permissions/permission_request_manager.h +index 80505ee3af9db..2fc3c626205af 100644 +--- a/components/permissions/permission_request_manager.h ++++ b/components/permissions/permission_request_manager.h +@@ -159,8 +159,10 @@ class PermissionRequestManager + GURL GetRequestingOrigin() const override; + GURL GetEmbeddingOrigin() const override; + void Accept() override; +- void AcceptThisTime() override; ++ void AcceptThisTime(content_settings::LifetimeMode lifetime_option) override; + void Deny() override; ++ void Deny_(bool is_one_time, content_settings::LifetimeMode lifetime_option); ++ void DenyThisTime(content_settings::LifetimeMode lifetime_option) override; + void Dismiss() override; + void Ignore() override; + void OpenHelpCenterLink(const ui::Event& event) override; +@@ -362,9 +364,12 @@ class PermissionRequestManager + + // Calls PermissionGranted on a request and all its duplicates. + void PermissionGrantedIncludingDuplicates(PermissionRequest* request, +- bool is_one_time); ++ bool is_one_time, ++ content_settings::LifetimeMode lifetime_option); + // Calls PermissionDenied on a request and all its duplicates. +- void PermissionDeniedIncludingDuplicates(PermissionRequest* request); ++ void PermissionDeniedIncludingDuplicates(PermissionRequest* request, ++ bool is_one_time, ++ content_settings::LifetimeMode lifetime_option); + // Calls Cancelled on a request and all its duplicates. + void CancelledIncludingDuplicates(PermissionRequest* request, + bool is_final_decision = true); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-menu-item-to-bookmark-all-tabs.patch b/build/bromite_patches/Add-menu-item-to-bookmark-all-tabs.patch new file mode 100644 index 0000000000000000000000000000000000000000..b587d3fea91371e121c600c05df50821b2d5dfd3 --- /dev/null +++ b/build/bromite_patches/Add-menu-item-to-bookmark-all-tabs.patch @@ -0,0 +1,745 @@ +From bcf63e9bf569667104c2144d94489da0aaa87b5a Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Thu, 18 Feb 2021 21:22:52 +0100 +Subject: [PATCH 125/192] Add menu item to bookmark all tabs + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/java/res/menu/main_menu.xml | 7 +++ + .../chrome/browser/ChromeTabbedActivity.java | 24 ++++++++ + .../browser/bookmarks/BookmarkBridge.java | 61 +++++++++++++++++++ + .../browser/bookmarks/BookmarkUtils.java | 4 ++ + .../bookmarks/android/bookmark_bridge.cc | 19 +++++- + .../bookmarks/android/bookmark_bridge.h | 4 ++ + .../browser/bookmarks/bookmark_html_writer.cc | 8 ++- + .../bookmarks/chrome_bookmark_client.cc | 2 + + .../dialogs/DownloadLocationCustomView.java | 4 +- + .../strings/android_chrome_strings.grd | 3 + + components/bookmark_bar_strings.grdp | 6 ++ + .../bookmarks/browser/bookmark_codec.cc | 22 +++++-- + components/bookmarks/browser/bookmark_codec.h | 7 ++- + .../browser/bookmark_load_details.cc | 4 ++ + .../bookmarks/browser/bookmark_load_details.h | 3 + + .../bookmarks/browser/bookmark_model.cc | 3 +- + components/bookmarks/browser/bookmark_model.h | 7 +++ + components/bookmarks/browser/bookmark_node.cc | 11 ++++ + components/bookmarks/browser/bookmark_node.h | 4 ++ + .../bookmarks/browser/bookmark_uuids.cc | 3 + + components/bookmarks/browser/bookmark_uuids.h | 1 + + components/bookmarks/browser/model_loader.cc | 3 +- + .../bookmark_specifics_conversions.cc | 1 + + 23 files changed, 200 insertions(+), 11 deletions(-) + +diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml +index 6e7eff229907d..ac0aad12b3dda 100644 +--- a/chrome/android/java/res/menu/main_menu.xml ++++ b/chrome/android/java/res/menu/main_menu.xml +@@ -69,6 +69,10 @@ found in the LICENSE file. + ++ + +@@ -184,6 +188,9 @@ found in the LICENSE file. + ++ + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +index fa55eb8d43dc9..a93251c24b3e9 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +@@ -82,6 +82,7 @@ import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHan + import org.chromium.chrome.browser.bookmarks.BookmarkUtils; + import org.chromium.chrome.browser.browserservices.intents.WebappConstants; + import org.chromium.chrome.browser.compositor.CompositorViewHolder; ++import org.chromium.chrome.browser.bookmarks.BookmarkModel; + import org.chromium.chrome.browser.compositor.layouts.Layout; + import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome; + import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone; +@@ -2288,6 +2289,8 @@ public class ChromeTabbedActivity extends ChromeActivity getTabModelSelector().closeAllTabs()); + RecordUserAction.record("MobileMenuCloseAllTabs"); ++ } else if (id == R.id.bookmark_all_tabs_menu_id) { ++ bookmarkAllTabs(); + } else if (id == R.id.close_all_incognito_tabs_menu_id) { + // Close only incognito tabs + CloseAllTabsDialog.show(this, getModalDialogManagerSupplier(), +@@ -2353,6 +2356,27 @@ public class ChromeTabbedActivity extends ChromeActivity { ++ for (int i = 0; i < tabModel.getCount(); i++) { ++ Tab tab = tabModel.getTabAt(i); ++ if (tab.isNativePage()) { ++ continue; ++ } ++ bookmarkModel.addToTabsCollection(this, tab); ++ } ++ bookmarkModel.finishedAddingToTabsCollection(this, getSnackbarManager()); ++ }); ++ } ++ + private void recordLauncherShortcutAction(boolean isIncognito) { + if (isIncognito) { + RecordUserAction.record("Android.LauncherShortcut.NewIncognitoTab"); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java +index 95788b70eff1e..2c46d17385741 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java +@@ -29,6 +29,7 @@ import com.google.protobuf.InvalidProtocolBufferException; + + import org.chromium.base.Callback; + import org.chromium.base.ContextUtils; ++import org.chromium.base.Log; + import org.chromium.base.ObserverList; + import org.chromium.base.ThreadUtils; + import org.chromium.base.annotations.CalledByNative; +@@ -39,6 +40,10 @@ import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim; + import org.chromium.chrome.browser.profiles.Profile; + import org.chromium.chrome.browser.read_later.ReadingListUtils; + import org.chromium.chrome.browser.tab.Tab; ++import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; ++import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; ++import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController; ++import org.chromium.chrome.R; + import org.chromium.components.bookmarks.BookmarkId; + import org.chromium.components.bookmarks.BookmarkItem; + import org.chromium.components.bookmarks.BookmarkType; +@@ -82,6 +87,8 @@ import java.io.File; + * bookmark model stored in native. + */ + class BookmarkBridge { ++ private static final String TAG = "BookmarkBridge"; ++ + private long mNativeBookmarkBridge; + private boolean mIsDestroyed; + private boolean mIsDoingExtensiveChanges; +@@ -377,6 +384,16 @@ class BookmarkBridge { + mNativeBookmarkBridge, BookmarkBridge.this); + } + ++ /** ++ * @return The BookmarkId for the Tabs collecction folder node ++ */ ++ public BookmarkId getTabsCollectionFolderId() { ++ ThreadUtils.assertOnUiThread(); ++ assert mIsNativeBookmarkModelLoaded; ++ return BookmarkBridgeJni.get().getTabsCollectionFolderId( ++ mNativeBookmarkBridge, BookmarkBridge.this); ++ } ++ + /** + * @return Id representing the special "other" folder from bookmark model. + */ +@@ -1012,6 +1029,49 @@ class BookmarkBridge { + mNativeBookmarkBridge, BookmarkBridge.this, title, url); + } + ++ // Used to bookmark all tabs in a specific folder, created if not existing ++ public BookmarkId addToTabsCollection(Context context, Tab tab) { ++ BookmarkId parent = getTabsCollectionFolderId(); ++ BookmarkId existingId = getUserBookmarkIdForTab(tab); ++ if (existingId != null && existingId.getId() != BookmarkId.INVALID_ID) { ++ BookmarkId existingBookmarkId = new BookmarkId(existingId.getId(), BookmarkType.NORMAL); ++ BookmarkItem existingBookmark = getBookmarkById(existingBookmarkId); ++ if (parent.equals(existingBookmark.getParentId())) { ++ // bookmark already exists in the tabs collection folder ++ return existingBookmarkId; ++ } ++ } ++ BookmarkId bookmarkId = ++ addBookmark(parent, getChildCount(parent), tab.getTitle(), tab.getUrl()); ++ ++ if (bookmarkId == null) { ++ Log.e(TAG, ++ "Failed to add bookmarks: parentTypeAndId %s", parent); ++ } ++ return bookmarkId; ++ } ++ ++ public void finishedAddingToTabsCollection(Activity activity, SnackbarManager snackbarManager) { ++ BookmarkId parent = getTabsCollectionFolderId(); ++ ++ BookmarkItem bookmarkItem = getBookmarkById(parent); ++ String folderName; ++ if (bookmarkItem != null) { ++ folderName = bookmarkItem.getTitle(); ++ } else { ++ folderName = ""; ++ } ++ SnackbarController snackbarController = new SnackbarController() { ++ @Override ++ public void onAction(Object actionData) { ++ } ++ }; ++ Snackbar snackbar = Snackbar.make(folderName, snackbarController, Snackbar.TYPE_ACTION, ++ Snackbar.UMA_BOOKMARK_ADDED) ++ .setTemplateText(activity.getString(R.string.bookmark_page_saved_folder)); ++ snackbarManager.showSnackbar(snackbar); ++ } ++ + /** + * @param url The URL of the reading list item. + * @return The reading list item with the URL, or null if no such reading list item. +@@ -1232,6 +1292,7 @@ class BookmarkBridge { + void getAllFoldersWithDepths(long nativeBookmarkBridge, BookmarkBridge caller, + List folderList, List depthList); + BookmarkId getRootFolderId(long nativeBookmarkBridge, BookmarkBridge caller); ++ BookmarkId getTabsCollectionFolderId(long nativeBookmarkBridge, BookmarkBridge caller); + BookmarkId getMobileFolderId(long nativeBookmarkBridge, BookmarkBridge caller); + BookmarkId getOtherFolderId(long nativeBookmarkBridge, BookmarkBridge caller); + BookmarkId getDesktopFolderId(long nativeBookmarkBridge, BookmarkBridge caller); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java +index 2eaf9a695d60d..b7b46031880e6 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java +@@ -615,6 +615,7 @@ public class BookmarkUtils { + List topLevelFolders = new ArrayList<>(); + BookmarkId desktopNodeId = bookmarkModel.getDesktopFolderId(); + BookmarkId mobileNodeId = bookmarkModel.getMobileFolderId(); ++ BookmarkId tabsCollectionNodeId = bookmarkModel.getTabsCollectionFolderId(); + BookmarkId othersNodeId = bookmarkModel.getOtherFolderId(); + + List specialFoldersIds = +@@ -640,6 +641,9 @@ public class BookmarkUtils { + if (bookmarkModel.isFolderVisible(mobileNodeId)) { + topLevelFolders.add(mobileNodeId); + } ++ if (bookmarkModel.isFolderVisible(tabsCollectionNodeId)) { ++ topLevelFolders.add(tabsCollectionNodeId); ++ } + if (bookmarkModel.isFolderVisible(desktopNodeId)) { + topLevelFolders.add(desktopNodeId); + } +diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc +index b0c4c40e9c240..f9745329803d7 100644 +--- a/chrome/browser/bookmarks/android/bookmark_bridge.cc ++++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc +@@ -470,12 +470,17 @@ void BookmarkBridge::GetTopLevelFolderIds( + std::size_t special_count = top_level_folders.size(); + + if (get_normal) { +- DCHECK_EQ(4u, bookmark_model_->root_node()->children().size()); ++ DCHECK_EQ(5u, bookmark_model_->root_node()->children().size()); + for (const auto& node : bookmark_model_->mobile_node()->children()) { + if (node->is_folder()) + top_level_folders.push_back(node.get()); + } + ++ for (const auto& node : bookmark_model_->tabs_collection_node()->children()) { ++ if (node->is_folder()) ++ top_level_folders.push_back(node.get()); ++ } ++ + for (const auto& node : bookmark_model_->bookmark_bar_node()->children()) { + if (node->is_folder()) + top_level_folders.push_back(node.get()); +@@ -523,6 +528,7 @@ void BookmarkBridge::GetAllFoldersWithDepths( + // Vector to temporarily contain all child bookmarks at same level for sorting + std::vector bookmarks = { + bookmark_model_->mobile_node(), ++ bookmark_model_->tabs_collection_node(), + bookmark_model_->bookmark_bar_node(), + bookmark_model_->other_node(), + }; +@@ -575,6 +581,17 @@ ScopedJavaLocalRef BookmarkBridge::GetMobileFolderId( + return folder_id_obj; + } + ++ScopedJavaLocalRef BookmarkBridge::GetTabsCollectionFolderId( ++ JNIEnv* env, ++ const JavaParamRef& obj) { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); ++ const BookmarkNode* tabs_collection_node = bookmark_model_->tabs_collection_node(); ++ ScopedJavaLocalRef folder_id_obj = ++ JavaBookmarkIdCreateBookmarkId( ++ env, tabs_collection_node->id(), GetBookmarkType(tabs_collection_node)); ++ return folder_id_obj; ++} ++ + ScopedJavaLocalRef BookmarkBridge::GetOtherFolderId( + JNIEnv* env, + const JavaParamRef& obj) { +diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.h b/chrome/browser/bookmarks/android/bookmark_bridge.h +index f194fe45a09f5..ef6ad825b903f 100644 +--- a/chrome/browser/bookmarks/android/bookmark_bridge.h ++++ b/chrome/browser/bookmarks/android/bookmark_bridge.h +@@ -133,6 +133,10 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, + JNIEnv* env, + const base::android::JavaParamRef& obj); + ++ base::android::ScopedJavaLocalRef GetTabsCollectionFolderId( ++ JNIEnv* env, ++ const base::android::JavaParamRef& obj); ++ + base::android::ScopedJavaLocalRef GetOtherFolderId( + JNIEnv* env, + const base::android::JavaParamRef& obj); +diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc +index 2f458ada42c60..ad61a33630e61 100644 +--- a/chrome/browser/bookmarks/bookmark_html_writer.cc ++++ b/chrome/browser/bookmarks/bookmark_html_writer.cc +@@ -190,15 +190,19 @@ class Writer : public base::RefCountedThreadSafe { + roots->FindDict(BookmarkCodec::kOtherBookmarkFolderNameKey); + base::Value::Dict* mobile_folder_value = + roots->FindDict(BookmarkCodec::kMobileBookmarkFolderNameKey); ++ base::Value::Dict* tabs_collection_value = ++ roots->FindDict(BookmarkCodec::kTabsBookmarkFolderNameKey); + DCHECK(root_folder_value); + DCHECK(other_folder_value); + DCHECK(mobile_folder_value); ++ DCHECK(tabs_collection_value); + + IncrementIndent(); + + if (!WriteNode(*root_folder_value, BookmarkNode::BOOKMARK_BAR) || + !WriteNode(*other_folder_value, BookmarkNode::OTHER_NODE) || +- !WriteNode(*mobile_folder_value, BookmarkNode::MOBILE)) { ++ !WriteNode(*mobile_folder_value, BookmarkNode::MOBILE) || ++ !WriteNode(*tabs_collection_value, BookmarkNode::TABS_COLLECTION)) { + NotifyOnFinish(BookmarksExportObserver::Result::kCouldNotWriteNodes); + return; + } +@@ -465,6 +469,8 @@ void BookmarkFaviconFetcher::ExportBookmarks() { + BookmarkModelFactory::GetForBrowserContext(profile_)->other_node()); + ExtractUrls( + BookmarkModelFactory::GetForBrowserContext(profile_)->mobile_node()); ++ ExtractUrls( ++ BookmarkModelFactory::GetForBrowserContext(profile_)->tabs_collection_node()); + if (!bookmark_urls_.empty()) + FetchNextFavicon(); + else +diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.cc b/chrome/browser/bookmarks/chrome_bookmark_client.cc +index 247da1056dae1..81c56bacf0ab7 100644 +--- a/chrome/browser/bookmarks/chrome_bookmark_client.cc ++++ b/chrome/browser/bookmarks/chrome_bookmark_client.cc +@@ -106,6 +106,8 @@ bool ChromeBookmarkClient::IsPermanentNodeVisibleWhenEmpty( + return !is_mobile; + case bookmarks::BookmarkNode::MOBILE: + return is_mobile; ++ case bookmarks::BookmarkNode::TABS_COLLECTION: ++ return is_mobile; + } + + return false; +diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java +index c6c56e5bf5063..a49059193334b 100644 +--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java ++++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java +@@ -79,7 +79,7 @@ public class DownloadLocationCustomView + mDirectoryAdapter.update(); + } + +- void setTitle(CharSequence title) { ++ public void setTitle(CharSequence title) { + mTitle.setText(title); + } + +@@ -87,7 +87,7 @@ public class DownloadLocationCustomView + mSubtitleView.setText(subtitle); + } + +- void setFileName(CharSequence fileName) { ++ public void setFileName(CharSequence fileName) { + mFileName.setText(fileName); + } + +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index 6ae11fc0b71ef..3198555c554c0 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -3871,6 +3871,9 @@ To change this setting, <resetlink>reset sync

+ Select tabs + ++ ++ Bookmark all tabs ++ + + Get image descriptions + +diff --git a/components/bookmark_bar_strings.grdp b/components/bookmark_bar_strings.grdp +index 2f737d348e586..c78b9bb5490d2 100644 +--- a/components/bookmark_bar_strings.grdp ++++ b/components/bookmark_bar_strings.grdp +@@ -20,6 +20,9 @@ + + Mobile bookmarks + ++ ++ Tabs collection ++ + + Other bookmarks + +@@ -34,6 +37,9 @@ + + Mobile Bookmarks + ++ ++ Tabs Collection ++ + + Other Bookmarks + +diff --git a/components/bookmarks/browser/bookmark_codec.cc b/components/bookmarks/browser/bookmark_codec.cc +index 187520ff6cb69..8afed3e19f10a 100644 +--- a/components/bookmarks/browser/bookmark_codec.cc ++++ b/components/bookmarks/browser/bookmark_codec.cc +@@ -36,6 +36,7 @@ const char BookmarkCodec::kBookmarkBarFolderNameKey[] = "bookmark_bar"; + const char BookmarkCodec::kOtherBookmarkFolderNameKey[] = "other"; + // The value is left as 'synced' for historical reasons. + const char BookmarkCodec::kMobileBookmarkFolderNameKey[] = "synced"; ++const char BookmarkCodec::kTabsBookmarkFolderNameKey[] = "tabs"; + const char BookmarkCodec::kVersionKey[] = "version"; + const char BookmarkCodec::kChecksumKey[] = "checksum"; + const char BookmarkCodec::kIdKey[] = "id"; +@@ -75,7 +76,8 @@ BookmarkCodec::~BookmarkCodec() = default; + base::Value::Dict BookmarkCodec::Encode(BookmarkModel* model, + std::string sync_metadata_str) { + return Encode(model->bookmark_bar_node(), model->other_node(), +- model->mobile_node(), model->root_node()->GetMetaInfoMap(), ++ model->mobile_node(), model->tabs_collection_node(), ++ model->root_node()->GetMetaInfoMap(), + model->root_node()->GetUnsyncedMetaInfoMap(), + std::move(sync_metadata_str)); + } +@@ -84,6 +86,7 @@ base::Value::Dict BookmarkCodec::Encode( + const BookmarkNode* bookmark_bar_node, + const BookmarkNode* other_folder_node, + const BookmarkNode* mobile_folder_node, ++ const BookmarkNode* tabs_folder_node, + const BookmarkNode::MetaInfoMap* model_meta_info_map, + const BookmarkNode::MetaInfoMap* model_unsynced_meta_info_map, + std::string sync_metadata_str) { +@@ -105,6 +108,7 @@ base::Value::Dict BookmarkCodec::Encode( + roots.Set(kBookmarkBarFolderNameKey, EncodeNode(bookmark_bar_node)); + roots.Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node)); + roots.Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node)); ++ roots.Set(kTabsBookmarkFolderNameKey, EncodeNode(tabs_folder_node)); + if (model_meta_info_map) + roots.Set(kMetaInfo, EncodeMetaInfo(*model_meta_info_map)); + if (model_unsynced_meta_info_map) { +@@ -125,6 +129,7 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, + BookmarkNode* bb_node, + BookmarkNode* other_folder_node, + BookmarkNode* mobile_folder_node, ++ BookmarkNode* tabs_folder_node, + int64_t* max_id, + std::string* sync_metadata_str) { + ids_.clear(); +@@ -132,7 +137,8 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, + base::Uuid::ParseLowercase(kBookmarkBarNodeUuid), + base::Uuid::ParseLowercase(kOtherBookmarksNodeUuid), + base::Uuid::ParseLowercase(kMobileBookmarksNodeUuid), +- base::Uuid::ParseLowercase(kManagedNodeUuid)}; ++ base::Uuid::ParseLowercase(kManagedNodeUuid), ++ base::Uuid::ParseLowercase(kTabsCollectionBookmarksNodeUuid)}; + ids_reassigned_ = false; + uuids_reassigned_ = false; + ids_valid_ = true; +@@ -140,12 +146,13 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, + stored_checksum_.clear(); + InitializeChecksum(); + bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node, ++ tabs_folder_node, + value, sync_metadata_str); + FinalizeChecksum(); + // If either the checksums differ or some IDs were missing/not unique, + // reassign IDs. + if (!ids_valid_ || computed_checksum() != stored_checksum()) +- ReassignIDs(bb_node, other_folder_node, mobile_folder_node); ++ ReassignIDs(bb_node, other_folder_node, mobile_folder_node, tabs_folder_node); + *max_id = maximum_id_ + 1; + return success; + } +@@ -201,6 +208,7 @@ base::Value::Dict BookmarkCodec::EncodeMetaInfo( + bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, + BookmarkNode* other_folder_node, + BookmarkNode* mobile_folder_node, ++ BookmarkNode* tabs_folder_node, + const base::Value::Dict& value, + std::string* sync_metadata_str) { + absl::optional version = value.FindInt(kVersionKey); +@@ -225,6 +233,8 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, + roots->FindDict(kOtherBookmarkFolderNameKey); + const base::Value::Dict* mobile_folder_value = + roots->FindDict(kMobileBookmarkFolderNameKey); ++ const base::Value::Dict* tabs_folder_value = ++ roots->FindDict(kTabsBookmarkFolderNameKey); + + if (!bb_value || !other_folder_value || !mobile_folder_value) + return false; +@@ -232,6 +242,8 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, + DecodeNode(*bb_value, nullptr, bb_node); + DecodeNode(*other_folder_value, nullptr, other_folder_node); + DecodeNode(*mobile_folder_value, nullptr, mobile_folder_node); ++ if (tabs_folder_value) ++ DecodeNode(*tabs_folder_value, nullptr, tabs_folder_node); + + if (!DecodeMetaInfo(*roots, &model_meta_info_map_)) + return false; +@@ -497,11 +509,13 @@ void BookmarkCodec::DecodeMetaInfoHelper( + + void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node, + BookmarkNode* other_node, +- BookmarkNode* mobile_node) { ++ BookmarkNode* mobile_node, ++ BookmarkNode* tabs_folder_node) { + maximum_id_ = 0; + ReassignIDsHelper(bb_node); + ReassignIDsHelper(other_node); + ReassignIDsHelper(mobile_node); ++ ReassignIDsHelper(tabs_folder_node); + ids_reassigned_ = true; + } + +diff --git a/components/bookmarks/browser/bookmark_codec.h b/components/bookmarks/browser/bookmark_codec.h +index 930d0ad114572..da0e9c24c249c 100644 +--- a/components/bookmarks/browser/bookmark_codec.h ++++ b/components/bookmarks/browser/bookmark_codec.h +@@ -46,6 +46,7 @@ class BookmarkCodec { + const BookmarkNode* bookmark_bar_node, + const BookmarkNode* other_folder_node, + const BookmarkNode* mobile_folder_node, ++ const BookmarkNode* tabs_folder_node, + const BookmarkNode::MetaInfoMap* model_meta_info_map, + const BookmarkNode::MetaInfoMap* model_unsynced_meta_info_map, + std::string sync_metadata_str); +@@ -59,6 +60,7 @@ class BookmarkCodec { + BookmarkNode* bb_node, + BookmarkNode* other_folder_node, + BookmarkNode* mobile_folder_node, ++ BookmarkNode* tabs_folder_node, + int64_t* max_node_id, + std::string* sync_metadata_str); + +@@ -110,6 +112,7 @@ class BookmarkCodec { + // Allows the BookmarkClient to read and a write a string blob from the JSON + // file. That string captures the bookmarks sync metadata. + static const char kSyncMetadata[]; ++ static const char kTabsBookmarkFolderNameKey[]; + static const char kDateLastUsed[]; + + // Possible values for kTypeKey. +@@ -128,6 +131,7 @@ class BookmarkCodec { + bool DecodeHelper(BookmarkNode* bb_node, + BookmarkNode* other_folder_node, + BookmarkNode* mobile_folder_node, ++ BookmarkNode* tabs_folder_node, + const base::Value::Dict& value, + std::string* sync_metadata_str); + +@@ -139,7 +143,8 @@ class BookmarkCodec { + // Reassigns bookmark IDs for all nodes. + void ReassignIDs(BookmarkNode* bb_node, + BookmarkNode* other_node, +- BookmarkNode* mobile_node); ++ BookmarkNode* mobile_node, ++ BookmarkNode* tabs_folder_node); + + // Helper to recursively reassign IDs. + void ReassignIDsHelper(BookmarkNode* node); +diff --git a/components/bookmarks/browser/bookmark_load_details.cc b/components/bookmarks/browser/bookmark_load_details.cc +index 9a44f40824bed..73ccfbb9059f9 100644 +--- a/components/bookmarks/browser/bookmark_load_details.cc ++++ b/components/bookmarks/browser/bookmark_load_details.cc +@@ -37,6 +37,10 @@ BookmarkLoadDetails::BookmarkLoadDetails(BookmarkClient* client) + root_node_->Add(BookmarkPermanentNode::CreateMobileBookmarks( + max_id_++, + client->IsPermanentNodeVisibleWhenEmpty(BookmarkNode::MOBILE)))); ++ tabs_collection_folder_node_ = static_cast( ++ root_node_->Add(BookmarkPermanentNode::CreateTabsCollectionBookmarks( ++ max_id_++, ++ client->IsPermanentNodeVisibleWhenEmpty(BookmarkNode::TABS_COLLECTION)))); + } + + BookmarkLoadDetails::~BookmarkLoadDetails() = default; +diff --git a/components/bookmarks/browser/bookmark_load_details.h b/components/bookmarks/browser/bookmark_load_details.h +index eed55313469c8..f6145e2f1277e 100644 +--- a/components/bookmarks/browser/bookmark_load_details.h ++++ b/components/bookmarks/browser/bookmark_load_details.h +@@ -47,6 +47,7 @@ class BookmarkLoadDetails { + BookmarkPermanentNode* bb_node() { return bb_node_; } + BookmarkPermanentNode* mobile_folder_node() { return mobile_folder_node_; } + BookmarkPermanentNode* other_folder_node() { return other_folder_node_; } ++ BookmarkPermanentNode* tabs_collection_folder_node() { return tabs_collection_folder_node_; } + + TitledUrlIndex* index() { return index_.get(); } + std::unique_ptr owned_index() { return std::move(index_); } +@@ -112,6 +113,8 @@ class BookmarkLoadDetails { + nullptr; + raw_ptr mobile_folder_node_ = + nullptr; ++ raw_ptr tabs_collection_folder_node_ = ++ nullptr; + LoadManagedNodeCallback load_managed_node_callback_; + std::unique_ptr index_; + BookmarkNode::MetaInfoMap model_meta_info_map_; +diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc +index 162556334e2fc..aa13a067aeeb0 100644 +--- a/components/bookmarks/browser/bookmark_model.cc ++++ b/components/bookmarks/browser/bookmark_model.cc +@@ -818,7 +818,7 @@ bool BookmarkModel::HasBookmarks() { + bool BookmarkModel::HasNoUserCreatedBookmarksOrFolders() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return bookmark_bar_node_->children().empty() && +- other_node_->children().empty() && mobile_node_->children().empty(); ++ other_node_->children().empty() && mobile_node_->children().empty() && tabs_collection_node_->children().empty(); + } + + bool BookmarkModel::IsBookmarked(const GURL& url) { +@@ -1124,6 +1124,7 @@ void BookmarkModel::DoneLoading(std::unique_ptr details) { + bookmark_bar_node_ = details->bb_node(); + other_node_ = details->other_folder_node(); + mobile_node_ = details->mobile_folder_node(); ++ tabs_collection_node_ = details->tabs_collection_folder_node(); + + titled_url_index_->SetNodeSorter( + std::make_unique(client_.get())); +diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h +index 815a6cf5f7c4f..b36193dc864d3 100644 +--- a/components/bookmarks/browser/bookmark_model.h ++++ b/components/bookmarks/browser/bookmark_model.h +@@ -131,6 +131,12 @@ class BookmarkModel final : public BookmarkUndoProvider, + return mobile_node_; + } + ++ // Returns the 'mobile' node. This is NULL until loaded. ++ const BookmarkNode* tabs_collection_node() const { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ return tabs_collection_node_; ++ } ++ + bool is_root_node(const BookmarkNode* node) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return node == root_; +@@ -491,6 +497,7 @@ class BookmarkModel final : public BookmarkUndoProvider, + nullptr; + raw_ptr mobile_node_ = + nullptr; ++ raw_ptr tabs_collection_node_ = nullptr; + + // The maximum ID assigned to the bookmark nodes in the model. + int64_t next_node_id_ = 1; +diff --git a/components/bookmarks/browser/bookmark_node.cc b/components/bookmarks/browser/bookmark_node.cc +index 8b66e51afdcc1..d1876c4d152e1 100644 +--- a/components/bookmarks/browser/bookmark_node.cc ++++ b/components/bookmarks/browser/bookmark_node.cc +@@ -239,6 +239,17 @@ BookmarkPermanentNode::CreateMobileBookmarks(int64_t id, + visible_when_empty)); + } + ++// static ++std::unique_ptr ++BookmarkPermanentNode::CreateTabsCollectionBookmarks(int64_t id, ++ bool visible_when_empty) { ++ // base::WrapUnique() used because the constructor is private. ++ return base::WrapUnique(new BookmarkPermanentNode( ++ id, TABS_COLLECTION, base::Uuid::ParseLowercase(kTabsCollectionBookmarksNodeUuid), ++ l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_TABS_COLLECTION_FOLDER_NAME), ++ visible_when_empty)); ++} ++ + BookmarkPermanentNode::BookmarkPermanentNode(int64_t id, + Type type, + const base::Uuid& uuid, +diff --git a/components/bookmarks/browser/bookmark_node.h b/components/bookmarks/browser/bookmark_node.h +index 53a3d8c41ac53..875aba93dc1ae 100644 +--- a/components/bookmarks/browser/bookmark_node.h ++++ b/components/bookmarks/browser/bookmark_node.h +@@ -35,6 +35,7 @@ class BookmarkNode : public ui::TreeNode, public TitledUrlNode { + FOLDER, + BOOKMARK_BAR, + OTHER_NODE, ++ TABS_COLLECTION, + MOBILE + }; + +@@ -259,6 +260,9 @@ class BookmarkPermanentNode : public BookmarkNode { + static std::unique_ptr CreateMobileBookmarks( + int64_t id, + bool visible_when_empty); ++ static std::unique_ptr CreateTabsCollectionBookmarks( ++ int64_t id, ++ bool visible_when_empty); + + // Constructor is private to disallow the construction of permanent nodes + // other than the well-known ones, see factory methods. +diff --git a/components/bookmarks/browser/bookmark_uuids.cc b/components/bookmarks/browser/bookmark_uuids.cc +index c31fac979886b..4fc90832dd560 100644 +--- a/components/bookmarks/browser/bookmark_uuids.cc ++++ b/components/bookmarks/browser/bookmark_uuids.cc +@@ -33,6 +33,9 @@ const char kManagedNodeUuid[] = "323123f4-9381-5aee-80e6-ea5fca2f7672"; + // > uuid.uuid5(bookmarks_namespace, "shopping_collection") + const char kShoppingCollectionUuid[] = "e55c13d5-c108-52a6-bfd5-590b885500d0"; + ++const char kTabsCollectionBookmarksNodeUuid[] = ++ "00000000-0000-4000-a000-000000000006"; ++ + // This value is the result of exercising sync's function + // syncer::InferGuidForLegacyBookmark() with an empty input. + const char kBannedUuidDueToPastSyncBug[] = +diff --git a/components/bookmarks/browser/bookmark_uuids.h b/components/bookmarks/browser/bookmark_uuids.h +index c59b64038c5d4..27d632c471e3b 100644 +--- a/components/bookmarks/browser/bookmark_uuids.h ++++ b/components/bookmarks/browser/bookmark_uuids.h +@@ -14,6 +14,7 @@ extern const char kBookmarkBarNodeUuid[]; + extern const char kOtherBookmarksNodeUuid[]; + extern const char kMobileBookmarksNodeUuid[]; + extern const char kManagedNodeUuid[]; ++extern const char kTabsCollectionBookmarksNodeUuid[]; + extern const char kShoppingCollectionUuid[]; + + // A bug in sync caused some problematic UUIDs to be produced. +diff --git a/components/bookmarks/browser/model_loader.cc b/components/bookmarks/browser/model_loader.cc +index 7b19a6617fd89..d81817620b5f0 100644 +--- a/components/bookmarks/browser/model_loader.cc ++++ b/components/bookmarks/browser/model_loader.cc +@@ -60,7 +60,8 @@ void LoadBookmarks(const base::FilePath& path, + std::string sync_metadata_str; + BookmarkCodec codec; + codec.Decode(*root_dict, details->bb_node(), details->other_folder_node(), +- details->mobile_folder_node(), &max_node_id, ++ details->mobile_folder_node(), ++ details->tabs_collection_folder_node(), &max_node_id, + &sync_metadata_str); + details->set_sync_metadata_str(std::move(sync_metadata_str)); + details->set_max_id(std::max(max_node_id, details->max_id())); +diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc +index ca6a2752ac811..32b18cd55051f 100644 +--- a/components/sync_bookmarks/bookmark_specifics_conversions.cc ++++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc +@@ -447,6 +447,7 @@ sync_pb::BookmarkSpecifics::Type GetProtoTypeFromBookmarkNode( + case bookmarks::BookmarkNode::BOOKMARK_BAR: + case bookmarks::BookmarkNode::OTHER_NODE: + case bookmarks::BookmarkNode::MOBILE: ++ case bookmarks::BookmarkNode::TABS_COLLECTION: + DCHECK(node->is_folder()); + return sync_pb::BookmarkSpecifics::FOLDER; + } +-- +2.34.1 + diff --git a/build/bromite_patches/Add-menu-item-to-view-source.patch b/build/bromite_patches/Add-menu-item-to-view-source.patch new file mode 100644 index 0000000000000000000000000000000000000000..3361e91860a039123cba795627da5bedb261eae0 --- /dev/null +++ b/build/bromite_patches/Add-menu-item-to-view-source.patch @@ -0,0 +1,139 @@ +From e98c8856c303b8ed2d63608bd9c84712c9c9305b Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Mon, 13 Jul 2020 00:37:06 +0200 +Subject: [PATCH 107/192] Add menu item to view source + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../android/java/res/menu/custom_tabs_menu.xml | 3 +++ + chrome/android/java/res/menu/main_menu.xml | 3 +++ + .../chrome/browser/ChromeTabbedActivity.java | 2 ++ + .../chrome/browser/app/ChromeActivity.java | 5 +++++ + .../appmenu/AppMenuPropertiesDelegateImpl.java | 18 ++++++++++++++++++ + .../CustomTabAppMenuPropertiesDelegate.java | 1 + + .../android/strings/android_chrome_strings.grd | 4 ++++ + 7 files changed, 36 insertions(+) + +diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml +index 5cbabcc7c98d2..4a98471bffb07 100644 +--- a/chrome/android/java/res/menu/custom_tabs_menu.xml ++++ b/chrome/android/java/res/menu/custom_tabs_menu.xml +@@ -55,6 +55,9 @@ found in the LICENSE file. + ++ + +diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml +index 6bcd575f5c91e..6e7eff229907d 100644 +--- a/chrome/android/java/res/menu/main_menu.xml ++++ b/chrome/android/java/res/menu/main_menu.xml +@@ -111,6 +111,9 @@ found in the LICENSE file. + ++ + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +index e96e8955b97be..fa55eb8d43dc9 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +@@ -2313,6 +2313,8 @@ public class ChromeTabbedActivity extends ChromeActivity + return doOpenWebApk(currentTab); + } + ++ if (id == R.id.view_source_id) { ++ currentTab.getWebContents().getNavigationController().loadUrl(new LoadUrlParams("view-source:"+currentTab.getUrl().getSpec())); ++ return true; ++ } ++ + if (id == R.id.request_desktop_site_id || id == R.id.request_desktop_site_check_id) { + boolean usingDesktopUserAgent = + currentTab.getWebContents().getNavigationController().getUseDesktopUserAgent(); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java +index 211cd162129e7..4cf89cda35950 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java +@@ -552,6 +552,7 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate + isChromeScheme, isFileScheme, isContentScheme, isIncognito, url)); + + updateRequestDesktopSiteMenuItem(menu, currentTab, true /* can show */, isChromeScheme); ++ updateViewSourceMenuItem(menu, currentTab); + + updateAutoDarkMenuItem(menu, currentTab, isChromeScheme); + +@@ -1133,6 +1134,23 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate + } + } + ++ /** ++ * Updates the view source menu item's state. ++ * ++ * @param menu {@link Menu} for view source. ++ * @param currentTab Current tab being displayed. ++ */ ++ protected void updateViewSourceMenuItem( ++ Menu menu, Tab currentTab) { ++ MenuItem viewSourceMenuItem = menu.findItem(R.id.view_source_id); ++ boolean visible = false; ++ if (currentTab != null) { ++ String url = currentTab.getUrl().getSpec(); ++ visible = !url.isEmpty() && !url.startsWith("view-source:"); ++ } ++ viewSourceMenuItem.setVisible(visible); ++ } ++ + /** + * Updates the request desktop site item's state. + * +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +index ca00a6a29cacf..e8678081b4a7c 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +@@ -241,6 +241,7 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat + + updateRequestDesktopSiteMenuItem( + menu, currentTab, requestDesktopSiteVisible, isChromeScheme); ++ updateViewSourceMenuItem(menu, currentTab); + prepareAddToHomescreenMenuItem(menu, currentTab, addToHomeScreenVisible); + } + } +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index c004ab5d7a186..f874f81537b71 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -314,6 +314,10 @@ CHAR_LIMIT guidelines: + USB + + ++ ++ View source ++ ++ + + + You and Google +-- +2.34.1 + diff --git a/build/bromite_patches/Add-option-to-force-tablet-UI.patch b/build/bromite_patches/Add-option-to-force-tablet-UI.patch new file mode 100644 index 0000000000000000000000000000000000000000..8b77e71099ebe05d8a176769ceb8c664d2ae252a --- /dev/null +++ b/build/bromite_patches/Add-option-to-force-tablet-UI.patch @@ -0,0 +1,255 @@ +From 4f62d262b02c3bab7d7329009d21aadee3095dc5 Mon Sep 17 00:00:00 2001 +From: Wengling Chen +Date: Mon, 1 Feb 2021 19:18:55 +0200 +Subject: [PATCH 127/192] Add option to force tablet UI + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../ChromeAccessibilitySettingsDelegate.java | 19 +++++++++++++++++++ + .../preferences/ChromePreferenceKeys.java | 1 + + .../LegacyChromePreferenceKeys.java | 1 + + .../omnibox/LocationBarCoordinator.java | 2 +- + .../strings/android_chrome_strings.grd | 6 ++++++ + .../toolbar/top/ToolbarControlContainer.java | 5 ++--- + components/BUILD.gn | 6 +++--- + .../res/xml/accessibility_preferences.xml | 5 +++++ + .../accessibility/AccessibilitySettings.java | 10 ++++++++++ + .../AccessibilitySettingsDelegate.java | 2 ++ + ui/android/BUILD.gn | 2 ++ + .../chromium/ui/base/DeviceFormFactor.java | 5 +++++ + 12 files changed, 57 insertions(+), 7 deletions(-) + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java +index 16adf88cd50f7..7e35ced51b523 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java +@@ -77,6 +77,25 @@ public class ChromeAccessibilitySettingsDelegate implements AccessibilitySetting + return new ReaderForAccessibilityDelegate(mProfile); + } + ++ private static class ForceTabletUIDelegate implements BooleanPreferenceDelegate { ++ @Override ++ public boolean isEnabled() { ++ return SharedPreferencesManager.getInstance().readBoolean( ++ ChromePreferenceKeys.FLAGS_FORCE_TABLET_UI_ENABLED, false); ++ } ++ ++ @Override ++ public void setEnabled(boolean value) { ++ SharedPreferencesManager.getInstance().writeBoolean( ++ ChromePreferenceKeys.FLAGS_FORCE_TABLET_UI_ENABLED, value); ++ } ++ } ++ ++ @Override ++ public BooleanPreferenceDelegate getForceTabletUIDelegate() { ++ return new ForceTabletUIDelegate(); ++ } ++ + @Override + public void addExtraPreferences(PreferenceFragmentCompat fragment) { + if (ImageDescriptionsController.getInstance().shouldShowImageDescriptionsMenuItem()) { +diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +index a78a936c3177f..f920daba5c9c5 100644 +--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java ++++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +@@ -374,6 +374,7 @@ public final class ChromePreferenceKeys { + AccessibilityConstants.FONT_USER_SET_FORCE_ENABLE_ZOOM; + + public static final String HISTORY_SHOW_HISTORY_INFO = "history_home_show_info"; ++ public static final String FLAGS_FORCE_TABLET_UI_ENABLED = "force_tablet_ui_enabled"; + + /** Keys used to save settings related to homepage. */ + public static final String HOMEPAGE_CUSTOM_URI = "homepage_custom_uri"; +diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java +index 3663a9ee67edf..92f7a055d6bac 100644 +--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java ++++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java +@@ -65,6 +65,7 @@ public class LegacyChromePreferenceKeys { + ChromePreferenceKeys.FLAGS_CACHED_SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, + ChromePreferenceKeys.FLAGS_CACHED_TAB_GROUPS_ANDROID_ENABLED, + ChromePreferenceKeys.FONT_USER_FONT_SCALE_FACTOR, ++ ChromePreferenceKeys.FLAGS_FORCE_TABLET_UI_ENABLED, + ChromePreferenceKeys.FONT_USER_SET_FORCE_ENABLE_ZOOM, + ChromePreferenceKeys.HISTORY_SHOW_HISTORY_INFO, + ChromePreferenceKeys.HOMEPAGE_CUSTOM_URI, +diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java +index f6770c2ea02e1..57196951523dd 100644 +--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java ++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java +@@ -685,7 +685,7 @@ public class LocationBarCoordinator + } + + private boolean isTabletWindow() { +- return DeviceFormFactor.isWindowOnTablet(mWindowAndroid); ++ return DeviceFormFactor.isWindowOnTablet(mWindowAndroid) || isTabletLayout(); + } + + /* package */ LocationBarMediator getMediatorForTesting() { +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index 3198555c554c0..a93ae631ae6a8 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -1702,6 +1702,12 @@ Your Google account may have other forms of browsing history like searches and a + + Temporarily links this data to your Google Account when you’re signed in, to protect you across Google apps + ++ ++ Open chromium in Tablet Mode ++ ++ ++ Force Tablet Mode ++ + + + +diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java +index 1635f445e6314..d5b4038172800 100644 +--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java ++++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java +@@ -166,9 +166,8 @@ public class ToolbarControlContainer extends OptimizedFrameLayout implements Con + + return backgroundDrawable; + } else { +- final Drawable backgroundDrawable = +- AppCompatResources.getDrawable(getContext(), R.drawable.toolbar_background) +- .mutate(); ++ Drawable backgroundDrawable = new ColorDrawable( ++ ChromeColors.getPrimaryBackgroundColor(getContext(), incognito)); + backgroundDrawable.setTint(ChromeColors.getDefaultThemeColor(getContext(), incognito)); + backgroundDrawable.setTintMode(PorterDuff.Mode.MULTIPLY); + +diff --git a/components/BUILD.gn b/components/BUILD.gn +index 9ae45227c765a..08b765a18c9b2 100644 +--- a/components/BUILD.gn ++++ b/components/BUILD.gn +@@ -68,7 +68,7 @@ if (is_ios) { + + # Omit Lacros because it allows //components to depend on //chrome, which in + # turn depends on //extensions. +-if (!is_chromeos_lacros) { ++if (!is_chromeos_lacros && !is_android) { + disallowed_extension_deps_ = [ + # Components should largely not depend on //extensions. Since // extensions + # is not a component target and is linked with //chrome, depending on most +@@ -736,7 +736,7 @@ test("components_unittests") { + # On other platforms, no components should depend on Chrome. + # Since //chrome depends on //extensions, we also only assert_no_deps on + # extensions targets for non-lacros builds. +- if (!is_chromeos_lacros) { ++ if (!is_chromeos_lacros && !is_android) { + assert_no_deps = [ "//chrome/*" ] + assert_no_deps += disallowed_extension_deps_ + } +@@ -1025,7 +1025,7 @@ if (use_blink) { + # dependency. On other platforms, no components should depend on Chrome. + # Since //chrome depends on //extensions, we also only assert_no_deps on + # extensions targets for non-lacros builds. +- if (!is_chromeos_lacros) { ++ if (!is_chromeos_lacros && !is_android) { + assert_no_deps = [ "//chrome/*" ] + assert_no_deps += disallowed_extension_deps_ + } +diff --git a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml +index 04e522c74af48..2fc5bcc4e2976 100644 +--- a/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml ++++ b/components/browser_ui/accessibility/android/java/res/xml/accessibility_preferences.xml +@@ -36,6 +36,11 @@ found in the LICENSE file. + android:summary="@string/accessibility_tab_switcher_summary" + android:title="@string/accessibility_tab_switcher_title" /> + ++ ++ + +diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java +index 77f841d447d7e..2e582921f433d 100644 +--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java ++++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettings.java +@@ -30,6 +30,8 @@ public class AccessibilitySettings extends PreferenceFragmentCompat + public static final String PREF_READER_FOR_ACCESSIBILITY = "reader_for_accessibility"; + public static final String PREF_CAPTIONS = "captions"; + ++ private BooleanPreferenceDelegate mForceTabletUIDelegate; ++ static final String PREF_FORCE_TABLET_UI = "force_tablet_ui"; + private TextScalePreference mTextScalePref; + private PageZoomPreference mPageZoomDefaultZoomPref; + private ChromeSwitchPreference mPageZoomAlwaysShowPref; +@@ -109,6 +111,12 @@ public class AccessibilitySettings extends PreferenceFragmentCompat + getPreferenceScreen().removePreference(readerForAccessibilityPref); + } + ++ ChromeSwitchPreference forceTabletUiPref = ++ (ChromeSwitchPreference) findPreference(PREF_FORCE_TABLET_UI); ++ mForceTabletUIDelegate = mDelegate.getForceTabletUIDelegate(); ++ forceTabletUiPref.setChecked(mForceTabletUIDelegate.isEnabled()); ++ forceTabletUiPref.setOnPreferenceChangeListener(this); ++ + ChromeSwitchPreference accessibilityTabSwitcherPref = + (ChromeSwitchPreference) findPreference( + AccessibilityConstants.ACCESSIBILITY_TAB_SWITCHER); +@@ -165,6 +173,8 @@ public class AccessibilitySettings extends PreferenceFragmentCompat + mFontSizePrefs.setUserFontScaleFactor((Float) newValue); + } else if (PREF_FORCE_ENABLE_ZOOM.equals(preference.getKey())) { + mFontSizePrefs.setForceEnableZoomFromUser((Boolean) newValue); ++ } else if (PREF_FORCE_TABLET_UI.equals(preference.getKey())) { ++ mForceTabletUIDelegate.setEnabled((Boolean) newValue); + } else if (PREF_READER_FOR_ACCESSIBILITY.equals(preference.getKey())) { + if (mReaderForAccessibilityDelegate != null) { + mReaderForAccessibilityDelegate.setEnabled((Boolean) newValue); +diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java +index f1a4f4a02c056..41a0726e9bad8 100644 +--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java ++++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/AccessibilitySettingsDelegate.java +@@ -44,6 +44,8 @@ public interface AccessibilitySettingsDelegate { + */ + BooleanPreferenceDelegate getReaderForAccessibilityDelegate(); + ++ BooleanPreferenceDelegate getForceTabletUIDelegate(); ++ + /** + * Allows the embedder to add more preferences to the preference screen. + * +diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn +index f49b09257e4e7..0bba2850d5773 100644 +--- a/ui/android/BUILD.gn ++++ b/ui/android/BUILD.gn +@@ -409,6 +409,8 @@ android_library("ui_no_recycler_view_java") { + ":ui_android_features_java", + ":ui_java_resources", + ":ui_utils_java", ++ # dependency for ui_no_recycler_view_java ++ "//chrome/browser/preferences:java", + "//base:base_java", + "//base:jni_java", + "//build/android:build_java", +diff --git a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java +index b7efa9f648f31..3df825d11052e 100644 +--- a/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java ++++ b/ui/android/java/src/org/chromium/ui/base/DeviceFormFactor.java +@@ -12,6 +12,8 @@ import androidx.annotation.VisibleForTesting; + import org.chromium.base.ContextUtils; + import org.chromium.base.ThreadUtils; + import org.chromium.base.annotations.CalledByNative; ++import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; ++import org.chromium.chrome.browser.preferences.SharedPreferencesManager; + import org.chromium.ui.R; + import org.chromium.ui.display.DisplayAndroid; + import org.chromium.ui.display.DisplayUtil; +@@ -62,6 +64,9 @@ public class DeviceFormFactor { + * E.g. http://developer.samsung.com/samsung-dex/testing + */ + public static boolean isNonMultiDisplayContextOnTablet(Context context) { ++ if (SharedPreferencesManager.getInstance().readBoolean( ++ ChromePreferenceKeys.FLAGS_FORCE_TABLET_UI_ENABLED, false)) ++ return true; + return detectScreenWidthBucket(context) >= SCREEN_BUCKET_TABLET; + } + +-- +2.34.1 + diff --git a/build/bromite_patches/Add-option-to-not-persist-tabs-across-sessions.patch b/build/bromite_patches/Add-option-to-not-persist-tabs-across-sessions.patch new file mode 100644 index 0000000000000000000000000000000000000000..4aecac494c43ca9a6b188495b95ec63dccb40cbb --- /dev/null +++ b/build/bromite_patches/Add-option-to-not-persist-tabs-across-sessions.patch @@ -0,0 +1,128 @@ +From 0652be489d2c11a83862158b89bee4c3506e22dc Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 7 Sep 2019 15:07:42 +0200 +Subject: [PATCH 065/192] Add option to not persist tabs across sessions + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../android/java/res/xml/privacy_preferences.xml | 5 +++++ + .../chrome/browser/ChromeTabbedActivity.java | 5 ++++- + .../privacy/settings/PrivacySettings.java | 16 +++++++++++++++- + .../android/strings/android_chrome_strings.grd | 6 ++++++ + 4 files changed, 30 insertions(+), 2 deletions(-) + +diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml +index 31168cba63391..4c71a1e2ba2ab 100644 +--- a/chrome/android/java/res/xml/privacy_preferences.xml ++++ b/chrome/android/java/res/xml/privacy_preferences.xml +@@ -55,6 +55,11 @@ found in the LICENSE file. + android:title="@string/incognito_settings_title" + android:summary="@string/incognito_settings_summary" + android:fragment="org.chromium.chrome.browser.privacy.settings.IncognitoSettings"/> ++ + <resetlink>reset sync

+ IMAGE + ++ ++ Close all open tabs on exit ++ ++ ++ Don't persist tabs between browsing sessions ++ + + LINK + +-- +2.34.1 + diff --git a/build/bromite_patches/Add-option-to-use-home-page-as-NTP.patch b/build/bromite_patches/Add-option-to-use-home-page-as-NTP.patch new file mode 100644 index 0000000000000000000000000000000000000000..e22cfc81d5ee2ad3a28cde85ead667eccf577c37 --- /dev/null +++ b/build/bromite_patches/Add-option-to-use-home-page-as-NTP.patch @@ -0,0 +1,262 @@ +From b4f05e2c0dea7f0e9b3d8e89c35c92fbfd83c524 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 20 Nov 2021 15:36:54 +0000 +Subject: [PATCH 118/192] Add option to use home page as NTP + +And allow use about:blank as default homepage + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../tab_management/TabGridDialogMediator.java | 7 ++++++- + .../tasks/tab_management/TabGroupUiMediator.java | 7 ++++++- + .../java/res/xml/homepage_preferences.xml | 5 +++++ + .../chrome/browser/homepage/HomepageManager.java | 16 ++++++++++++++++ + .../homepage/settings/HomepageSettings.java | 12 ++++++++++++ + .../chrome/browser/metrics/LaunchMetrics.java | 1 - + .../browser/tabmodel/ChromeTabCreator.java | 7 +++++++ + .../preferences/ChromePreferenceKeys.java | 1 + + .../preferences/LegacyChromePreferenceKeys.java | 1 + + .../android/strings/android_chrome_strings.grd | 3 +++ + chrome/browser/ui/browser_ui_prefs.cc | 2 ++ + chrome/common/pref_names.h | 4 ++++ + 12 files changed, 63 insertions(+), 3 deletions(-) + +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java +index 5f12387c05368..f29c86612efb8 100644 +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java +@@ -18,6 +18,7 @@ import org.chromium.base.Callback; + import org.chromium.base.ResettersForTesting; + import org.chromium.base.metrics.RecordUserAction; + import org.chromium.base.supplier.Supplier; ++import org.chromium.chrome.browser.homepage.HomepageManager; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tab.TabCreationState; + import org.chromium.chrome.browser.tab.TabLaunchType; +@@ -578,9 +579,13 @@ public class TabGridDialogMediator + + assert relatedTabs.size() > 0; + ++ String url = UrlConstants.NTP_URL; ++ if (HomepageManager.getInstance().getPrefNTPIsHomepageEnabled()) { ++ url = HomepageManager.getInstance().getHomepageUri(); ++ } + Tab parentTabToAttach = relatedTabs.get(relatedTabs.size() - 1); + mTabCreatorManager.getTabCreator(currentTab.isIncognito()) +- .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL), ++ .createNewTab(new LoadUrlParams(url), + TabLaunchType.FROM_TAB_GROUP_UI, parentTabToAttach); + RecordUserAction.record("MobileNewTabOpened." + mComponentName); + }; +diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java +index 3765322c8f0c0..fa3ddb43ddf8c 100644 +--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java ++++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java +@@ -17,6 +17,7 @@ import org.chromium.base.metrics.RecordUserAction; + import org.chromium.base.supplier.ObservableSupplier; + import org.chromium.base.supplier.ObservableSupplierImpl; + import org.chromium.base.supplier.OneshotSupplier; ++import org.chromium.chrome.browser.homepage.HomepageManager; + import org.chromium.chrome.browser.layouts.LayoutStateProvider; + import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver; + import org.chromium.chrome.browser.layouts.LayoutType; +@@ -350,8 +351,12 @@ public class TabGroupUiMediator implements BackPressHandler { + + parentTabToAttach = relatedTabs.get(relatedTabs.size() - 1); + } ++ String url = UrlConstants.NTP_URL; ++ if (HomepageManager.getInstance().getPrefNTPIsHomepageEnabled()) { ++ url = HomepageManager.getInstance().getHomepageUri(); ++ } + mTabCreatorManager.getTabCreator(currentTab.isIncognito()) +- .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL), ++ .createNewTab(new LoadUrlParams(url), + TabLaunchType.FROM_TAB_GROUP_UI, parentTabToAttach); + RecordUserAction.record("MobileNewTabOpened." + TabGroupUiCoordinator.COMPONENT_NAME); + }; +diff --git a/chrome/android/java/res/xml/homepage_preferences.xml b/chrome/android/java/res/xml/homepage_preferences.xml +index 3796a71e33826..ad7282896808e 100644 +--- a/chrome/android/java/res/xml/homepage_preferences.xml ++++ b/chrome/android/java/res/xml/homepage_preferences.xml +@@ -14,6 +14,11 @@ found in the LICENSE file. + android:summaryOn="@string/text_on" + android:summaryOff="@string/text_off" /> + ++ ++ + { ++ mHomepageManager.setPrefNTPIsHomepageEnabled((boolean) newValue); ++ return true; ++ }); ++ + RecordUserAction.record("Settings.Homepage.Opened"); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java +index 40f30858f0180..2849b22a08148 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/LaunchMetrics.java +@@ -103,7 +103,6 @@ public class LaunchMetrics { + boolean showHomeButton, boolean homepageIsNtp, String homepageUrl) { + if (homepageUrl == null) { + homepageUrl = ""; +- assert !showHomeButton : "Homepage should be disabled for a null URL"; + } + LaunchMetricsJni.get().recordHomePageLaunchMetrics( + showHomeButton, homepageIsNtp, homepageUrl); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +index 4300ad0894ab0..acf440dfd0a36 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java +@@ -26,6 +26,7 @@ import org.chromium.chrome.browser.app.tab_activity_glue.ReparentingTask; + import org.chromium.chrome.browser.compositor.CompositorViewHolder; + import org.chromium.chrome.browser.flags.ChromeFeatureList; + import org.chromium.chrome.browser.incognito.IncognitoUtils; ++import org.chromium.chrome.browser.homepage.HomepageManager; + import org.chromium.chrome.browser.ntp.NewTabPageLaunchOrigin; + import org.chromium.chrome.browser.ntp.NewTabPageUtils; + import org.chromium.chrome.browser.prefetch.settings.PreloadPagesSettingsBridge; +@@ -472,6 +473,12 @@ public class ChromeTabCreator extends TabCreator { + * @return the created tab. + */ + public Tab launchUrl(String url, @TabLaunchType int type, Intent intent, long intentTimestamp) { ++ if (!mIncognito && url.equals(UrlConstants.NTP_URL)) { ++ if (HomepageManager.getInstance().getPrefNTPIsHomepageEnabled()) { ++ url = HomepageManager.getInstance().getHomepageUri(); ++ } ++ } ++ + LoadUrlParams loadUrlParams = new LoadUrlParams(url); + loadUrlParams.setIntentReceivedTimestamp(intentTimestamp); + return createNewTab(loadUrlParams, type, null, intent); +diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +index 8596c3022dcb3..a78a936c3177f 100644 +--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java ++++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java +@@ -385,6 +385,7 @@ public final class ChromePreferenceKeys { + public static final String HOMEPAGE_PARTNER_CUSTOMIZED_DEFAULT_GURL = + "Chrome.Homepage.PartnerCustomizedDefaultGurl"; + ++ public static final String HOMEPAGE_NTP_IS_HOMEPAGE = "newtabpage_is_homepage"; + /** + * Key used to save homepage location set by enterprise policy + */ +diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java +index 8eecb99b97f6f..3663a9ee67edf 100644 +--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java ++++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/LegacyChromePreferenceKeys.java +@@ -70,6 +70,7 @@ public class LegacyChromePreferenceKeys { + ChromePreferenceKeys.HOMEPAGE_CUSTOM_URI, + ChromePreferenceKeys.HOMEPAGE_ENABLED, + ChromePreferenceKeys.HOMEPAGE_USE_DEFAULT_URI, ++ ChromePreferenceKeys.HOMEPAGE_NTP_IS_HOMEPAGE, + ChromePreferenceKeys.INCOGNITO_SHORTCUT_ADDED, + ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION, + ChromePreferenceKeys.LOCALE_MANAGER_AUTO_SWITCH, +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index f874f81537b71..6ae11fc0b71ef 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -1514,6 +1514,9 @@ Your Google account may have other forms of browsing history like searches and a + + Last hour + ++ ++ Use for new tabs ++ + + Last 24 hours + +diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc +index 3024320b2d014..3ce76a2ae9827 100644 +--- a/chrome/browser/ui/browser_ui_prefs.cc ++++ b/chrome/browser/ui/browser_ui_prefs.cc +@@ -65,6 +65,8 @@ void RegisterBrowserUserPrefs(user_prefs::PrefRegistrySyncable* registry) { + GetHomeButtonAndHomePageIsNewTabPageFlags()); + registry->RegisterBooleanPref(prefs::kShowHomeButton, false, + GetHomeButtonAndHomePageIsNewTabPageFlags()); ++ registry->RegisterBooleanPref(prefs::kNewTabPageIsHomePage, false, ++ GetHomeButtonAndHomePageIsNewTabPageFlags()); + + registry->RegisterInt64Pref(prefs::kDefaultBrowserLastDeclined, 0); + bool reset_check_default = false; +diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h +index 03237079edc8d..caa31c9e129df 100644 +--- a/chrome/common/pref_names.h ++++ b/chrome/common/pref_names.h +@@ -1310,6 +1310,10 @@ inline constexpr char kUsedPolicyCertificates[] = + // visible on the toolbar. + inline constexpr char kShowHomeButton[] = "browser.show_home_button"; + ++// A boolean specifying whether opening a new tab should open the Home page ++// instead of the New Tab page. ++inline constexpr char kNewTabPageIsHomePage[] = "newtabpage_is_homepage"; ++ + // Boolean pref to define the default setting for "block offensive words". + // The old key value is kept to avoid unnecessary migration code. + inline constexpr char kSpeechRecognitionFilterProfanities[] = +-- +2.34.1 + diff --git a/build/bromite_patches/Add-site-engagement-flag.patch b/build/bromite_patches/Add-site-engagement-flag.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d9ddb38e0b99af566c2b1952e325bdc701634a0 --- /dev/null +++ b/build/bromite_patches/Add-site-engagement-flag.patch @@ -0,0 +1,195 @@ +From fcc9d8969aca34d7f82d0270de79a70a33ab30c9 Mon Sep 17 00:00:00 2001 +From: uazo +Date: Mon, 2 May 2022 11:48:03 +0000 +Subject: [PATCH 170/192] Add site engagement flag + +Disabled by default. + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 6 ++++ + chrome/browser/flag_descriptions.cc | 5 +++ + chrome/browser/flag_descriptions.h | 3 ++ + .../content/site_engagement_score.cc | 5 +++ + components/site_engagement/core/BUILD.gn | 6 ++++ + components/site_engagement/core/features.cc | 29 ++++++++++++++++ + components/site_engagement/core/features.h | 34 +++++++++++++++++++ + 7 files changed, 88 insertions(+) + create mode 100644 components/site_engagement/core/features.cc + create mode 100644 components/site_engagement/core/features.h + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index bc1a9f986ff8c..1a967fcb42c29 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -144,6 +144,7 @@ + #include "components/segmentation_platform/public/features.h" + #include "components/send_tab_to_self/features.h" + #include "components/services/heap_profiling/public/cpp/switches.h" ++#include "components/site_engagement/core/features.h" + #include "components/shared_highlighting/core/common/shared_highlighting_features.h" + #include "components/signin/core/browser/dice_account_reconcilor_delegate.h" + #include "components/signin/public/base/signin_buildflags.h" +@@ -9564,6 +9565,11 @@ const FeatureEntry kFeatureEntries[] = { + kLargeFaviconFromGoogleVariations, + "LargeFaviconFromGoogle")}, + ++ {"site-engagement", ++ flag_descriptions::kSiteEngagementName, ++ flag_descriptions::kSiteEngagementDescription, kOsAll, ++ FEATURE_VALUE_TYPE(site_engagement::features::kSiteEngagement)}, ++ + #if BUILDFLAG(IS_ANDROID) + {"force-off-text-autosizing", + flag_descriptions::kForceOffTextAutosizingName, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index 1c8b73b87da7f..df16e84247120 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -3770,6 +3770,11 @@ const char kSkipServiceWorkerFetchHandlerDescription[] = + "Skips starting the service worker and run the fetch handler if the fetch " + "handler is recognized as skippable."; + ++const char kSiteEngagementName[] = ++ "Enable site engagement feature"; ++const char kSiteEngagementDescription[] = ++ "Site Engagement Service provides information about how engaged a user is with a origin; this affects which NTP tiles are automatically created."; ++ + const char kWebSQLAccessName[] = "Allows access to WebSQL APIs"; + const char kWebSQLAccessDescription[] = + "The WebSQL API is enabled by default, but can be disabled here."; +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 39c1bc44ac2ef..269c8f89b2640 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -2198,6 +2198,9 @@ extern const char kReduceAcceptLanguageDescription[]; + extern const char kSkipServiceWorkerFetchHandlerName[]; + extern const char kSkipServiceWorkerFetchHandlerDescription[]; + ++extern const char kSiteEngagementName[]; ++extern const char kSiteEngagementDescription[]; ++ + extern const char kWebSQLAccessName[]; + extern const char kWebSQLAccessDescription[]; + +diff --git a/components/site_engagement/content/site_engagement_score.cc b/components/site_engagement/content/site_engagement_score.cc +index b5f8f39dca4b8..18b3ba8919005 100644 +--- a/components/site_engagement/content/site_engagement_score.cc ++++ b/components/site_engagement/content/site_engagement_score.cc +@@ -18,6 +18,7 @@ + #include "components/content_settings/core/common/content_settings.h" + #include "components/content_settings/core/common/content_settings_types.h" + #include "components/content_settings/core/common/content_settings_utils.h" ++#include "components/site_engagement/core/features.h" + #include "components/site_engagement/content/engagement_type.h" + #include "components/site_engagement/content/site_engagement_metrics.h" + #include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom.h" +@@ -275,6 +276,10 @@ void SiteEngagementScore::Commit() { + if (!UpdateScoreDict(*score_dict_)) + return; + ++ if (!base::FeatureList::IsEnabled(features::kSiteEngagement)) { ++ score_dict_.reset(); ++ return; ++ } + settings_map_->SetWebsiteSettingDefaultScope( + origin_, GURL(), ContentSettingsType::SITE_ENGAGEMENT, + base::Value(std::move(*score_dict_))); +diff --git a/components/site_engagement/core/BUILD.gn b/components/site_engagement/core/BUILD.gn +index 4975a87fc555c..307b7a27c29a0 100644 +--- a/components/site_engagement/core/BUILD.gn ++++ b/components/site_engagement/core/BUILD.gn +@@ -4,8 +4,14 @@ + + static_library("core") { + sources = [ ++ "features.cc", ++ "features.h", + "pref_names.cc", + "pref_names.h", + "site_engagement_score_provider.h", + ] ++ ++ deps = [ ++ "//base", ++ ] + } +diff --git a/components/site_engagement/core/features.cc b/components/site_engagement/core/features.cc +new file mode 100644 +index 0000000000000..a8c8bed9df7b6 +--- /dev/null ++++ b/components/site_engagement/core/features.cc +@@ -0,0 +1,29 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++#include "components/site_engagement/core/features.h" ++ ++#include "base/feature_list.h" ++ ++namespace site_engagement { ++namespace features { ++ ++const base::Feature kSiteEngagement{"SiteEngagement", ++ base::FEATURE_DISABLED_BY_DEFAULT}; ++ ++} // namespace features ++} // namespace site_engagement +diff --git a/components/site_engagement/core/features.h b/components/site_engagement/core/features.h +new file mode 100644 +index 0000000000000..2eaf1540dca6b +--- /dev/null ++++ b/components/site_engagement/core/features.h +@@ -0,0 +1,34 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++#ifndef SITE_ENGAGEMENT_CORE_FEATURES_H_ ++#define SITE_ENGAGEMENT_CORE_FEATURES_H_ ++ ++#include ++ ++#include "base/feature_list.h" ++ ++namespace site_engagement { ++namespace features { ++ ++// Enable site engagement ++extern const base::Feature kSiteEngagement; ++ ++} // namespace features ++} // namespace site_engagement ++ ++#endif // SITE_ENGAGEMENT_CORE_FEATURES_H_ +-- +2.34.1 + diff --git a/build/bromite_patches/Add-support-for-ISupportHelpAndFeedback.patch b/build/bromite_patches/Add-support-for-ISupportHelpAndFeedback.patch new file mode 100644 index 0000000000000000000000000000000000000000..d23b3f40507139fdf3035da03ab80c5412ba9d45 --- /dev/null +++ b/build/bromite_patches/Add-support-for-ISupportHelpAndFeedback.patch @@ -0,0 +1,55 @@ +From d4c8c3a1e2acb49f8f714c30edf51dc32f59c956 Mon Sep 17 00:00:00 2001 +From: uazo +Date: Mon, 17 May 2021 12:30:12 +0000 +Subject: [PATCH 015/192] Add support for ISupportHelpAndFeedback + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../chrome/browser/settings/SettingsActivity.java | 9 +++++++-- + .../components/browser_ui/settings/SettingsUtils.java | 4 ++++ + 2 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +index fb1b2661cbb45..130443d449f84 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java +@@ -83,6 +83,7 @@ import org.chromium.components.browser_ui.settings.CustomDividerFragment; + import org.chromium.components.browser_ui.settings.FragmentSettingsLauncher; + import org.chromium.components.browser_ui.settings.PaddedDividerItemDecoration; + import org.chromium.components.browser_ui.settings.SettingsLauncher; ++import org.chromium.components.browser_ui.settings.SettingsUtils; + import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; + import org.chromium.components.browser_ui.site_settings.SiteSettingsPreferenceFragment; + import org.chromium.components.browser_ui.util.TraceEventVectorDrawableCompat; +@@ -398,8 +399,12 @@ public class SettingsActivity extends ChromeBaseAppCompatActivity + finish(); + return true; + } else if (item.getItemId() == R.id.menu_id_general_help) { +- HelpAndFeedbackLauncherImpl.getForProfile(mProfile).show( +- this, getString(R.string.help_context_settings), null); ++ if (mainFragment instanceof SettingsUtils.ISupportHelpAndFeedback) { ++ ((SettingsUtils.ISupportHelpAndFeedback)mainFragment).onHelpAndFeebackPressed(); ++ } else { ++ HelpAndFeedbackLauncherImpl.getForProfile(mProfile).show( ++ this, getString(R.string.help_context_settings), null); ++ } + return true; + } + return super.onOptionsItemSelected(item); +diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java +index 2c2340191f930..65975f026fa67 100644 +--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java ++++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java +@@ -97,4 +97,8 @@ public class SettingsUtils { + ToolbarUtils.setOverflowMenuVisibility(toolbar, visibility); + return true; + } ++ ++ public interface ISupportHelpAndFeedback { ++ void onHelpAndFeebackPressed(); ++ } + } +-- +2.34.1 + diff --git a/build/bromite_patches/Add-support-for-writing-URIs.patch b/build/bromite_patches/Add-support-for-writing-URIs.patch new file mode 100644 index 0000000000000000000000000000000000000000..3261c41940af3e37d97dc36eebb87ddcadc8fac7 --- /dev/null +++ b/build/bromite_patches/Add-support-for-writing-URIs.patch @@ -0,0 +1,105 @@ +From ba6f4dedcf923b69c13393abd5c862d2f9f6cb21 Mon Sep 17 00:00:00 2001 +From: uazo +Date: Tue, 12 Apr 2022 15:58:01 +0000 +Subject: [PATCH 060/192] Add support for writing URIs + +Allows native-side URI file writing + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + base/android/content_uri_utils.cc | 10 ++++++ + base/android/content_uri_utils.h | 4 +++ + .../org/chromium/base/ContentUriUtils.java | 33 +++++++++++++++++++ + 3 files changed, 47 insertions(+) + +diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc +index 2ab50fcc48832..300e79d86d9a8 100644 +--- a/base/android/content_uri_utils.cc ++++ b/base/android/content_uri_utils.cc +@@ -30,6 +30,16 @@ File OpenContentUriForRead(const FilePath& content_uri) { + return File(fd); + } + ++File OpenContentUriForWrite(const FilePath& content_uri) { ++ JNIEnv* env = base::android::AttachCurrentThread(); ++ ScopedJavaLocalRef j_uri = ++ ConvertUTF8ToJavaString(env, content_uri.value()); ++ jint fd = Java_ContentUriUtils_openContentUriForWrite(env, j_uri); ++ if (fd < 0) ++ return File(); ++ return File(fd); ++} ++ + std::string GetContentUriMimeType(const FilePath& content_uri) { + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef j_uri = +diff --git a/base/android/content_uri_utils.h b/base/android/content_uri_utils.h +index 33665f6cf70aa..ce9a6d75ab424 100644 +--- a/base/android/content_uri_utils.h ++++ b/base/android/content_uri_utils.h +@@ -18,6 +18,10 @@ namespace base { + // Returns -1 if the URI is invalid. + BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri); + ++// Opens a content URI for write and returns the file descriptor to the caller. ++// Returns -1 if the URI is invalid. ++BASE_EXPORT File OpenContentUriForWrite(const FilePath& content_uri); ++ + // Check whether a content URI exists. + BASE_EXPORT bool ContentUriExists(const FilePath& content_uri); + +diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java +index 27501317a8206..cfef594067d00 100644 +--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java ++++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java +@@ -23,6 +23,9 @@ import org.chromium.base.annotations.CalledByNative; + import java.io.File; + import java.io.IOException; + ++import android.system.Os; ++import android.content.ContentProviderClient; ++ + /** + * This class provides methods to access content URI schemes. + */ +@@ -89,6 +92,36 @@ public abstract class ContentUriUtils { + return -1; + } + ++ @CalledByNative ++ public static int openContentUriForWrite(String uriString) { ++ try { ++ Uri uri = Uri.parse(uriString); ++ ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver(); ++ ContentProviderClient client = resolver.acquireContentProviderClient( ++ uri.getAuthority()); ++ ParcelFileDescriptor pfd = client.openFile(uri, "rw"); ++ int fd = pfd.detachFd(); ++ client.close(); ++ return fd; ++ } catch (Exception e) { ++ Log.e(TAG, "Cannot open intermediate URI", e); ++ } ++ return -1; ++ } ++ ++ public static String getFilePathFromContentUri(Uri uri) { ++ String path = null; ++ try { ++ ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver(); ++ ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); ++ path = Os.readlink("/proc/self/fd/" + pfd.getFd()); ++ pfd.close(); ++ } catch (Exception e) { ++ Log.e(TAG, "Cannot get file path from content URI", e); ++ } ++ return path; ++ } ++ + /** + * Check whether a content URI exists. + * +-- +2.34.1 + diff --git a/build/bromite_patches/Add-webGL-site-setting.patch b/build/bromite_patches/Add-webGL-site-setting.patch new file mode 100644 index 0000000000000000000000000000000000000000..b001dfd0b7bb31df2ada515c9e8ae474ab8c886a --- /dev/null +++ b/build/bromite_patches/Add-webGL-site-setting.patch @@ -0,0 +1,296 @@ +From 8e60b0aa533ff7ba976004f54a81f00de853cd6a Mon Sep 17 00:00:00 2001 +From: uazo +Date: Tue, 3 May 2022 14:44:11 +0000 +Subject: [PATCH 019/192] Add webGL site setting + +Do not provide any device information when serving context creation errors. + +Requires patch: Content-settings-infrastructure.patch + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../impl/BromiteWebGLContentSetting.java | 92 +++++++++++++++++++ + .../bromite_content_settings/webgl.grdp | 27 ++++++ + .../bromite_content_settings/webgl.inc | 22 +++++ + .../common/bromite_content_settings/WEBGL.inc | 1 + + .../execution_context/execution_context.cc | 7 ++ + .../execution_context/execution_context.h | 2 + + .../webgl/webgl_rendering_context_base.cc | 32 +++---- + .../webgl/webgl_rendering_context_base.h | 2 + + 8 files changed, 166 insertions(+), 19 deletions(-) + create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebGLContentSetting.java + create mode 100644 components/browser_ui/strings/bromite_content_settings/webgl.grdp + create mode 100644 components/content_settings/core/browser/bromite_content_settings/webgl.inc + create mode 100644 components/content_settings/core/common/bromite_content_settings/WEBGL.inc + +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebGLContentSetting.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebGLContentSetting.java +new file mode 100644 +index 0000000000000..f7bb6433b714d +--- /dev/null ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebGLContentSetting.java +@@ -0,0 +1,92 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++package org.chromium.components.browser_ui.site_settings.impl; ++ ++import org.chromium.components.browser_ui.site_settings.R; ++ ++import org.chromium.components.browser_ui.site_settings.BromiteCustomContentSetting; ++import org.chromium.components.browser_ui.site_settings.ContentSettingsResources; ++import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; ++import org.chromium.components.content_settings.ContentSettingValues; ++import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.content_public.browser.BrowserContextHandle; ++ ++import androidx.annotation.Nullable; ++import androidx.preference.Preference; ++import androidx.preference.PreferenceScreen; ++ ++import java.util.ArrayList; ++ ++public class BromiteWebGLContentSetting extends BromiteCustomContentSetting { ++ public BromiteWebGLContentSetting() { ++ super(/*contentSettingsType*/ ContentSettingsType.WEBGL, ++ /*defaultEnabledValue*/ ContentSettingValues.ALLOW, ++ /*defaultDisabledValue*/ ContentSettingValues.BLOCK, ++ /*allowException*/ true, ++ /*preferenceKey*/ "webgl", ++ /*profilePrefKey*/ "webgl"); ++ } ++ ++ @Override ++ public ContentSettingsResources.ResourceItem getResourceItem() { ++ return new ContentSettingsResources.ResourceItem( ++ /*icon*/ R.drawable.web_asset, ++ /*title*/ R.string.webgl_permission_title, ++ /*defaultEnabledValue*/ getDefaultEnabledValue(), ++ /*defaultDisabledValue*/ getDefaultDisabledValue(), ++ /*enabledSummary*/ R.string.website_settings_category_webgl_enabled, ++ /*disabledSummary*/ R.string.website_settings_category_webgl_disabled); ++ } ++ ++ @Override ++ public int getCategorySummary(@Nullable @ContentSettingValues int value) { ++ switch (value) { ++ case ContentSettingValues.ALLOW: ++ return R.string.website_settings_category_webgl_enabled; ++ case ContentSettingValues.BLOCK: ++ return R.string.website_settings_category_webgl_disabled; ++ default: ++ return 0; ++ } ++ } ++ ++ @Override ++ public int getCategoryDescription() { ++ return R.string.settings_site_settings_webgl_description; ++ } ++ ++ @Override ++ public boolean requiresTriStateContentSetting() { ++ return false; ++ } ++ ++ @Override ++ public boolean showOnlyDescriptions() { ++ return true; ++ } ++ ++ @Override ++ public int getAddExceptionDialogMessage() { ++ return R.string.website_settings_category_webgl_enabled; ++ } ++ ++ @Override ++ public @Nullable Boolean considerException(SiteSettingsCategory category, @ContentSettingValues int value) { ++ return value != ContentSettingValues.BLOCK; ++ } ++} +diff --git a/components/browser_ui/strings/bromite_content_settings/webgl.grdp b/components/browser_ui/strings/bromite_content_settings/webgl.grdp +new file mode 100644 +index 0000000000000..53635991ed294 +--- /dev/null ++++ b/components/browser_ui/strings/bromite_content_settings/webgl.grdp +@@ -0,0 +1,27 @@ ++ ++ ++ ++ Webgl ++ ++ ++ Enable Webgl, a JavaScript API for rendering high-performance interactive 3D and 2D graphics ++ ++ ++ webgl ++ ++ ++ Webgl ++ ++ ++ Enabled ++ ++ ++ Disabled ++ ++ ++ Allowed to use webgl ++ ++ ++ Not allowed to use webgl ++ ++ +diff --git a/components/content_settings/core/browser/bromite_content_settings/webgl.inc b/components/content_settings/core/browser/bromite_content_settings/webgl.inc +new file mode 100644 +index 0000000000000..3ff01832baef4 +--- /dev/null ++++ b/components/content_settings/core/browser/bromite_content_settings/webgl.inc +@@ -0,0 +1,22 @@ ++ Register(ContentSettingsType::WEBGL, "webgl", CONTENT_SETTING_BLOCK, ++ WebsiteSettingsInfo::SYNCABLE, ++ /*allowlisted_schemes=*/{}, ++ /*valid_settings=*/{CONTENT_SETTING_ALLOW, ++ CONTENT_SETTING_BLOCK}, ++ WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, ++ WebsiteSettingsRegistry::ALL_PLATFORMS, ++ ContentSettingsInfo::INHERIT_IN_INCOGNITO, ++ ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); ++ ++ content_settings::WebsiteSettingsRegistry::GetInstance() ++ ->GetMutable(ContentSettingsType::WEBGL) ++ ->set_show_into_info_page() ++ .set_desktop_ui() ++ .set_is_renderer_content_setting() ++ .set_title_ui(IDS_SITE_SETTINGS_TYPE_WEBGL) ++ .set_description_ui(IDS_SETTINGS_SITE_SETTINGS_WEBGL_DESCRIPTION) ++ .set_allowed_ui(IDS_WEBSITE_SETTINGS_CATEGORY_WEBGL_ENABLED) ++ .set_blocked_ui(IDS_WEBSITE_SETTINGS_CATEGORY_WEBGL_DISABLED) ++ .set_allowed_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_WEBGL_ALLOWED_EXCEPTIONS) ++ .set_blocked_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_WEBGL_BLOCKED_EXCEPTIONS) ++ .set_mid_sentence_ui(IDS_SITE_SETTINGS_TYPE_WEBGL); +diff --git a/components/content_settings/core/common/bromite_content_settings/WEBGL.inc b/components/content_settings/core/common/bromite_content_settings/WEBGL.inc +new file mode 100644 +index 0000000000000..30dc2ff2d125f +--- /dev/null ++++ b/components/content_settings/core/common/bromite_content_settings/WEBGL.inc +@@ -0,0 +1 @@ ++ WEBGL, +diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc +index efc08516fd1bb..485ba73ada1f1 100644 +--- a/third_party/blink/renderer/core/execution_context/execution_context.cc ++++ b/third_party/blink/renderer/core/execution_context/execution_context.cc +@@ -84,6 +84,13 @@ blink::WebContentSettingsClient* GetContentSettingsClientFor( + return settings; + } + ++bool AllowWebgl(ExecutionContext* context) { ++ blink::WebContentSettingsClient* settings = GetContentSettingsClientFor(context); ++ if (settings) ++ return settings->AllowContentSetting(ContentSettingsType::WEBGL, false); ++ return false; ++} ++ + ExecutionContext::ExecutionContext(v8::Isolate* isolate, + Agent* agent, + bool is_window) +diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h +index 72f9eca8d4a84..71b10f5a48244 100644 +--- a/third_party/blink/renderer/core/execution_context/execution_context.h ++++ b/third_party/blink/renderer/core/execution_context/execution_context.h +@@ -115,6 +115,8 @@ enum ReasonForCallingCanExecuteScripts { + + enum ReferrerPolicySource { kPolicySourceHttpHeader, kPolicySourceMetaTag }; + ++CORE_EXPORT bool AllowWebgl(ExecutionContext* context); ++ + // An environment in which script can execute. This class exposes the common + // properties of script execution environments on the web (i.e, common between + // script executing in a window and script executing in a worker), such as: +diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +index 74a289a88a953..8783d3560f727 100644 +--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc ++++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +@@ -248,6 +248,13 @@ void WebGLRenderingContextBase::InitializeWebGLContextLimits( + } + } + ++bool WebGLRenderingContextBase::AllowWebglForHost(blink::CanvasRenderingContextHost* host) { ++ if (!host) ++ return false; ++ blink::ExecutionContext* context = host->GetTopExecutionContext(); ++ return blink::AllowWebgl(context); ++} ++ + unsigned WebGLRenderingContextBase::CurrentMaxGLContexts() { + base::AutoLock locker(WebGLContextLimitLock()); + DCHECK(webgl_context_limits_initialized_); +@@ -521,25 +528,6 @@ static String ExtractWebGLContextCreationError( + const Platform::GraphicsInfo& info) { + StringBuilder builder; + builder.Append("Could not create a WebGL context"); +- FormatWebGLStatusString( +- "VENDOR", +- info.vendor_id ? String::Format("0x%04x", info.vendor_id) : "0xffff", +- builder); +- FormatWebGLStatusString( +- "DEVICE", +- info.device_id ? String::Format("0x%04x", info.device_id) : "0xffff", +- builder); +- FormatWebGLStatusString("GL_VENDOR", info.vendor_info, builder); +- FormatWebGLStatusString("GL_RENDERER", info.renderer_info, builder); +- FormatWebGLStatusString("GL_VERSION", info.driver_version, builder); +- FormatWebGLStatusString("Sandboxed", info.sandboxed ? "yes" : "no", builder); +- FormatWebGLStatusString("Optimus", info.optimus ? "yes" : "no", builder); +- FormatWebGLStatusString("AMD switchable", info.amd_switchable ? "yes" : "no", +- builder); +- FormatWebGLStatusString( +- "Reset notification strategy", +- String::Format("0x%04x", info.reset_notification_strategy).Utf8().c_str(), +- builder); + FormatWebGLStatusString("ErrorMessage", info.error_message.Utf8().c_str(), + builder); + builder.Append('.'); +@@ -599,6 +587,12 @@ WebGLRenderingContextBase::CreateWebGraphicsContext3DProvider( + const CanvasContextCreationAttributesCore& attributes, + Platform::ContextType context_type, + Platform::GraphicsInfo* graphics_info) { ++ if (!AllowWebglForHost(host)) { ++ host->HostDispatchEvent(WebGLContextEvent::Create( ++ event_type_names::kWebglcontextcreationerror, ++ "disabled by site settings policy.")); ++ return nullptr; ++ } + if ((context_type == Platform::kWebGL1ContextType && + !host->IsWebGL1Enabled()) || + (context_type == Platform::kWebGL2ContextType && +diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h +index 2261611fdd586..58a3066ac23a1 100644 +--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h ++++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h +@@ -1936,6 +1936,8 @@ class MODULES_EXPORT WebGLRenderingContextBase : public CanvasRenderingContext, + DOMArrayBufferView* pixels, + int64_t offset); + ++ static bool AllowWebglForHost(blink::CanvasRenderingContextHost* host); ++ + // Record Canvas/OffscreenCanvas.RenderingContextDrawnTo at the first draw + // call. + void RecordUKMCanvasDrawnToAtFirstDrawCall(); +-- +2.34.1 + diff --git a/build/bromite_patches/Add-webRTC-site-settings.patch b/build/bromite_patches/Add-webRTC-site-settings.patch new file mode 100644 index 0000000000000000000000000000000000000000..9c2ca379fb64a3227f521a113578756e9a27cf39 --- /dev/null +++ b/build/bromite_patches/Add-webRTC-site-settings.patch @@ -0,0 +1,273 @@ +From 5d4af42b4e5d6d6a86af19192bf998ad4226d48e Mon Sep 17 00:00:00 2001 +From: uazo +Date: Fri, 6 May 2022 14:27:17 +0000 +Subject: [PATCH 020/192] Add webRTC site settings + +Requires patch: Content-settings-infrastructure.patch + +Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../impl/BromiteWebRTCContentSetting.java | 93 +++++++++++++++++++ + .../bromite_content_settings/webrtc.grdp | 27 ++++++ + .../bromite_content_settings/webrtc.inc | 22 +++++ + .../bromite_content_settings/WEBRTC.inc | 1 + + .../peer_connection_dependency_factory.cc | 6 ++ + .../peerconnection/rtc_rtp_receiver.cc | 10 ++ + .../modules/peerconnection/rtc_rtp_sender.cc | 10 ++ + 7 files changed, 169 insertions(+) + create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebRTCContentSetting.java + create mode 100644 components/browser_ui/strings/bromite_content_settings/webrtc.grdp + create mode 100644 components/content_settings/core/browser/bromite_content_settings/webrtc.inc + create mode 100644 components/content_settings/core/common/bromite_content_settings/WEBRTC.inc + +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebRTCContentSetting.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebRTCContentSetting.java +new file mode 100644 +index 0000000000000..90db1c61a577a +--- /dev/null ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteWebRTCContentSetting.java +@@ -0,0 +1,93 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with Bromite. If not, see . ++*/ ++ ++package org.chromium.components.browser_ui.site_settings.impl; ++ ++import org.chromium.components.browser_ui.site_settings.R; ++ ++import org.chromium.components.browser_ui.site_settings.BromiteCustomContentSetting; ++import org.chromium.components.browser_ui.site_settings.ContentSettingsResources; ++import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; ++import org.chromium.components.content_settings.ContentSettingValues; ++import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.content_public.browser.BrowserContextHandle; ++ ++import androidx.annotation.Nullable; ++import androidx.preference.Preference; ++import androidx.preference.PreferenceScreen; ++ ++import java.util.ArrayList; ++ ++public class BromiteWebRTCContentSetting extends BromiteCustomContentSetting { ++ public BromiteWebRTCContentSetting() { ++ super(/*contentSettingsType*/ ContentSettingsType.WEBRTC, ++ /*defaultEnabledValue*/ ContentSettingValues.ALLOW, ++ /*defaultDisabledValue*/ ContentSettingValues.BLOCK, ++ /*allowException*/ true, ++ /*preferenceKey*/ "webrtc", ++ /*profilePrefKey*/ "webrtc"); ++ } ++ ++ @Override ++ public ContentSettingsResources.ResourceItem getResourceItem() { ++ return new ContentSettingsResources.ResourceItem( ++ /*icon*/ R.drawable.web_asset, ++ /*title*/ R.string.webrtc_permission_title, ++ /*defaultEnabledValue*/ getDefaultEnabledValue(), ++ /*defaultDisabledValue*/ getDefaultDisabledValue(), ++ /*enabledSummary*/ R.string.website_settings_category_webrtc_enabled, ++ /*disabledSummary*/ R.string.website_settings_category_webrtc_disabled); ++ } ++ ++ @Override ++ public int getCategorySummary(@Nullable @ContentSettingValues int value) { ++ switch (value) { ++ case ContentSettingValues.ALLOW: ++ return R.string.website_settings_category_webrtc_enabled; ++ case ContentSettingValues.BLOCK: ++ return R.string.website_settings_category_webrtc_disabled; ++ default: ++ // this will cause a runtime exception ++ return 0; ++ } ++ } ++ ++ @Override ++ public int getCategoryDescription() { ++ return R.string.settings_site_settings_webrtc_description; ++ } ++ ++ @Override ++ public boolean requiresTriStateContentSetting() { ++ return false; ++ } ++ ++ @Override ++ public boolean showOnlyDescriptions() { ++ return true; ++ } ++ ++ @Override ++ public int getAddExceptionDialogMessage() { ++ return R.string.website_settings_category_webrtc_enabled; ++ } ++ ++ @Override ++ public @Nullable Boolean considerException(SiteSettingsCategory category, @ContentSettingValues int value) { ++ return value != ContentSettingValues.BLOCK; ++ } ++} +diff --git a/components/browser_ui/strings/bromite_content_settings/webrtc.grdp b/components/browser_ui/strings/bromite_content_settings/webrtc.grdp +new file mode 100644 +index 0000000000000..cf4fd02e990e1 +--- /dev/null ++++ b/components/browser_ui/strings/bromite_content_settings/webrtc.grdp +@@ -0,0 +1,27 @@ ++ ++ ++ ++ WebRTC ++ ++ ++ webRTC ++ ++ ++ Enable WebRTC, a JavaScript API for real-time communication and direct peer-to-peer communication ++ ++ ++ WebRTC ++ ++ ++ Enabled ++ ++ ++ Disabled ++ ++ ++ Allowed to use WebRTC ++ ++ ++ Not allowed to use WebRTC ++ ++ +diff --git a/components/content_settings/core/browser/bromite_content_settings/webrtc.inc b/components/content_settings/core/browser/bromite_content_settings/webrtc.inc +new file mode 100644 +index 0000000000000..69b2de0ff52fa +--- /dev/null ++++ b/components/content_settings/core/browser/bromite_content_settings/webrtc.inc +@@ -0,0 +1,22 @@ ++ Register(ContentSettingsType::WEBRTC, "webrtc", CONTENT_SETTING_BLOCK, ++ WebsiteSettingsInfo::SYNCABLE, ++ /*allowlisted_schemes=*/{}, ++ /*valid_settings=*/{CONTENT_SETTING_ALLOW, ++ CONTENT_SETTING_BLOCK}, ++ WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, ++ WebsiteSettingsRegistry::ALL_PLATFORMS, ++ ContentSettingsInfo::INHERIT_IN_INCOGNITO, ++ ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); ++ ++ content_settings::WebsiteSettingsRegistry::GetInstance() ++ ->GetMutable(ContentSettingsType::WEBRTC) ++ ->set_show_into_info_page() ++ .set_desktop_ui() ++ .set_is_renderer_content_setting() ++ .set_title_ui(IDS_SITE_SETTINGS_TYPE_WEBRTC) ++ .set_description_ui(IDS_SETTINGS_SITE_SETTINGS_WEBRTC_DESCRIPTION) ++ .set_allowed_ui(IDS_WEBSITE_SETTINGS_CATEGORY_WEBRTC_ENABLED) ++ .set_blocked_ui(IDS_WEBSITE_SETTINGS_CATEGORY_WEBRTC_DISABLED) ++ .set_allowed_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_WEBRTC_ALLOWED_EXCEPTIONS) ++ .set_blocked_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_WEBRTC_BLOCKED_EXCEPTIONS) ++ .set_mid_sentence_ui(IDS_SITE_SETTINGS_TYPE_WEBRTC_MID_SENTENCE); +diff --git a/components/content_settings/core/common/bromite_content_settings/WEBRTC.inc b/components/content_settings/core/common/bromite_content_settings/WEBRTC.inc +new file mode 100644 +index 0000000000000..9af8b04d7949e +--- /dev/null ++++ b/components/content_settings/core/common/bromite_content_settings/WEBRTC.inc +@@ -0,0 +1 @@ ++ WEBRTC, +diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc +index 3ed83696b33f3..4912585888b84 100644 +--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc ++++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc +@@ -35,6 +35,7 @@ + #include "third_party/blink/public/common/peerconnection/webrtc_ip_handling_policy.h" + #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" + #include "third_party/blink/public/platform/platform.h" ++#include "third_party/blink/public/platform/web_content_settings_client.h" + #include "third_party/blink/public/platform/web_url.h" + #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h" + #include "third_party/blink/public/web/web_document.h" +@@ -739,6 +740,11 @@ PeerConnectionDependencyFactory::CreatePortAllocator( + // origin. + WebRTCIPHandlingPolicy policy = + GetWebRTCIPHandlingPolicy(webrtc_ip_handling_policy); ++ blink::WebContentSettingsClient* settings = web_frame->GetContentSettingsClient(); ++ if (settings && settings->AllowContentSetting(ContentSettingsType::WEBRTC, false)) { ++ policy = kDefault; ++ } ++ + switch (policy) { + // TODO(guoweis): specify the flag of disabling local candidate + // collection when webrtc is updated. +diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc +index 1d750f9cb2296..280210171d68c 100644 +--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc ++++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc +@@ -12,6 +12,8 @@ + #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h" + #include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h" + #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" ++#include "third_party/blink/public/platform/web_content_settings_client.h" ++#include "third_party/blink/public/web/web_local_frame.h" + #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" + #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_insertable_streams.h" + #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtcp_parameters.h" +@@ -255,6 +257,14 @@ RTCRtpCapabilities* RTCRtpReceiver::getCapabilities(ScriptState* state, + if (kind != "audio" && kind != "video") + return nullptr; + ++ LocalDOMWindow* window = To(ExecutionContext::From(state)); ++ auto* web_frame = ++ static_cast(WebFrame::FromCoreFrame(window->GetFrame())); ++ blink::WebContentSettingsClient* settings = web_frame->GetContentSettingsClient(); ++ if (settings && !settings->AllowContentSetting(ContentSettingsType::WEBRTC, false)) { ++ return nullptr; ++ } ++ + RTCRtpCapabilities* capabilities = RTCRtpCapabilities::Create(); + capabilities->setCodecs(HeapVector>()); + capabilities->setHeaderExtensions( +diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc +index d054b9a0f8e1a..6beddc4471207 100644 +--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc ++++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc +@@ -17,6 +17,8 @@ + #include "third_party/blink/public/common/privacy_budget/identifiable_surface.h" + #include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h" + #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" ++#include "third_party/blink/public/platform/web_content_settings_client.h" ++#include "third_party/blink/public/web/web_local_frame.h" + #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" + #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_insertable_streams.h" + #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_rtcp_parameters.h" +@@ -950,6 +952,14 @@ RTCRtpCapabilities* RTCRtpSender::getCapabilities(ScriptState* state, + if (kind != "audio" && kind != "video") + return nullptr; + ++ LocalDOMWindow* window = To(ExecutionContext::From(state)); ++ auto* web_frame = ++ static_cast(WebFrame::FromCoreFrame(window->GetFrame())); ++ blink::WebContentSettingsClient* settings = web_frame->GetContentSettingsClient(); ++ if (settings && !settings->AllowContentSetting(ContentSettingsType::WEBRTC, false)) { ++ return nullptr; ++ } ++ + RTCRtpCapabilities* capabilities = RTCRtpCapabilities::Create(); + capabilities->setCodecs(HeapVector>()); + capabilities->setHeaderExtensions( +-- +2.34.1 + diff --git a/build/bromite_patches/Allow-building-without-enable_reporting.patch b/build/bromite_patches/Allow-building-without-enable_reporting.patch new file mode 100644 index 0000000000000000000000000000000000000000..45e405b5b2de8bc61e35fd0ab6ef8a44e8a52a3f --- /dev/null +++ b/build/bromite_patches/Allow-building-without-enable_reporting.patch @@ -0,0 +1,547 @@ +From af7ded228141d30812c7766cff17f7630168051d Mon Sep 17 00:00:00 2001 +From: Zoraver Kang +Date: Fri, 22 May 2020 22:43:27 -0400 +Subject: [PATCH 140/192] Allow building without enable_reporting + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/net/chrome_report_sender.cc | 1 + + .../browser/devtools/protocol/network_handler.cc | 2 ++ + .../cross_origin_embedder_policy_reporter.cc | 7 +++++-- + .../cross_origin_opener_policy_reporter.cc | 16 +++++++--------- + .../browser/network/reporting_service_proxy.cc | 3 +++ + .../renderer_host/render_frame_host_impl.cc | 6 ------ + .../web_package/signed_exchange_reporter.cc | 2 ++ + ...content_switch_dependent_feature_overrides.cc | 1 + + net/base/features.cc | 2 +- + net/reporting/reporting_service.cc | 6 ++++++ + services/network/network_context.h | 4 ++-- + services/network/public/cpp/features.cc | 6 +++--- + services/network/public/mojom/BUILD.gn | 1 - + .../network/public/mojom/network_context.mojom | 2 ++ + .../core/frame/csp/content_security_policy.cc | 3 +++ + .../blink/renderer/core/frame/local_frame.cc | 3 +++ + .../blink/renderer/core/frame/local_frame.h | 6 +++--- + .../renderer/core/frame/reporting_context.cc | 9 +++++++++ + .../renderer/core/frame/reporting_context.h | 7 +++++-- + .../renderer/core/frame/reporting_observer.cc | 3 +-- + 20 files changed, 59 insertions(+), 31 deletions(-) + +diff --git a/chrome/browser/net/chrome_report_sender.cc b/chrome/browser/net/chrome_report_sender.cc +index 44c6864687a21..d914ae08612ec 100644 +--- a/chrome/browser/net/chrome_report_sender.cc ++++ b/chrome/browser/net/chrome_report_sender.cc +@@ -85,6 +85,7 @@ void SendReport( + const std::string& report, + base::OnceClosure success_callback, + ErrorCallback error_callback) { ++ if ((true)) return; + auto resource_request = std::make_unique(); + resource_request->url = report_uri; + resource_request->method = "POST"; +diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc +index a5c43bbdf09c2..77a7304636853 100644 +--- a/content/browser/devtools/protocol/network_handler.cc ++++ b/content/browser/devtools/protocol/network_handler.cc +@@ -1299,6 +1299,7 @@ String BuildReportStatus(const net::ReportingReport::Status status) { + } + } + ++#if BUILDFLAG(ENABLE_REPORTING) + std::vector ComputeReportingURLs(RenderFrameHostImpl* frame_host) { + std::vector urls; + frame_host->ForEachRenderFrameHostWithAction( +@@ -1312,6 +1313,7 @@ std::vector ComputeReportingURLs(RenderFrameHostImpl* frame_host) { + }); + return urls; + } ++#endif // BUILDFLAG(ENABLE_REPORTING) + + } // namespace + +diff --git a/content/browser/network/cross_origin_embedder_policy_reporter.cc b/content/browser/network/cross_origin_embedder_policy_reporter.cc +index c6e0784009602..37e66a4576f9d 100644 +--- a/content/browser/network/cross_origin_embedder_policy_reporter.cc ++++ b/content/browser/network/cross_origin_embedder_policy_reporter.cc +@@ -4,6 +4,7 @@ + + #include "content/browser/network/cross_origin_embedder_policy_reporter.h" + ++#include "net/base/features.h" + #include "base/strings/string_piece.h" + #include "base/values.h" + #include "content/public/browser/storage_partition.h" +@@ -95,8 +96,6 @@ void CrossOriginEmbedderPolicyReporter::Clone( + void CrossOriginEmbedderPolicyReporter::QueueAndNotify( + std::initializer_list> body, + bool report_only) { +- const absl::optional& endpoint = +- report_only ? report_only_endpoint_ : endpoint_; + const char* const disposition = report_only ? "reporting" : "enforce"; + if (observer_) { + std::vector list; +@@ -111,6 +110,9 @@ void CrossOriginEmbedderPolicyReporter::QueueAndNotify( + observer_->Notify(blink::mojom::Report::New( + kType, context_url_, blink::mojom::ReportBody::New(std::move(list)))); + } ++#if BUILDFLAG(ENABLE_REPORTING) ++ const absl::optional& endpoint = ++ report_only ? report_only_endpoint_ : endpoint_; + if (endpoint) { + base::Value::Dict body_to_pass; + for (const auto& pair : body) { +@@ -125,6 +127,7 @@ void CrossOriginEmbedderPolicyReporter::QueueAndNotify( + /*user_agent=*/absl::nullopt, std::move(body_to_pass)); + } + } ++#endif + } + + } // namespace content +diff --git a/content/browser/network/cross_origin_opener_policy_reporter.cc b/content/browser/network/cross_origin_opener_policy_reporter.cc +index bf2df309fa7c2..46467cfb34885 100644 +--- a/content/browser/network/cross_origin_opener_policy_reporter.cc ++++ b/content/browser/network/cross_origin_opener_policy_reporter.cc +@@ -18,25 +18,17 @@ + #include "services/network/public/mojom/network_context.mojom.h" + #include "services/network/public/mojom/source_location.mojom.h" + #include "url/origin.h" ++#include "build/build_config.h" + + namespace content { + + namespace { + + // Report attribute names (camelCase): +-constexpr char kColumnNumber[] = "columnNumber"; + constexpr char kDisposition[] = "disposition"; +-constexpr char kEffectivePolicy[] = "effectivePolicy"; +-constexpr char kInitialPopupURL[] = "initialPopupURL"; +-constexpr char kLineNumber[] = "lineNumber"; + constexpr char kNextURL[] = "nextResponseURL"; +-constexpr char kOpeneeURL[] = "openeeURL"; +-constexpr char kOpenerURL[] = "openerURL"; +-constexpr char kOtherDocumentURL[] = "otherDocumentURL"; + constexpr char kPreviousURL[] = "previousResponseURL"; +-constexpr char kProperty[] = "property"; + constexpr char kReferrer[] = "referrer"; +-constexpr char kSourceFile[] = "sourceFile"; + constexpr char kType[] = "type"; + + // Report attribute values: +@@ -45,6 +37,7 @@ constexpr char kDispositionReporting[] = "reporting"; + constexpr char kTypeFromResponse[] = "navigation-from-response"; + constexpr char kTypeToResponse[] = "navigation-to-response"; + ++#if BUILDFLAG(ENABLE_REPORTING) + std::string ToString(network::mojom::CrossOriginOpenerPolicyValue coop_value) { + switch (coop_value) { + case network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone: +@@ -62,6 +55,7 @@ std::string ToString(network::mojom::CrossOriginOpenerPolicyValue coop_value) { + return "restrict-properties-plus-coep"; + } + } ++#endif + + FrameTreeNode* TopLevelOpener(FrameTreeNode* frame) { + FrameTreeNode* opener = +@@ -233,6 +227,7 @@ void CrossOriginOpenerPolicyReporter::QueueAccessReport( + network::mojom::SourceLocationPtr source_location, + const std::string& reported_window_url, + const std::string& initial_popup_url) const { ++#if BUILDFLAG(ENABLE_REPORTING) + // Cross-Origin-Opener-Policy-Report-Only is not required to provide + // endpoints. + if (!coop_.report_only_reporting_endpoint) +@@ -280,12 +275,14 @@ void CrossOriginOpenerPolicyReporter::QueueAccessReport( + storage_partition_->GetNetworkContext()->QueueReport( + "coop", endpoint, context_url_, reporting_source_, + network_anonymization_key_, absl::nullopt, std::move(body)); ++#endif + } + + void CrossOriginOpenerPolicyReporter::QueueNavigationReport( + base::Value::Dict body, + const std::string& endpoint, + bool is_report_only) { ++#if BUILDFLAG(ENABLE_REPORTING) + body.Set(kDisposition, + is_report_only ? kDispositionReporting : kDispositionEnforce); + body.Set(kEffectivePolicy, +@@ -294,6 +291,7 @@ void CrossOriginOpenerPolicyReporter::QueueNavigationReport( + "coop", endpoint, context_url_, reporting_source_, + network_anonymization_key_, + /*user_agent=*/absl::nullopt, std::move(body)); ++#endif + } + + } // namespace content +diff --git a/content/browser/network/reporting_service_proxy.cc b/content/browser/network/reporting_service_proxy.cc +index 44a10ccda2cf6..cdabde61cb8ea 100644 +--- a/content/browser/network/reporting_service_proxy.cc ++++ b/content/browser/network/reporting_service_proxy.cc +@@ -11,6 +11,7 @@ + #include "base/memory/ref_counted.h" + #include "base/unguessable_token.h" + #include "base/values.h" ++#include "net/base/features.h" + #include "content/browser/service_worker/service_worker_host.h" + #include "content/browser/worker_host/dedicated_worker_host.h" + #include "content/browser/worker_host/shared_worker_host.h" +@@ -174,12 +175,14 @@ class ReportingServiceProxyImpl : public blink::mojom::ReportingServiceProxy { + const std::string& group, + const std::string& type, + base::Value::Dict body) { ++#if BUILDFLAG(ENABLE_REPORTING) + auto* rph = RenderProcessHost::FromID(render_process_id_); + if (!rph) + return; + rph->GetStoragePartition()->GetNetworkContext()->QueueReport( + type, group, url, reporting_source_, network_anonymization_key_, + /*user_agent=*/absl::nullopt, std::move(body)); ++#endif + } + + const int render_process_id_; +diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc +index 8525270e85ccb..2dc9bc023ba2a 100644 +--- a/content/browser/renderer_host/render_frame_host_impl.cc ++++ b/content/browser/renderer_host/render_frame_host_impl.cc +@@ -13360,12 +13360,6 @@ void RenderFrameHostImpl::MaybeGenerateCrashReport( + base::Value::Dict body; + if (!reason.empty()) + body.Set("reason", reason); +- +- // Send the crash report to the Reporting API. +- GetProcess()->GetStoragePartition()->GetNetworkContext()->QueueReport( +- /*type=*/"crash", /*group=*/"default", last_committed_url_, +- GetReportingSource(), isolation_info_.network_anonymization_key(), +- absl::nullopt /* user_agent */, std::move(body)); + } + + void RenderFrameHostImpl::SendCommitNavigation( +diff --git a/content/browser/web_package/signed_exchange_reporter.cc b/content/browser/web_package/signed_exchange_reporter.cc +index 2da894ca9ccfa..b624edd47d71a 100644 +--- a/content/browser/web_package/signed_exchange_reporter.cc ++++ b/content/browser/web_package/signed_exchange_reporter.cc +@@ -128,6 +128,7 @@ void ReportResult( + int frame_tree_node_id, + network::mojom::SignedExchangeReportPtr report, + const net::NetworkAnonymizationKey& network_anonymization_key) { ++#if BUILDFLAG(ENABLE_REPORTING) + FrameTreeNode* frame_tree_node = + FrameTreeNode::GloballyFindByID(frame_tree_node_id); + if (!frame_tree_node) +@@ -142,6 +143,7 @@ void ReportResult( + DCHECK(partition); + partition->GetNetworkContext()->QueueSignedExchangeReport( + std::move(report), network_anonymization_key); ++#endif + } + + } // namespace +diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc +index 8a9b2ff57d197..d3e1fc11967c6 100644 +--- a/content/public/common/content_switch_dependent_feature_overrides.cc ++++ b/content/public/common/content_switch_dependent_feature_overrides.cc +@@ -7,6 +7,7 @@ + #include "content/public/common/content_features.h" + #include "content/public/common/content_switches.h" + #include "net/base/features.h" ++#include "net/net_buildflags.h" + #include "services/network/public/cpp/features.h" + #include "services/network/public/cpp/network_switches.h" + #include "third_party/blink/public/common/features.h" +diff --git a/net/base/features.cc b/net/base/features.cc +index 41dfa7bc63480..640f5b8f93f2b 100644 +--- a/net/base/features.cc ++++ b/net/base/features.cc +@@ -242,7 +242,7 @@ extern const base::FeatureParam kTimeoutTcpConnectAttemptMax( + #if BUILDFLAG(ENABLE_REPORTING) + BASE_FEATURE(kDocumentReporting, + "DocumentReporting", +- base::FEATURE_ENABLED_BY_DEFAULT); ++ base::FEATURE_DISABLED_BY_DEFAULT); + #endif // BUILDFLAG(ENABLE_REPORTING) + + #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc +index 75ec4d24f3b6d..da1ce56867538 100644 +--- a/net/reporting/reporting_service.cc ++++ b/net/reporting/reporting_service.cc +@@ -211,6 +211,12 @@ class ReportingServiceImpl : public ReportingService { + base::Value::Dict body, + int depth, + base::TimeTicks queued_ticks) { ++#if BUILDFLAG(ENABLE_REPORTING) ++ if ((true)) ++ return; ++#else ++#error Attempting to build with enable_reporting ++#endif + DCHECK(initialized_); + context_->cache()->AddReport( + reporting_source, network_anonymization_key, sanitized_url, user_agent, +diff --git a/services/network/network_context.h b/services/network/network_context.h +index b2b848b38840b..3f50c687a95c7 100644 +--- a/services/network/network_context.h ++++ b/services/network/network_context.h +@@ -473,10 +473,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext + const absl::optional& reporting_source, + const net::NetworkAnonymizationKey& network_anonymization_key, + const absl::optional& user_agent, +- base::Value::Dict body) override; ++ base::Value::Dict body); + void QueueSignedExchangeReport( + mojom::SignedExchangeReportPtr report, +- const net::NetworkAnonymizationKey& network_anonymization_key) override; ++ const net::NetworkAnonymizationKey& network_anonymization_key); + void AddDomainReliabilityContextForTesting( + const url::Origin& origin, + const GURL& upload_url, +diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc +index a22ad71f7c32a..b9683742c4385 100644 +--- a/services/network/public/cpp/features.cc ++++ b/services/network/public/cpp/features.cc +@@ -14,10 +14,10 @@ + namespace network::features { + + BASE_FEATURE(kNetworkErrorLogging, +- "NetworkErrorLogging", +- base::FEATURE_ENABLED_BY_DEFAULT); ++ "NetworkErrorLogging", // disabled by default ++ base::FEATURE_DISABLED_BY_DEFAULT); // in bromite + +-BASE_FEATURE(kReporting, "Reporting", base::FEATURE_ENABLED_BY_DEFAULT); ++BASE_FEATURE(kReporting, "Reporting", base::FEATURE_DISABLED_BY_DEFAULT); // disabled by default + + // Based on the field trial parameters, this feature will override the value of + // the maximum number of delayable requests allowed in flight. The number of +diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn +index 311985f787a85..d6a4da7274b39 100644 +--- a/services/network/public/mojom/BUILD.gn ++++ b/services/network/public/mojom/BUILD.gn +@@ -1431,7 +1431,6 @@ mojom("mojom") { + export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1" + export_header_blink = "third_party/blink/public/platform/web_common.h" + if (enable_reporting) { +- enabled_features += [ "enable_reporting" ] + } + } + +diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom +index e1385ffd42940..25cec7c08ce34 100644 +--- a/services/network/public/mojom/network_context.mojom ++++ b/services/network/public/mojom/network_context.mojom +@@ -1169,6 +1169,7 @@ interface NetworkContext { + // provided |network_isolation_key|. + // + // Spec: https://w3c.github.io/reporting/#concept-reports ++ [EnableIf=enable_reporting] + QueueReport(string type, + string group, + url.mojom.Url url, +@@ -1182,6 +1183,7 @@ interface NetworkContext { + // Note that this queued report will never be delivered if no reporting + // endpoint matching is registered for with the provided + // |network_isolation_key|. ++ [EnableIf=enable_reporting] + QueueSignedExchangeReport(SignedExchangeReport report, + NetworkAnonymizationKey network_anonymization_key); + +diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc +index a588d41ff283a..cae080360baf9 100644 +--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc ++++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc +@@ -28,6 +28,7 @@ + #include + #include + ++#include "net/net_buildflags.h" + #include "base/containers/contains.h" + #include "base/debug/dump_without_crashing.h" + #include "services/network/public/cpp/web_sandbox_flags.h" +@@ -1156,6 +1157,7 @@ void ContentSecurityPolicy::ReportViolation( + return; + } + ++#if BUILDFLAG(ENABLE_REPORTING) + PostViolationReport(violation_data, context_frame, report_endpoints, + use_reporting_api); + +@@ -1163,6 +1165,7 @@ void ContentSecurityPolicy::ReportViolation( + // `context_frame` (i.e. we're not processing 'frame-ancestors'). + if (delegate_ && !context_frame) + delegate_->DispatchViolationEvent(*violation_data, element); ++#endif + + AuditsIssue audits_issue = AuditsIssue::CreateContentSecurityPolicyIssue( + *violation_data, header_type == ContentSecurityPolicyType::kReport, +diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc +index 336215b71f630..f2ca47225e545 100644 +--- a/third_party/blink/renderer/core/frame/local_frame.cc ++++ b/third_party/blink/renderer/core/frame/local_frame.cc +@@ -42,6 +42,7 @@ + #include "build/build_config.h" + #include "mojo/public/cpp/bindings/self_owned_receiver.h" + #include "mojo/public/cpp/system/message_pipe.h" ++#include "net/net_buildflags.h" + #include "services/network/public/cpp/features.h" + #include "services/network/public/mojom/content_security_policy.mojom-blink.h" + #include "services/network/public/mojom/source_location.mojom-blink.h" +@@ -2504,9 +2505,11 @@ void LocalFrame::MainFrameInteractive() { + } + } + ++#if BUILDFLAG(ENABLE_REPORTING) + mojom::blink::ReportingServiceProxy* LocalFrame::GetReportingService() { + return mojo_handler_->ReportingService(); + } ++#endif + + // static + void LocalFrame::NotifyUserActivation( +diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h +index eb4ce179cb09e..35f542133787f 100644 +--- a/third_party/blink/renderer/core/frame/local_frame.h ++++ b/third_party/blink/renderer/core/frame/local_frame.h +@@ -35,9 +35,9 @@ + #include "base/time/default_tick_clock.h" + #include "base/time/time.h" + #include "base/unguessable_token.h" +-#include "build/build_config.h" + #include "mojo/public/cpp/bindings/pending_associated_receiver.h" + #include "mojo/public/cpp/bindings/pending_receiver.h" ++#include "net/net_buildflags.h" + #include "services/device/public/mojom/device_posture_provider.mojom-blink-forward.h" + #include "services/metrics/public/cpp/ukm_source_id.h" + #include "services/network/public/mojom/fetch_api.mojom-blink-forward.h" +@@ -629,9 +629,9 @@ class CORE_EXPORT LocalFrame final + void FinishedScrollSequence(); + + SmoothScrollSequencer* GetSmoothScrollSequencer() const; +- ++#if BUILDFLAG(ENABLE_REPORTING) + mojom::blink::ReportingServiceProxy* GetReportingService(); +- ++#endif + // Returns the frame host ptr. The interface returned is backed by an + // associated interface with the legacy Chrome IPC channel. + mojom::blink::LocalFrameHost& GetLocalFrameHostRemote() const; +diff --git a/third_party/blink/renderer/core/frame/reporting_context.cc b/third_party/blink/renderer/core/frame/reporting_context.cc +index dc32fd90d945a..c06fe44b1b7f1 100644 +--- a/third_party/blink/renderer/core/frame/reporting_context.cc ++++ b/third_party/blink/renderer/core/frame/reporting_context.cc +@@ -4,6 +4,7 @@ + + #include "third_party/blink/renderer/core/frame/reporting_context.h" + ++#include "net/net_buildflags.h" + #include "third_party/blink/public/common/browser_interface_broker_proxy.h" + #include "third_party/blink/public/platform/platform.h" + #include "third_party/blink/public/platform/task_type.h" +@@ -53,7 +54,9 @@ const char ReportingContext::kSupplementName[] = "ReportingContext"; + ReportingContext::ReportingContext(ExecutionContext& context) + : Supplement(context), + execution_context_(context), ++#if BUILDFLAG(ENABLE_REPORTING) + reporting_service_(&context), ++#endif + receiver_(this, &context) {} + + // static +@@ -117,7 +120,9 @@ void ReportingContext::Trace(Visitor* visitor) const { + visitor->Trace(observers_); + visitor->Trace(report_buffer_); + visitor->Trace(execution_context_); ++#if BUILDFLAG(ENABLE_REPORTING) + visitor->Trace(reporting_service_); ++#endif + visitor->Trace(receiver_); + Supplement::Trace(visitor); + } +@@ -139,6 +144,7 @@ void ReportingContext::CountReport(Report* report) { + UseCounter::Count(execution_context_, feature); + } + ++#if BUILDFLAG(ENABLE_REPORTING) + const HeapMojoRemote& + ReportingContext::GetReportingService() const { + if (!reporting_service_.is_bound()) { +@@ -148,6 +154,7 @@ ReportingContext::GetReportingService() const { + } + return reporting_service_; + } ++#endif + + void ReportingContext::NotifyInternal(Report* report) { + // Buffer the report. +@@ -170,6 +177,7 @@ void ReportingContext::NotifyInternal(Report* report) { + + void ReportingContext::SendToReportingAPI(Report* report, + const String& endpoint) const { ++#if BUILDFLAG(ENABLE_REPORTING) + const String& type = report->type(); + if (!(type == ReportType::kCSPViolation || type == ReportType::kDeprecation || + type == ReportType::kPermissionsPolicyViolation || +@@ -227,6 +235,7 @@ void ReportingContext::SendToReportingAPI(Report* report, + url, endpoint, body->featureId(), body->disposition(), body->message(), + body->sourceFile(), line_number, column_number); + } ++#endif + } + + } // namespace blink +diff --git a/third_party/blink/renderer/core/frame/reporting_context.h b/third_party/blink/renderer/core/frame/reporting_context.h +index 68e9fc988a2d6..ec033d78813bf 100644 +--- a/third_party/blink/renderer/core/frame/reporting_context.h ++++ b/third_party/blink/renderer/core/frame/reporting_context.h +@@ -5,6 +5,7 @@ + #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORTING_CONTEXT_H_ + #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORTING_CONTEXT_H_ + ++#include "net/net_buildflags.h" + #include "third_party/blink/public/mojom/frame/reporting_observer.mojom-blink.h" + #include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h" + #include "third_party/blink/renderer/core/core_export.h" +@@ -55,10 +56,10 @@ class CORE_EXPORT ReportingContext : public GarbageCollected, + private: + // Counts the use of a report type via UseCounter. + void CountReport(Report*); +- ++#if BUILDFLAG(ENABLE_REPORTING) + const HeapMojoRemote& + GetReportingService() const; +- ++#endif + void NotifyInternal(Report* report); + // Send |report| via the Reporting API to |endpoint|. + void SendToReportingAPI(Report* report, const String& endpoint) const; +@@ -69,8 +70,10 @@ class CORE_EXPORT ReportingContext : public GarbageCollected, + + // This is declared mutable so that the service endpoint can be cached by + // const methods. ++#if BUILDFLAG(ENABLE_REPORTING) + mutable HeapMojoRemote + reporting_service_; ++#endif + + HeapMojoReceiver receiver_; + }; +diff --git a/third_party/blink/renderer/core/frame/reporting_observer.cc b/third_party/blink/renderer/core/frame/reporting_observer.cc +index 89c04a3d0f726..b34c1f85d7694 100644 +--- a/third_party/blink/renderer/core/frame/reporting_observer.cc ++++ b/third_party/blink/renderer/core/frame/reporting_observer.cc +@@ -62,8 +62,7 @@ void ReportingObserver::QueueReport(Report* report) { + } + + bool ReportingObserver::ObservedType(const String& type) { +- return !options_->hasTypesNonNull() || options_->typesNonNull().empty() || +- options_->typesNonNull().Find(type) != kNotFound; ++ return false; + } + + bool ReportingObserver::Buffered() { +-- +2.34.1 + diff --git a/build/bromite_patches/Allow-building-without-supervised-users.patch b/build/bromite_patches/Allow-building-without-supervised-users.patch new file mode 100644 index 0000000000000000000000000000000000000000..b35a5bad8c836c03d752b02bc2ddfb9aac61b57a --- /dev/null +++ b/build/bromite_patches/Allow-building-without-supervised-users.patch @@ -0,0 +1,275 @@ +From c87087b8b33da4d49581eae8e7fe0d8cfc1f1369 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Mon, 21 Feb 2022 01:24:11 +0100 +Subject: [PATCH 153/192] Allow building without supervised users + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/BUILD.gn | 1 - + chrome/android/chrome_java_sources.gni | 1 - + chrome/browser/BUILD.gn | 6 ------ + .../browser/extensions/api/settings_private/prefs_util.cc | 2 ++ + chrome/browser/profiles/profile_impl.cc | 4 ++++ + chrome/browser/profiles/profile_manager.cc | 4 ++++ + chrome/browser/ui/webui/managed_ui_handler.cc | 2 ++ + .../browser/ui/webui/management/management_ui_handler.cc | 2 ++ + components/supervised_user/buildflags.gni | 6 +----- + components/supervised_user/core/common/pref_names.h | 4 ++++ + .../core/common/supervised_user_constants.cc | 2 ++ + .../supervised_user/core/common/supervised_user_utils.cc | 8 ++++++++ + 12 files changed, 29 insertions(+), 13 deletions(-) + +diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn +index 8a1fa40a1e7aa..8194a946c15c6 100644 +--- a/chrome/android/BUILD.gn ++++ b/chrome/android/BUILD.gn +@@ -3480,7 +3480,6 @@ generate_jni("chrome_jni_headers") { + "java/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorSceneLayer.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSites.java", + "java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java", +- "java/src/org/chromium/chrome/browser/supervised_user/ChildAccountService.java", + "java/src/org/chromium/chrome/browser/sync/TrustedVaultClient.java", + "java/src/org/chromium/chrome/browser/tab/TabBrowserControlsConstraintsHelper.java", + "java/src/org/chromium/chrome/browser/tab/TabFavicon.java", +diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni +index 87ddc92e24f7e..7c5779f77bf09 100644 +--- a/chrome/android/chrome_java_sources.gni ++++ b/chrome/android/chrome_java_sources.gni +@@ -1060,7 +1060,6 @@ chrome_java_sources = [ + "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java", + "java/src/org/chromium/chrome/browser/suggestions/tile/TopSitesTileView.java", +- "java/src/org/chromium/chrome/browser/supervised_user/ChildAccountService.java", + "java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java", + "java/src/org/chromium/chrome/browser/survey/SurveyController.java", + "java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java", +diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn +index 38787b5f4c80e..de54aa91f1759 100644 +--- a/chrome/browser/BUILD.gn ++++ b/chrome/browser/BUILD.gn +@@ -3046,8 +3046,6 @@ static_library("browser") { + "feed/android/refresh_task_scheduler_impl.cc", + "feed/android/refresh_task_scheduler_impl.h", + "feed/android/web_feed_bridge.cc", +- "feedback/android/family_info_feedback_source.cc", +- "feedback/android/family_info_feedback_source.h", + "file_select_helper_contacts_android.cc", + "file_select_helper_contacts_android.h", + "first_run/android/first_run_prefs.cc", +@@ -3262,10 +3260,6 @@ static_library("browser") { + "ssl/chrome_security_state_model_delegate.h", + "ssl/known_interception_disclosure_infobar.cc", + "ssl/known_interception_disclosure_infobar.h", +- "supervised_user/android/favicon_fetcher.cc", +- "supervised_user/android/favicon_fetcher.h", +- "supervised_user/android/supervised_user_web_content_handler_impl.cc", +- "supervised_user/android/supervised_user_web_content_handler_impl.h", + "sync/glue/synced_tab_delegate_android.cc", + "sync/glue/synced_tab_delegate_android.h", + "sync/glue/synced_window_delegate_android.cc", +diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc +index 854e064b46d38..b77e1e2778232 100644 +--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc ++++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc +@@ -987,9 +987,11 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { + settings_api::PrefType::PREF_TYPE_BOOLEAN; + #endif + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + // Supervised Users. This setting is queried in our Tast tests (b/241943380). + (*s_allowlist)[::prefs::kSupervisedUserExtensionsMayRequestPermissions] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; ++#endif + + #if BUILDFLAG(IS_CHROMEOS_LACROS) + (*s_allowlist)[::prefs::kUseAshProxy] = +diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc +index 2de1d788b43b8..983235212478e 100644 +--- a/chrome/browser/profiles/profile_impl.cc ++++ b/chrome/browser/profiles/profile_impl.cc +@@ -691,10 +691,12 @@ void ProfileImpl::DoFinalInit(CreateMode create_mode) { + prefs->SetTime(prefs::kProfileCreationTime, path_creation_time_); + + pref_change_registrar_.Init(prefs); ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + pref_change_registrar_.Add( + prefs::kSupervisedUserId, + base::BindRepeating(&ProfileImpl::UpdateSupervisedUserIdInStorage, + base::Unretained(this))); ++#endif + + // Changes in the profile avatar. + pref_change_registrar_.Add( +@@ -1614,11 +1616,13 @@ GURL ProfileImpl::GetHomePage() { + } + + void ProfileImpl::UpdateSupervisedUserIdInStorage() { ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + ProfileManager* profile_manager = g_browser_process->profile_manager(); + ProfileAttributesEntry* entry = profile_manager->GetProfileAttributesStorage() + .GetProfileAttributesWithPath(GetPath()); + if (entry) + entry->SetSupervisedUserId(GetPrefs()->GetString(prefs::kSupervisedUserId)); ++#endif + } + + void ProfileImpl::UpdateNameInStorage() { +diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc +index 26b23873b37f4..d6f52f0ecbb62 100644 +--- a/chrome/browser/profiles/profile_manager.cc ++++ b/chrome/browser/profiles/profile_manager.cc +@@ -1178,6 +1178,7 @@ void ProfileManager::InitProfileUserPrefs(Profile* profile) { + profile->GetPrefs()->SetString(prefs::kProfileName, profile_name); + } + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + bool force_supervised_user_id = + #if BUILDFLAG(IS_CHROMEOS_ASH) +@@ -1199,6 +1200,7 @@ void ProfileManager::InitProfileUserPrefs(Profile* profile) { + profile->GetPrefs()->SetString(prefs::kSupervisedUserId, + supervised_user_id); + } ++#endif + #if !BUILDFLAG(IS_ANDROID) + if (profile->IsNewProfile()) { + profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, false); +@@ -1966,8 +1968,10 @@ void ProfileManager::AddProfileToStorage(Profile* profile) { + init_params.icon_index = + profile->GetPrefs()->GetInteger(prefs::kProfileAvatarIndex); + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + init_params.supervised_user_id = + profile->GetPrefs()->GetString(prefs::kSupervisedUserId); ++#endif + + #if BUILDFLAG(IS_CHROMEOS_ASH) + user_manager::User* user = +diff --git a/chrome/browser/ui/webui/managed_ui_handler.cc b/chrome/browser/ui/webui/managed_ui_handler.cc +index 5a9861e233fa9..7120aad88c697 100644 +--- a/chrome/browser/ui/webui/managed_ui_handler.cc ++++ b/chrome/browser/ui/webui/managed_ui_handler.cc +@@ -90,9 +90,11 @@ void ManagedUIHandler::AddObservers() { + policy_service->AddObserver(domain, this); + } + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + pref_registrar_.Add(prefs::kSupervisedUserId, + base::BindRepeating(&ManagedUIHandler::NotifyIfChanged, + base::Unretained(this))); ++#endif + } + + void ManagedUIHandler::RemoveObservers() { +diff --git a/chrome/browser/ui/webui/management/management_ui_handler.cc b/chrome/browser/ui/webui/management/management_ui_handler.cc +index efb1f0799ec6f..0637f3fd1dc4f 100644 +--- a/chrome/browser/ui/webui/management/management_ui_handler.cc ++++ b/chrome/browser/ui/webui/management/management_ui_handler.cc +@@ -1423,10 +1423,12 @@ void ManagementUIHandler::AddObservers() { + + pref_registrar_.Init(profile->GetPrefs()); + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + pref_registrar_.Add( + prefs::kSupervisedUserId, + base::BindRepeating(&ManagementUIHandler::UpdateManagedState, + base::Unretained(this))); ++#endif + + #if BUILDFLAG(IS_CHROMEOS_ASH) + pref_registrar_.Add( +diff --git a/components/supervised_user/buildflags.gni b/components/supervised_user/buildflags.gni +index 7c1d7a2c912e3..77e35248336ec 100644 +--- a/components/supervised_user/buildflags.gni ++++ b/components/supervised_user/buildflags.gni +@@ -4,9 +4,5 @@ + + declare_args() { + # Enables supervision for Family Link users. +- enable_supervised_users = +- # Platforms which fully support supervision features: +- is_android || is_chromeos || +- # Platforms for which support is currently in development: +- is_ios || is_linux || is_mac || is_win ++ enable_supervised_users = false + } +diff --git a/components/supervised_user/core/common/pref_names.h b/components/supervised_user/core/common/pref_names.h +index 34099d0378513..ec1641f0ec1f9 100644 +--- a/components/supervised_user/core/common/pref_names.h ++++ b/components/supervised_user/core/common/pref_names.h +@@ -5,8 +5,10 @@ + #ifndef COMPONENTS_SUPERVISED_USER_CORE_COMMON_PREF_NAMES_H_ + #define COMPONENTS_SUPERVISED_USER_CORE_COMMON_PREF_NAMES_H_ + ++#include "components/supervised_user/core/common/buildflags.h" + #include "extensions/buildflags/buildflags.h" + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + namespace prefs { + + // A bool pref that keeps whether the child status for this profile was already +@@ -124,4 +126,6 @@ inline constexpr char kFirstTimeInterstitialBannerState[] = + + } // namespace prefs + ++#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) ++ + #endif // COMPONENTS_SUPERVISED_USER_CORE_COMMON_PREF_NAMES_H_ +diff --git a/components/supervised_user/core/common/supervised_user_constants.cc b/components/supervised_user/core/common/supervised_user_constants.cc +index 4fe93bce8bc43..1c40d69fe39c1 100644 +--- a/components/supervised_user/core/common/supervised_user_constants.cc ++++ b/components/supervised_user/core/common/supervised_user_constants.cc +@@ -68,6 +68,7 @@ const char kChromeOSAvatarIndex[] = "chromeos-avatar-index"; + + const char kChromeOSPasswordData[] = "chromeos-password-data"; + ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + const char* const kCustodianInfoPrefs[] = { + prefs::kSupervisedUserCustodianName, + prefs::kSupervisedUserCustodianEmail, +@@ -80,6 +81,7 @@ const char* const kCustodianInfoPrefs[] = { + prefs::kSupervisedUserSecondCustodianProfileURL, + prefs::kSupervisedUserSecondCustodianProfileImageURL, + }; ++#endif + + const base::FilePath::CharType kSupervisedUserSettingsFilename[] = + FILE_PATH_LITERAL("Managed Mode Settings"); +diff --git a/components/supervised_user/core/common/supervised_user_utils.cc b/components/supervised_user/core/common/supervised_user_utils.cc +index 05200defc3914..c7fe0f748ddee 100644 +--- a/components/supervised_user/core/common/supervised_user_utils.cc ++++ b/components/supervised_user/core/common/supervised_user_utils.cc +@@ -76,11 +76,15 @@ GURL NormalizeUrl(const GURL& url) { + } + + bool AreWebFilterPrefsDefault(const PrefService& pref_service) { ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + return pref_service + .FindPreference(prefs::kDefaultSupervisedUserFilteringBehavior) + ->IsDefaultValue() || + pref_service.FindPreference(prefs::kSupervisedUserSafeSites) + ->IsDefaultValue(); ++#else ++ return true; ++#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) + } + + bool EmitLogSegmentHistogram(const std::vector& primary_accounts) { +@@ -107,10 +111,14 @@ bool EmitLogSegmentHistogram(const std::vector& primary_accounts) { + } + + bool IsSubjectToParentalControls(const PrefService* pref_service) { ++#if BUILDFLAG(ENABLE_SUPERVISED_USERS) + return pref_service && + pref_service->GetString(prefs::kSupervisedUserId) == + kChildAccountSUID && + IsChildAccountSupervisionEnabled(); ++#else ++ return false; ++#endif + } + + } // namespace supervised_user +-- +2.34.1 + diff --git a/build/bromite_patches/Allow-playing-audio-in-background.patch b/build/bromite_patches/Allow-playing-audio-in-background.patch new file mode 100644 index 0000000000000000000000000000000000000000..bdd48e6429d557510a6bbdef601e8872eb193eae --- /dev/null +++ b/build/bromite_patches/Allow-playing-audio-in-background.patch @@ -0,0 +1,57 @@ +From c415ef600671a19210837579825a52a41a858b6b Mon Sep 17 00:00:00 2001 +From: AlexeyBarabash +Date: Thu, 2 Nov 2017 18:21:16 +0200 +Subject: [PATCH 052/192] Allow playing audio in background + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../renderer/platform/media/web_media_player_impl.cc | 11 ++++++++++- + .../renderer/platform/media/web_media_player_impl.h | 3 +++ + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc +index d2fde962467c5..3c802da166563 100644 +--- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc ++++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc +@@ -1184,6 +1184,12 @@ bool WebMediaPlayerImpl::HasAudio() const { + return pipeline_metadata_.has_audio; + } + ++bool WebMediaPlayerImpl::HasVideoNonEmptySize() const { ++ DCHECK(main_task_runner_->BelongsToCurrentThread()); ++ ++ return pipeline_metadata_.has_video && pipeline_metadata_.natural_size.width() != 0 && pipeline_metadata_.natural_size.height() != 0; ++} ++ + void WebMediaPlayerImpl::EnabledAudioTracksChanged( + const WebVector& enabledTrackIds) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); +@@ -3513,7 +3519,10 @@ bool WebMediaPlayerImpl::ShouldPausePlaybackWhenHidden() const { + : HasAudio(); + + // Audio only stream is allowed to play when in background. +- if (!HasVideo() && preserve_audio) ++ //pipeline_metadata_.has_video is true for MediaPlayerRenderer, ++ //see media/base/pipeline_metadata.h. This is a workaround to allow audio ++ //streams be played in background. ++ if (!HasVideoNonEmptySize() && preserve_audio) + return false; + + // MediaPlayer always signals audio and video, so use an empty natural size to +diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.h b/third_party/blink/renderer/platform/media/web_media_player_impl.h +index 9498bbefd9fbf..7b646531adf71 100644 +--- a/third_party/blink/renderer/platform/media/web_media_player_impl.h ++++ b/third_party/blink/renderer/platform/media/web_media_player_impl.h +@@ -200,6 +200,9 @@ class PLATFORM_EXPORT WebMediaPlayerImpl + bool HasVideo() const override; + bool HasAudio() const override; + ++ // True is has video and it's frame size is not zero ++ bool HasVideoNonEmptySize() const; ++ + void EnabledAudioTracksChanged( + const WebVector& enabledTrackIds) override; + void SelectedVideoTrackChanged( +-- +2.34.1 + diff --git a/build/bromite_patches/Always-use-new-tab-page-for-default-home-page.patch b/build/bromite_patches/Always-use-new-tab-page-for-default-home-page.patch new file mode 100644 index 0000000000000000000000000000000000000000..dcad834f941788a71017d03a778d3f6b2d5ff9fa --- /dev/null +++ b/build/bromite_patches/Always-use-new-tab-page-for-default-home-page.patch @@ -0,0 +1,30 @@ +From f9040cb7bed96a87d3afbfc713e1efd1cf84957e Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Wed, 27 Jun 2018 11:02:38 +0200 +Subject: [PATCH 025/192] Always use new tab page for default home page + +Ignore any partner-provided home page. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../org/chromium/chrome/browser/homepage/HomepageManager.java | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java b/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java +index 6acf3ff6d7ff6..a5cdf2cc84df4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/homepage/HomepageManager.java +@@ -152,10 +152,6 @@ public class HomepageManager implements HomepagePolicyManager.HomepagePolicyStat + * if the homepage button is force enabled via flag. + */ + public static String getDefaultHomepageUri() { +- if (PartnerBrowserCustomizations.getInstance().isHomepageProviderAvailableAndEnabled()) { +- return PartnerBrowserCustomizations.getInstance().getHomePageUrl().getSpec(); +- } +- + String homepagePartnerDefaultUri; + String homepagePartnerDefaultGurlSerialized = + SharedPreferencesManager.getInstance().readString( +-- +2.34.1 + diff --git a/build/bromite_patches/AudioBuffer-AnalyserNode-fp-mitigations.patch b/build/bromite_patches/AudioBuffer-AnalyserNode-fp-mitigations.patch new file mode 100644 index 0000000000000000000000000000000000000000..8969aba75036435e562a2a722f3df7730ecfb040 --- /dev/null +++ b/build/bromite_patches/AudioBuffer-AnalyserNode-fp-mitigations.patch @@ -0,0 +1,261 @@ +From 29987d607a6cc3d4d4ad1261524fbde4f5cf4808 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sun, 25 Mar 2018 21:49:37 +0200 +Subject: [PATCH 056/192] AudioBuffer, AnalyserNode: fp mitigations + +Truncate base latency precision to two digits + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/browser/about_flags.cc | 4 ++++ + chrome/browser/flag_descriptions.cc | 5 +++++ + chrome/browser/flag_descriptions.h | 3 +++ + third_party/blink/common/features.cc | 4 ++++ + third_party/blink/public/common/features.h | 2 ++ + .../renderer/modules/webaudio/audio_buffer.cc | 14 ++++++++++++++ + .../blink/renderer/modules/webaudio/audio_buffer.h | 2 ++ + .../renderer/modules/webaudio/audio_context.cc | 5 ++++- + .../modules/webaudio/base_audio_context.cc | 12 ++++++++++++ + .../renderer/modules/webaudio/base_audio_context.h | 2 ++ + .../modules/webaudio/offline_audio_context.cc | 1 + + .../renderer/modules/webaudio/realtime_analyser.cc | 7 +++++++ + 12 files changed, 60 insertions(+), 1 deletion(-) + +diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc +index 82741e95ba8a4..73d1dd5a999d9 100644 +--- a/chrome/browser/about_flags.cc ++++ b/chrome/browser/about_flags.cc +@@ -3881,6 +3881,10 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kKioskEnableAppServiceDescription, kOsCrOS, + FEATURE_VALUE_TYPE(features::kKioskEnableAppService)}, + #endif // BUILDFLAG(IS_CHROMEOS) ++ {"fingerprinting-audio-context-data-noise", ++ flag_descriptions::kAudioContextShuffleEnabledName, ++ flag_descriptions::kAudioContextShuffleEnabledDescription, kOsAll, ++ FEATURE_VALUE_TYPE(blink::features::kAudioContextShuffleEnabled)}, + #if !BUILDFLAG(IS_ANDROID) + {"enable-webrtc-remote-event-log", + flag_descriptions::kWebRtcRemoteEventLogName, +diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc +index c631c713f425c..9b9d00359008a 100644 +--- a/chrome/browser/flag_descriptions.cc ++++ b/chrome/browser/flag_descriptions.cc +@@ -3639,6 +3639,11 @@ const char kWebTransportDeveloperModeDescription[] = + "When enabled, removes the requirement that all certificates used for " + "WebTransport over HTTP/3 are issued by a known certificate root."; + ++const char kAudioContextShuffleEnabledName[] = ++ "Enable Audio Context fingerprint deception"; ++const char kAudioContextShuffleEnabledDescription[] = ++ "Scale the output values of rendered data with a randomly selected factor."; ++ + const char kWebUsbDeviceDetectionName[] = + "Automatic detection of WebUSB-compatible devices"; + const char kWebUsbDeviceDetectionDescription[] = +diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h +index 302a8e99a898e..e6dc3d91746f3 100644 +--- a/chrome/browser/flag_descriptions.h ++++ b/chrome/browser/flag_descriptions.h +@@ -2095,6 +2095,9 @@ extern const char kWebRtcRemoteEventLogDescription[]; + extern const char kWebrtcUseMinMaxVEADimensionsName[]; + extern const char kWebrtcUseMinMaxVEADimensionsDescription[]; + ++extern const char kAudioContextShuffleEnabledName[]; ++extern const char kAudioContextShuffleEnabledDescription[]; ++ + extern const char kWebTransportDeveloperModeName[]; + extern const char kWebTransportDeveloperModeDescription[]; + +diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc +index ae80cfe4aec94..fe7912e08aed7 100644 +--- a/third_party/blink/common/features.cc ++++ b/third_party/blink/common/features.cc +@@ -244,6 +244,10 @@ BASE_FEATURE(kBiddingAndScoringDebugReportingAPI, + "BiddingAndScoringDebugReportingAPI", + base::FEATURE_DISABLED_BY_DEFAULT); + ++BASE_FEATURE(kAudioContextShuffleEnabled, ++ "AudioContextShuffleEnabled", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + // Blink garbage collection. + // Enables compaction of backing stores on Blink's heap. + BASE_FEATURE(kBlinkHeapCompaction, +diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h +index 6ece6ab12363f..0be0e4712487d 100644 +--- a/third_party/blink/public/common/features.h ++++ b/third_party/blink/public/common/features.h +@@ -149,6 +149,8 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBlinkHeapConcurrentSweeping); + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBlinkHeapIncrementalMarking); + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kBlinkHeapIncrementalMarkingStress); + ++BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kAudioContextShuffleEnabled); ++ + BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE( + kBlockingDownloadsInAdFrameWithoutUserActivation); + +diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc +index 2e032806463bb..e75d55be56435 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_buffer.cc ++++ b/third_party/blink/renderer/modules/webaudio/audio_buffer.cc +@@ -196,6 +196,20 @@ AudioBuffer::AudioBuffer(AudioBus* bus) + } + } + ++void AudioBuffer::ShuffleAudioData() { ++ for (unsigned i = 0; i < channels_.size(); ++i) { ++ if (NotShared array = getChannelData(i)) { ++ size_t len = array->length(); ++ if (len > 0) { ++ float* destination = array->Data(); ++ for (unsigned j = 0; j < len; ++j) { ++ destination[j] = BaseAudioContext::ShuffleAudioData(destination[j], j); ++ } ++ } ++ } ++ } ++} ++ + NotShared AudioBuffer::getChannelData( + unsigned channel_index, + ExceptionState& exception_state) { +diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer.h b/third_party/blink/renderer/modules/webaudio/audio_buffer.h +index ce75d7ab3a290..b4c562a024749 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_buffer.h ++++ b/third_party/blink/renderer/modules/webaudio/audio_buffer.h +@@ -115,6 +115,8 @@ class MODULES_EXPORT AudioBuffer final : public ScriptWrappable { + + std::unique_ptr CreateSharedAudioBuffer(); + ++ void ShuffleAudioData(); ++ + private: + static DOMFloat32Array* CreateFloat32ArrayOrNull( + uint32_t length, +diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc +index 4dedc0b91be74..46c31a4d55c5e 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc ++++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc +@@ -11,6 +11,7 @@ + #include "services/metrics/public/cpp/ukm_recorder.h" + #include "third_party/blink/public/common/browser_interface_broker_proxy.h" + #include "third_party/blink/public/common/mediastream/media_devices.h" ++#include "third_party/blink/public/common/features.h" + #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" + #include "third_party/blink/public/platform/web_audio_latency_hint.h" + #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" +@@ -586,7 +587,9 @@ double AudioContext::baseLatency() const { + DCHECK(IsMainThread()); + DCHECK(destination()); + +- return base_latency_; ++ // remove precision past two decimal digits ++ int l = base_latency_ * 100; ++ return double(l)/100; + } + + double AudioContext::outputLatency() const { +diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc +index c9fa2fd3953b4..f744265cc7224 100644 +--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc ++++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc +@@ -29,6 +29,7 @@ + + #include "base/metrics/histogram_functions.h" + #include "build/build_config.h" ++#include "third_party/blink/public/common/features.h" + #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" + #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h" + #include "third_party/blink/public/platform/platform.h" +@@ -721,6 +722,17 @@ LocalDOMWindow* BaseAudioContext::GetWindow() const { + return To(GetExecutionContext()); + } + ++/*static*/ ++float BaseAudioContext::ShuffleAudioData(float data, unsigned index) { ++ if (base::FeatureList::IsEnabled(features::kAudioContextShuffleEnabled)) { ++ float rnd = 1.0f + ++ (base::RandDouble() / 10000.0) * ++ (base::RandInt(0,10) > 5 ? 1.f : -1.f); ++ return data * rnd; ++ } ++ return data; ++} ++ + void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) { + DCHECK(IsMainThread()); + GraphAutoLocker locker(this); +diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h +index b3a041474b308..152a0ae4881f8 100644 +--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h ++++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h +@@ -338,6 +338,8 @@ class MODULES_EXPORT BaseAudioContext + // if the execution context does not exist. + bool CheckExecutionContextAndThrowIfNecessary(ExceptionState&); + ++ static float ShuffleAudioData(float data, unsigned index); ++ + protected: + enum ContextType { kRealtimeContext, kOfflineContext }; + +diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc +index f9d05fc50e89d..f7179f5c98dbd 100644 +--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc ++++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc +@@ -368,6 +368,7 @@ void OfflineAudioContext::FireCompletionEvent() { + if (!rendered_buffer) { + return; + } ++ rendered_buffer->ShuffleAudioData(); + + // Call the offline rendering completion event listener and resolve the + // promise too. +diff --git a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc +index 256ea358bc701..a0873c1aed599 100644 +--- a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc ++++ b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc +@@ -29,6 +29,7 @@ + #include + #include + ++#include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" + #include "third_party/blink/renderer/platform/audio/audio_bus.h" + #include "third_party/blink/renderer/platform/audio/audio_utilities.h" + #include "third_party/blink/renderer/platform/audio/vector_math.h" +@@ -154,6 +155,7 @@ void RealtimeAnalyser::GetFloatTimeDomainData( + input_buffer[(i + write_index - fft_size + kInputBufferSize) % + kInputBufferSize]; + ++ value = BaseAudioContext::ShuffleAudioData(value, i); + destination[i] = value; + } + } +@@ -181,6 +183,8 @@ void RealtimeAnalyser::GetByteTimeDomainData(DOMUint8Array* destination_array) { + input_buffer[(i + write_index - fft_size + kInputBufferSize) % + kInputBufferSize]; + ++ value = BaseAudioContext::ShuffleAudioData(value, i); ++ + // Scale from nominal -1 -> +1 to unsigned byte. + double scaled_value = 128 * (value + 1); + +@@ -300,6 +304,8 @@ void RealtimeAnalyser::ConvertToByteData(DOMUint8Array* destination_array) { + double scaled_value = + UCHAR_MAX * (db_mag - min_decibels) * range_scale_factor; + ++ scaled_value = BaseAudioContext::ShuffleAudioData(scaled_value, i); ++ + // Clip to valid range. + destination[i] = + static_cast(ClampTo(scaled_value, 0, UCHAR_MAX)); +@@ -319,6 +325,7 @@ void RealtimeAnalyser::ConvertFloatToDb(DOMFloat32Array* destination_array) { + float linear_value = source[i]; + double db_mag = audio_utilities::LinearToDecibels(linear_value); + destination[i] = static_cast(db_mag); ++ destination[i] = BaseAudioContext::ShuffleAudioData(destination[i], i); + } + } + } +-- +2.34.1 + diff --git a/build/bromite_patches/Battery-API-return-nothing.patch b/build/bromite_patches/Battery-API-return-nothing.patch new file mode 100644 index 0000000000000000000000000000000000000000..2bd9dcde1962a2a19cb221b74d18cab97a9c38f7 --- /dev/null +++ b/build/bromite_patches/Battery-API-return-nothing.patch @@ -0,0 +1,70 @@ +From 6ecebf471ffe6ef16904021a199eb59c4c421efa Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Thu, 22 Mar 2018 22:11:57 +0100 +Subject: [PATCH 027/192] Battery API: return nothing + +Include @thestinger's fix for correct charging/unknown values + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../modules/battery/battery_manager.cc | 26 +++---------------- + 1 file changed, 4 insertions(+), 22 deletions(-) + +diff --git a/third_party/blink/renderer/modules/battery/battery_manager.cc b/third_party/blink/renderer/modules/battery/battery_manager.cc +index 05cbfd0b52ddc..60fd8093ca695 100644 +--- a/third_party/blink/renderer/modules/battery/battery_manager.cc ++++ b/third_party/blink/renderer/modules/battery/battery_manager.cc +@@ -78,46 +78,28 @@ ScriptPromise BatteryManager::StartRequest(ScriptState* script_state) { + } + + bool BatteryManager::charging() { +- return battery_status_.Charging(); ++ return true; + } + + double BatteryManager::chargingTime() { +- return battery_status_.charging_time().InSecondsF(); ++ return 0.0; + } + + double BatteryManager::dischargingTime() { +- return battery_status_.discharging_time().InSecondsF(); ++ return std::numeric_limits::infinity(); + } + + double BatteryManager::level() { +- return battery_status_.Level(); ++ return 1.0; + } + + void BatteryManager::DidUpdateData() { + DCHECK(battery_property_); + +- BatteryStatus old_status = battery_status_; +- battery_status_ = *battery_dispatcher_->LatestData(); +- + if (battery_property_->GetState() == BatteryProperty::kPending) { + battery_property_->Resolve(this); + return; + } +- +- DCHECK(GetExecutionContext()); +- if (GetExecutionContext()->IsContextPaused() || +- GetExecutionContext()->IsContextDestroyed()) { +- return; +- } +- +- if (battery_status_.Charging() != old_status.Charging()) +- DispatchEvent(*Event::Create(event_type_names::kChargingchange)); +- if (battery_status_.charging_time() != old_status.charging_time()) +- DispatchEvent(*Event::Create(event_type_names::kChargingtimechange)); +- if (battery_status_.discharging_time() != old_status.discharging_time()) +- DispatchEvent(*Event::Create(event_type_names::kDischargingtimechange)); +- if (battery_status_.Level() != old_status.Level()) +- DispatchEvent(*Event::Create(event_type_names::kLevelchange)); + } + + void BatteryManager::RegisterWithDispatcher() { +-- +2.34.1 + diff --git a/build/bromite_patches/Block-gateway-attacks-via-websockets.patch b/build/bromite_patches/Block-gateway-attacks-via-websockets.patch new file mode 100644 index 0000000000000000000000000000000000000000..698a6a146165a060c3f2016d0f8abde517ed8e9b --- /dev/null +++ b/build/bromite_patches/Block-gateway-attacks-via-websockets.patch @@ -0,0 +1,317 @@ +From 51d8cd9c6f81b5bc866fd0908255837d5e3c3b4b Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Tue, 28 Jul 2020 12:28:58 +0200 +Subject: [PATCH 109/192] Block gateway attacks via websockets + +This approach is not comprehensive, see also: +* https://bugs.chromium.org/p/chromium/issues/detail?id=590714 + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../execution_context/execution_context.cc | 16 ++++++++++ + .../execution_context/execution_context.h | 1 + + .../renderer/core/loader/base_fetch_context.h | 1 + + .../core/loader/frame_fetch_context.cc | 20 ++++++++++++ + .../core/loader/frame_fetch_context.h | 1 + + .../core/loader/worker_fetch_context.cc | 21 +++++++++++++ + .../core/loader/worker_fetch_context.h | 1 + + .../core/workers/installed_scripts_manager.cc | 4 +-- + .../background_fetch_manager.cc | 31 +++++++++++++++++++ + .../websockets/websocket_channel_impl.cc | 5 +++ + .../modules/websockets/websocket_common.cc | 30 ++++++++++++++++++ + .../modules/websockets/websocket_common.h | 4 +++ + 12 files changed, 133 insertions(+), 2 deletions(-) + +diff --git a/third_party/blink/renderer/core/execution_context/execution_context.cc b/third_party/blink/renderer/core/execution_context/execution_context.cc +index 1bc4805ab9da9..8f50f8feef79f 100644 +--- a/third_party/blink/renderer/core/execution_context/execution_context.cc ++++ b/third_party/blink/renderer/core/execution_context/execution_context.cc +@@ -736,4 +736,20 @@ void ExecutionContext::WriteIntoTrace( + proto->set_world_type(GetWorldType(*this)); + } + ++String ExecutionContext::addressSpaceForBindings() const { ++ switch (AddressSpace()) { ++ case network::mojom::IPAddressSpace::kPublic: ++ case network::mojom::IPAddressSpace::kUnknown: ++ return "public"; ++ ++ case network::mojom::IPAddressSpace::kPrivate: ++ return "private"; ++ ++ case network::mojom::IPAddressSpace::kLocal: ++ return "local"; ++ } ++ NOTREACHED(); ++ return "public"; ++} ++ + } // namespace blink +diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h +index 180cda4e10fc4..8d38f4c1b5f3b 100644 +--- a/third_party/blink/renderer/core/execution_context/execution_context.h ++++ b/third_party/blink/renderer/core/execution_context/execution_context.h +@@ -398,6 +398,7 @@ class CORE_EXPORT ExecutionContext : public Supplementable, + void SetAddressSpace(network::mojom::blink::IPAddressSpace ip_address_space); + + HeapObserverSet& ContextLifecycleObserverSet(); ++ String addressSpaceForBindings() const; + unsigned ContextLifecycleStateObserverCountForTesting() const; + + // Implementation of WindowOrWorkerGlobalScope.crossOriginIsolated. +diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.h b/third_party/blink/renderer/core/loader/base_fetch_context.h +index 1479573dbc605..0bcf66f467af2 100644 +--- a/third_party/blink/renderer/core/loader/base_fetch_context.h ++++ b/third_party/blink/renderer/core/loader/base_fetch_context.h +@@ -88,6 +88,7 @@ class CORE_EXPORT BaseFetchContext : public FetchContext { + + virtual SubresourceFilter* GetSubresourceFilter() const = 0; + virtual bool ShouldBlockWebSocketByMixedContentCheck(const KURL&) const = 0; ++ virtual bool ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL&) const = 0; + virtual std::unique_ptr + CreateWebSocketHandshakeThrottle() = 0; + +diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc +index 7d3adaa1f95fa..4168b49e6ac32 100644 +--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc ++++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc +@@ -591,6 +591,26 @@ bool FrameFetchContext::ShouldBlockRequestByInspector(const KURL& url) const { + return should_block_request; + } + ++bool FrameFetchContext::ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL& request_url) const { ++ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move ++ // all this up to //net and //content in order to have any real impact on ++ // gateway attacks. That turns out to be a TON of work (crbug.com/378566). ++ if (requestor_space == network::mojom::IPAddressSpace::kUnknown) ++ requestor_space = network::mojom::IPAddressSpace::kPublic; ++ network::mojom::IPAddressSpace target_space = ++ network::mojom::IPAddressSpace::kPublic; ++ if (network_utils::IsReservedIPAddress(request_url.Host())) ++ target_space = network::mojom::IPAddressSpace::kPrivate; ++ if (SecurityOrigin::Create(request_url)->IsLocalhost()) ++ target_space = network::mojom::IPAddressSpace::kLocal; ++ ++ bool is_external_request = requestor_space > target_space; ++ if (is_external_request) ++ return true; ++ ++ return false; ++} ++ + void FrameFetchContext::DispatchDidBlockRequest( + const ResourceRequest& resource_request, + const ResourceLoaderOptions& options, +diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h +index 6bf0e35ad2a1d..d211d25696602 100644 +--- a/third_party/blink/renderer/core/loader/frame_fetch_context.h ++++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h +@@ -175,6 +175,7 @@ class CORE_EXPORT FrameFetchContext final : public BaseFetchContext, + bool ShouldBlockWebSocketByMixedContentCheck(const KURL&) const override; + std::unique_ptr CreateWebSocketHandshakeThrottle() + override; ++ bool ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL&) const override; + bool ShouldBlockFetchByMixedContentCheck( + mojom::blink::RequestContextType request_context, + network::mojom::blink::IPAddressSpace target_address_space, +diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc +index 1f2ae8311b78a..06b92a8b1118d 100644 +--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc ++++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc +@@ -25,6 +25,7 @@ + #include "third_party/blink/renderer/platform/loader/fetch/url_loader/url_loader_factory.h" + #include "third_party/blink/renderer/platform/loader/fetch/worker_resource_timing_notifier.h" + #include "third_party/blink/renderer/platform/network/network_state_notifier.h" ++#include "third_party/blink/renderer/platform/network/network_utils.h" + #include "third_party/blink/renderer/platform/runtime_enabled_features.h" + #include "third_party/blink/renderer/platform/scheduler/public/virtual_time_controller.h" + #include "third_party/blink/renderer/platform/supplementable.h" +@@ -94,6 +95,26 @@ bool WorkerFetchContext::ShouldBlockRequestByInspector(const KURL& url) const { + return should_block_request; + } + ++bool WorkerFetchContext::ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL& request_url) const { ++ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move ++ // all this up to //net and //content in order to have any real impact on ++ // gateway attacks. That turns out to be a TON of work (crbug.com/378566). ++ if (requestor_space == network::mojom::IPAddressSpace::kUnknown) ++ requestor_space = network::mojom::IPAddressSpace::kPublic; ++ network::mojom::IPAddressSpace target_space = ++ network::mojom::IPAddressSpace::kPublic; ++ if (network_utils::IsReservedIPAddress(request_url.Host())) ++ target_space = network::mojom::IPAddressSpace::kPrivate; ++ if (SecurityOrigin::Create(request_url)->IsLocalhost()) ++ target_space = network::mojom::IPAddressSpace::kLocal; ++ ++ bool is_external_request = requestor_space > target_space; ++ if (is_external_request) ++ return true; ++ ++ return false; ++} ++ + void WorkerFetchContext::DispatchDidBlockRequest( + const ResourceRequest& resource_request, + const ResourceLoaderOptions& options, +diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.h b/third_party/blink/renderer/core/loader/worker_fetch_context.h +index 098cd9c783ec6..e4031b1005fbe 100644 +--- a/third_party/blink/renderer/core/loader/worker_fetch_context.h ++++ b/third_party/blink/renderer/core/loader/worker_fetch_context.h +@@ -63,6 +63,7 @@ class WorkerFetchContext final : public BaseFetchContext { + bool ShouldBlockWebSocketByMixedContentCheck(const KURL&) const override; + std::unique_ptr CreateWebSocketHandshakeThrottle() + override; ++ bool ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL&) const override; + bool ShouldBlockFetchByMixedContentCheck( + mojom::blink::RequestContextType request_context, + network::mojom::blink::IPAddressSpace target_address_space, +diff --git a/third_party/blink/renderer/core/workers/installed_scripts_manager.cc b/third_party/blink/renderer/core/workers/installed_scripts_manager.cc +index 2a978cb476740..fe67745d54ccd 100644 +--- a/third_party/blink/renderer/core/workers/installed_scripts_manager.cc ++++ b/third_party/blink/renderer/core/workers/installed_scripts_manager.cc +@@ -33,9 +33,9 @@ InstalledScriptsManager::ScriptData::ScriptData( + // place so that this is shareable out of worker code. + response_address_space_ = network::mojom::IPAddressSpace::kPublic; + if (network_utils::IsReservedIPAddress(script_url_.Host())) +- response_address_space_ = network::mojom::IPAddressSpace::kLocal; ++ response_address_space_ = network::mojom::IPAddressSpace::kPrivate; + if (SecurityOrigin::Create(script_url_)->IsLocalhost()) +- response_address_space_ = network::mojom::IPAddressSpace::kLoopback; ++ response_address_space_ = network::mojom::IPAddressSpace::kLocal; + } + + ContentSecurityPolicyResponseHeaders +diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc +index b8aff17d259ca..c25135fcb18b5 100644 +--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc ++++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_manager.cc +@@ -101,6 +101,30 @@ bool ShouldBlockDanglingMarkup(const KURL& request_url) { + request_url.ProtocolIsInHTTPFamily(); + } + ++bool ShouldBlockGateWayAttacks(ExecutionContext* execution_context, ++ const KURL& request_url) { ++ network::mojom::IPAddressSpace requestor_space = ++ execution_context->AddressSpace(); ++ if (requestor_space == network::mojom::IPAddressSpace::kUnknown) ++ requestor_space = network::mojom::IPAddressSpace::kPublic; ++ ++ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move ++ // all this up to //net and //content in order to have any real impact on ++ // gateway attacks. That turns out to be a TON of work (crbug.com/378566). ++ network::mojom::IPAddressSpace target_space = ++ network::mojom::IPAddressSpace::kPublic; ++ if (network_utils::IsReservedIPAddress(request_url.Host())) ++ target_space = network::mojom::IPAddressSpace::kPrivate; ++ if (SecurityOrigin::Create(request_url)->IsLocalhost()) ++ target_space = network::mojom::IPAddressSpace::kLocal; ++ ++ bool is_external_request = requestor_space > target_space; ++ if (is_external_request) ++ return true; ++ ++ return false; ++} ++ + scoped_refptr ExtractBlobHandle( + Request* request, + ExceptionState& exception_state) { +@@ -187,6 +211,13 @@ ScriptPromise BackgroundFetchManager::fetch( + exception_state); + } + ++ if (ShouldBlockGateWayAttacks(execution_context, request_url)) { ++ return RejectWithTypeError(script_state, request_url, ++ "Requestor IP address space doesn't match the " ++ "target address space.", ++ exception_state); ++ } ++ + if (ShouldBlockPort(request_url)) { + return RejectWithTypeError(script_state, request_url, + "that port is not allowed", exception_state); +diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +index 98a78f1a7b6a5..40b01a00c8904 100644 +--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc ++++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +@@ -275,6 +275,11 @@ bool WebSocketChannelImpl::Connect(const KURL& url, const String& protocol) { + return false; + } + ++ if (GetBaseFetchContext()->ShouldBlockGateWayAttacks(execution_context_->AddressSpace(), url)) { ++ has_initiated_opening_handshake_ = false; ++ return false; ++ } ++ + if (auto* scheduler = execution_context_->GetScheduler()) { + // Two features are registered here: + // - `kWebSocket`: a non-sticky feature that will disable BFCache for any +diff --git a/third_party/blink/renderer/modules/websockets/websocket_common.cc b/third_party/blink/renderer/modules/websockets/websocket_common.cc +index 9e543e41518ff..086628ea10ff5 100644 +--- a/third_party/blink/renderer/modules/websockets/websocket_common.cc ++++ b/third_party/blink/renderer/modules/websockets/websocket_common.cc +@@ -125,9 +125,39 @@ WebSocketCommon::ConnectResult WebSocketCommon::Connect( + return ConnectResult::kException; + } + ++ network::mojom::IPAddressSpace requestor_space = ++ execution_context->AddressSpace(); ++ if (ShouldBlockGateWayAttacks(requestor_space, url_)) { ++ state_ = kClosed; ++ exception_state.ThrowSecurityError( ++ "Access to address of '" + url_.Host() + ++ "' is not allowed from current address space."); ++ return ConnectResult::kException; ++ } ++ + return ConnectResult::kSuccess; + } + ++bool WebSocketCommon::ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL& request_url) const { ++ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move ++ // all this up to //net and //content in order to have any real impact on ++ // gateway attacks. That turns out to be a TON of work (crbug.com/378566). ++ if (requestor_space == network::mojom::IPAddressSpace::kUnknown) ++ requestor_space = network::mojom::IPAddressSpace::kPublic; ++ network::mojom::IPAddressSpace target_space = ++ network::mojom::IPAddressSpace::kPublic; ++ if (network_utils::IsReservedIPAddress(request_url.Host())) ++ target_space = network::mojom::IPAddressSpace::kPrivate; ++ if (SecurityOrigin::Create(request_url)->IsLocalhost()) ++ target_space = network::mojom::IPAddressSpace::kLocal; ++ ++ bool is_external_request = requestor_space > target_space; ++ if (is_external_request) ++ return true; ++ ++ return false; ++} ++ + void WebSocketCommon::CloseInternal(int code, + const String& reason, + WebSocketChannel* channel, +diff --git a/third_party/blink/renderer/modules/websockets/websocket_common.h b/third_party/blink/renderer/modules/websockets/websocket_common.h +index ed3d0daade26f..7f0cdf10d9732 100644 +--- a/third_party/blink/renderer/modules/websockets/websocket_common.h ++++ b/third_party/blink/renderer/modules/websockets/websocket_common.h +@@ -7,6 +7,8 @@ + #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_COMMON_H_ + #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_COMMON_H_ + ++#include "services/network/public/mojom/ip_address_space.mojom.h" ++#include "third_party/blink/renderer/platform/network/network_utils.h" + #include "third_party/blink/renderer/modules/modules_export.h" + #include "third_party/blink/renderer/platform/weborigin/kurl.h" + #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" +@@ -54,6 +56,8 @@ class MODULES_EXPORT WebSocketCommon { + void SetState(State state) { state_ = state; } + const KURL& Url() const { return url_; } + ++ bool ShouldBlockGateWayAttacks(network::mojom::IPAddressSpace requestor_space, const KURL& url) const; ++ + // The following methods are public for testing. + + // Returns true if |protocol| is a valid WebSocket subprotocol name. +-- +2.34.1 + diff --git a/build/bromite_patches/Block-qjz9zk-or-trk-requests.patch b/build/bromite_patches/Block-qjz9zk-or-trk-requests.patch new file mode 100644 index 0000000000000000000000000000000000000000..5823dc524fc15d74908000ffeceb83b9b806dd44 --- /dev/null +++ b/build/bromite_patches/Block-qjz9zk-or-trk-requests.patch @@ -0,0 +1,306 @@ +From e5fe5907c98ff8fce9462716ce9edb40f1e00938 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Wed, 30 Oct 2019 11:50:13 +0100 +Subject: [PATCH 115/192] Block 'qjz9zk' or 'trk:' requests + +An info bar is displayed unless the --disable-trkbar command-line flag or the chrome://flag option is used. +This patch is based on Iridium's 'net: add "trk:" scheme and help identify URLs being retrieved' + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../chrome_autocomplete_scheme_classifier.cc | 1 + + chrome/browser/history/history_utils.cc | 1 + + chrome/browser/ui/singleton_tabs.cc | 5 ++++ + .../omnibox/browser/autocomplete_input.cc | 8 ++++- + components/url_formatter/url_fixer.cc | 4 +++ + .../child_process_security_policy_impl.cc | 1 + + net/BUILD.gn | 2 ++ + net/url_request/trk_protocol_handler.cc | 25 ++++++++++++++++ + net/url_request/trk_protocol_handler.h | 30 +++++++++++++++++++ + net/url_request/url_request.cc | 8 +++++ + .../url_request_context_builder.cc | 3 ++ + url/url_constants.cc | 1 + + url/url_constants.h | 1 + + url/url_util.cc | 2 ++ + 14 files changed, 91 insertions(+), 1 deletion(-) + create mode 100644 net/url_request/trk_protocol_handler.cc + create mode 100644 net/url_request/trk_protocol_handler.h + +diff --git a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc +index 4c88614c68c25..e4366ea4d0902 100644 +--- a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc ++++ b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc +@@ -58,6 +58,7 @@ ChromeAutocompleteSchemeClassifier::GetInputTypeForScheme( + if (base::IsStringASCII(scheme) && + (ProfileIOData::IsHandledProtocol(scheme) || + base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || ++ base::EqualsCaseInsensitiveASCII(scheme, url::kTraceScheme) || + base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || + base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme))) { + return metrics::OmniboxInputType::URL; +diff --git a/chrome/browser/history/history_utils.cc b/chrome/browser/history/history_utils.cc +index 6da2d4cc0e48b..753f92da2e7be 100644 +--- a/chrome/browser/history/history_utils.cc ++++ b/chrome/browser/history/history_utils.cc +@@ -22,6 +22,7 @@ bool CanAddURLToHistory(const GURL& url) { + url.SchemeIs(content::kChromeUIScheme) || + url.SchemeIs(content::kChromeUIUntrustedScheme) || + url.SchemeIs(content::kViewSourceScheme) || ++ url.SchemeIs(url::kTraceScheme) || + url.SchemeIs(chrome::kChromeNativeScheme) || + url.SchemeIs(chrome::kChromeSearchScheme) || + url.SchemeIs(dom_distiller::kDomDistillerScheme)) +diff --git a/chrome/browser/ui/singleton_tabs.cc b/chrome/browser/ui/singleton_tabs.cc +index 544b7d815220c..8287f30c76e2b 100644 +--- a/chrome/browser/ui/singleton_tabs.cc ++++ b/chrome/browser/ui/singleton_tabs.cc +@@ -174,6 +174,11 @@ int GetIndexOfExistingTab(Browser* browser, const NavigateParams& params) { + continue; + } + ++ // trk: URLs must not be rewritten ++ if (tab_url.SchemeIs(url::kTraceScheme)) { ++ continue; ++ } ++ + GURL rewritten_tab_url = tab_url; + content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary( + &rewritten_tab_url, browser->profile()); +diff --git a/components/omnibox/browser/autocomplete_input.cc b/components/omnibox/browser/autocomplete_input.cc +index 3900b74d4b95c..8d23a6ed73c1e 100644 +--- a/components/omnibox/browser/autocomplete_input.cc ++++ b/components/omnibox/browser/autocomplete_input.cc +@@ -90,10 +90,15 @@ void OffsetComponentsExcludingScheme(url::Parsed* parts, int offset) { + bool HasScheme(const std::u16string& input, const char* scheme) { + std::string utf8_input(base::UTF16ToUTF8(input)); + url::Component view_source_scheme; ++ ++ if (url::FindAndCompareScheme(utf8_input, url::kTraceScheme, &view_source_scheme)) { ++ return false; ++ } + if (url::FindAndCompareScheme(utf8_input, kViewSourceScheme, + &view_source_scheme)) { + utf8_input.erase(0, view_source_scheme.end() + 1); + } ++ + return url::FindAndCompareScheme(utf8_input, scheme, nullptr); + } + +@@ -581,7 +586,8 @@ void AutocompleteInput::ParseForEmphasizeComponents( + // For the view-source and blob schemes, we should emphasize the host of the + // URL qualified by the view-source or blob prefix. + if ((base::EqualsCaseInsensitiveASCII(scheme_str, kViewSourceScheme) || +- base::EqualsCaseInsensitiveASCII(scheme_str, url::kBlobScheme)) && ++ base::EqualsCaseInsensitiveASCII(scheme_str, url::kBlobScheme) || ++ base::EqualsCaseInsensitiveASCII(scheme_str, url::kTraceScheme)) && + (static_cast(text.length()) > after_scheme_and_colon)) { + // Obtain the URL prefixed by view-source or blob and parse it. + std::u16string real_url(text.substr(after_scheme_and_colon)); +diff --git a/components/url_formatter/url_fixer.cc b/components/url_formatter/url_fixer.cc +index d44b4cee79157..3db922698161e 100644 +--- a/components/url_formatter/url_fixer.cc ++++ b/components/url_formatter/url_fixer.cc +@@ -563,6 +563,10 @@ GURL FixupURL(const std::string& text, const std::string& desired_tld) { + } + } + ++ if (scheme == url::kTraceScheme) { ++ return GURL(); ++ } ++ + // We handle the file scheme separately. + if (scheme == url::kFileScheme) + return GURL(parts.scheme.is_valid() ? text : FixupPath(text)); +diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc +index ccc2e7da62b79..d83cffabe575e 100644 +--- a/content/browser/child_process_security_policy_impl.cc ++++ b/content/browser/child_process_security_policy_impl.cc +@@ -823,6 +823,7 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() + RegisterPseudoScheme(url::kJavaScriptScheme); + RegisterPseudoScheme(kViewSourceScheme); + RegisterPseudoScheme(kGoogleChromeScheme); ++ RegisterWebSafeScheme(url::kTraceScheme); + } + + ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { +diff --git a/net/BUILD.gn b/net/BUILD.gn +index 8883c27b66b2a..bc87e0f8f0361 100644 +--- a/net/BUILD.gn ++++ b/net/BUILD.gn +@@ -1056,6 +1056,8 @@ component("net") { + "url_request/url_request_http_job.cc", + "url_request/url_request_http_job.h", + "url_request/url_request_interceptor.cc", ++ "url_request/trk_protocol_handler.cc", ++ "url_request/trk_protocol_handler.h", + "url_request/url_request_interceptor.h", + "url_request/url_request_job.cc", + "url_request/url_request_job.h", +diff --git a/net/url_request/trk_protocol_handler.cc b/net/url_request/trk_protocol_handler.cc +new file mode 100644 +index 0000000000000..e32409c333a5e +--- /dev/null ++++ b/net/url_request/trk_protocol_handler.cc +@@ -0,0 +1,25 @@ ++// Copyright (c) 2018 The ungoogled-chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "net/url_request/trk_protocol_handler.h" ++ ++#include "base/logging.h" ++#include "net/base/net_errors.h" ++#include "net/url_request/url_request_error_job.h" ++ ++namespace net { ++ ++TrkProtocolHandler::TrkProtocolHandler() = default; ++ ++std::unique_ptr TrkProtocolHandler::CreateJob( ++ URLRequest* request) const { ++ LOG(ERROR) << "Blocked URL in TrkProtocolHandler: " << request->original_url(); ++ return std::make_unique(request, ERR_BLOCKED_BY_CLIENT); ++} ++ ++bool TrkProtocolHandler::IsSafeRedirectTarget(const GURL& location) const { ++ return true; ++} ++ ++} // namespace net +diff --git a/net/url_request/trk_protocol_handler.h b/net/url_request/trk_protocol_handler.h +new file mode 100644 +index 0000000000000..b37fe2def5c6d +--- /dev/null ++++ b/net/url_request/trk_protocol_handler.h +@@ -0,0 +1,30 @@ ++// Copyright (c) 2018 The ungoogled-chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef NET_URL_REQUEST_TRK_PROTOCOL_HANDLER_H_ ++#define NET_URL_REQUEST_TRK_PROTOCOL_HANDLER_H_ ++ ++#include "base/compiler_specific.h" ++#include "net/base/net_export.h" ++#include "net/url_request/url_request_job_factory.h" ++ ++namespace net { ++ ++class URLRequestJob; ++ ++// Implements a ProtocolHandler for Trk jobs. ++class NET_EXPORT TrkProtocolHandler ++ : public URLRequestJobFactory::ProtocolHandler { ++ public: ++ TrkProtocolHandler(const TrkProtocolHandler&) = delete; ++ TrkProtocolHandler& operator=(const TrkProtocolHandler&) = delete; ++ ++ TrkProtocolHandler(); ++ std::unique_ptr CreateJob(URLRequest* request) const override; ++ bool IsSafeRedirectTarget(const GURL& location) const override; ++}; ++ ++} // namespace net ++ ++#endif // NET_URL_REQUEST_TRK_PROTOCOL_HANDLER_H_ +diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc +index 655d9ebcd3928..572f9e62e94f7 100644 +--- a/net/url_request/url_request.cc ++++ b/net/url_request/url_request.cc +@@ -13,6 +13,7 @@ + #include "base/metrics/histogram_macros.h" + #include "base/rand_util.h" + #include "base/strings/utf_string_conversions.h" ++#include "base/strings/string_util.h" + #include "base/synchronization/lock.h" + #include "base/task/single_thread_task_runner.h" + #include "base/types/pass_key.h" +@@ -47,6 +48,7 @@ + #include "net/url_request/url_request_redirect_job.h" + #include "url/gurl.h" + #include "url/origin.h" ++#include "url/url_constants.h" + + namespace net { + +@@ -597,6 +599,12 @@ URLRequest::URLRequest(base::PassKey pass_key, + // Sanity check out environment. + DCHECK(base::SingleThreadTaskRunner::HasCurrentDefault()); + ++ if (!url.SchemeIs(url::kTraceScheme) && ++ base::EndsWith(url.host(), "qjz9zk", base::CompareCase::INSENSITIVE_ASCII)) { ++ LOG(ERROR) << "Block URL in URLRequest: " << url; ++ url_chain_[0] = GURL(url::kTraceScheme + (":" + url.possibly_invalid_spec())); ++ } ++ + context->url_requests()->insert(this); + net_log_.BeginEvent(NetLogEventType::REQUEST_ALIVE, [&] { + return NetLogURLRequestConstructorParams(url, priority_, +diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc +index 9f4ed67802f46..d7bbc87ea0213 100644 +--- a/net/url_request/url_request_context_builder.cc ++++ b/net/url_request/url_request_context_builder.cc +@@ -48,6 +48,7 @@ + #include "net/socket/network_binding_client_socket_factory.h" + #include "net/ssl/ssl_config_service_defaults.h" + #include "net/url_request/static_http_user_agent_settings.h" ++#include "net/url_request/trk_protocol_handler.h" + #include "net/url_request/url_request_context.h" + #include "net/url_request/url_request_job_factory.h" + #include "net/url_request/url_request_throttler_manager.h" +@@ -573,6 +574,8 @@ std::unique_ptr URLRequestContextBuilder::Build() { + job_factory->SetProtocolHandler(scheme_handler.first, + std::move(scheme_handler.second)); + } ++ job_factory->SetProtocolHandler(url::kTraceScheme, ++ std::make_unique()); + protocol_handlers_.clear(); + + context->set_job_factory(std::move(job_factory)); +diff --git a/url/url_constants.cc b/url/url_constants.cc +index 850a31ce22de6..b65df785d593b 100644 +--- a/url/url_constants.cc ++++ b/url/url_constants.cc +@@ -29,6 +29,7 @@ const char16_t kDataScheme16[] = u"data"; + const char kFileScheme[] = "file"; + const char16_t kFileScheme16[] = u"file"; + const char kFileSystemScheme[] = "filesystem"; ++const char kTraceScheme[] = "trk"; + const char16_t kFileSystemScheme16[] = u"filesystem"; + const char kFtpScheme[] = "ftp"; + const char16_t kFtpScheme16[] = u"ftp"; +diff --git a/url/url_constants.h b/url/url_constants.h +index 5eda4e89f2582..6ba533af6d0b6 100644 +--- a/url/url_constants.h ++++ b/url/url_constants.h +@@ -33,6 +33,7 @@ COMPONENT_EXPORT(URL) extern const char16_t kContentIDScheme16[]; + COMPONENT_EXPORT(URL) extern const char kDataScheme[]; + COMPONENT_EXPORT(URL) extern const char16_t kDataScheme16[]; + COMPONENT_EXPORT(URL) extern const char kFileScheme[]; ++COMPONENT_EXPORT(URL) extern const char kTraceScheme[]; + COMPONENT_EXPORT(URL) extern const char16_t kFileScheme16[]; + COMPONENT_EXPORT(URL) extern const char kFileSystemScheme[]; + COMPONENT_EXPORT(URL) extern const char16_t kFileSystemScheme16[]; +diff --git a/url/url_util.cc b/url/url_util.cc +index 001c50e72fd24..685526881e955 100644 +--- a/url/url_util.cc ++++ b/url/url_util.cc +@@ -46,6 +46,7 @@ struct SchemeRegistry { + std::vector standard_schemes = { + {kHttpsScheme, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION}, + {kHttpScheme, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION}, ++ {kTraceScheme, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION}, + // Yes, file URLs can have a hostname, so file URLs should be handled as + // "standard". File URLs never have a port as specified by the SchemeType + // field. Unlike other SCHEME_WITH_HOST schemes, the 'host' in a file +@@ -91,6 +92,7 @@ struct SchemeRegistry { + kAboutScheme, + kJavaScriptScheme, + kDataScheme, ++ kTraceScheme, + }; + + // Schemes that can be sent CORS requests. +-- +2.34.1 + diff --git a/build/bromite_patches/Bookmarks-select-all-menu-entry.patch b/build/bromite_patches/Bookmarks-select-all-menu-entry.patch new file mode 100644 index 0000000000000000000000000000000000000000..6879966b77cf02778c5f89db52dc5c82a6ac5c27 --- /dev/null +++ b/build/bromite_patches/Bookmarks-select-all-menu-entry.patch @@ -0,0 +1,113 @@ +From 572f7fe604a0804eba8e087ef2e545a623b2e599 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 9 Apr 2022 23:01:55 +0200 +Subject: [PATCH 062/192] Bookmarks select all menu entry + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../java/res/menu/bookmark_toolbar_menu.xml | 7 +++++++ + .../res/menu/bookmark_toolbar_menu_improved.xml | 7 +++++++ + .../chrome/browser/bookmarks/BookmarkToolbar.java | 15 +++++++++++++++ + .../ui/android/strings/android_chrome_strings.grd | 3 +++ + 4 files changed, 32 insertions(+) + +diff --git a/chrome/android/java/res/menu/bookmark_toolbar_menu.xml b/chrome/android/java/res/menu/bookmark_toolbar_menu.xml +index bb414a0d4069d..06e8b38817949 100644 +--- a/chrome/android/java/res/menu/bookmark_toolbar_menu.xml ++++ b/chrome/android/java/res/menu/bookmark_toolbar_menu.xml +@@ -23,6 +23,13 @@ found in the LICENSE file. + android:visible="false" + app:showAsAction="ifRoom" + app:iconTint="@color/default_icon_color_secondary_tint_list" /> ++ + ++ + + + void setCurrentFolder(BookmarkId folder) { + mCurrentFolder = mBookmarkModel.getBookmarkById(folder); ++ getMenu().findItem(R.id.select_all_menu_id).setVisible(true); + getMenu().findItem(R.id.import_menu_id).setVisible(true); + getMenu().findItem(R.id.export_menu_id).setVisible(true); + } +@@ -195,6 +198,17 @@ public class BookmarkToolbar extends SelectableListToolbar + mExportBookmarkRunnable.run(); + return true; + } ++ if (menuItem.getItemId() == R.id.select_all_menu_id) { ++ if (mBookmarkModel.isBookmarkModelLoaded()) { ++ List items = mBookmarkModel.getBookmarksForFolder(mCurrentFolder.getId()); ++ HashSet ids = new HashSet<>(items.size()); ++ for (BookmarkItem item : items) { ++ ids.add(item.getId()); ++ } ++ mSelectionDelegate.setSelectedItems(ids); ++ } ++ return true; ++ } + return mMenuIdClickedFunction.apply(menuItem.getItemId()); + } + +@@ -217,6 +231,7 @@ public class BookmarkToolbar extends SelectableListToolbar + + getMenu().findItem(R.id.import_menu_id).setVisible(mCurrentFolder != null); + getMenu().findItem(R.id.export_menu_id).setVisible(mCurrentFolder != null); ++ getMenu().findItem(R.id.select_all_menu_id).setVisible(mCurrentFolder != null); + + // SelectableListToolbar will show/hide the entire group. + setSearchButtonVisible(mSearchButtonVisible); +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index bf095f7de2c72..aa35bda9b1066 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -245,6 +245,9 @@ CHAR_LIMIT guidelines: + + Sites + ++ ++ Select all ++ + + Import + +-- +2.34.1 + diff --git a/build/bromite_patches/Bromite-auto-updater.patch b/build/bromite_patches/Bromite-auto-updater.patch new file mode 100644 index 0000000000000000000000000000000000000000..0497a8331e14770c83afe0cf9553d5acc305447b --- /dev/null +++ b/build/bromite_patches/Bromite-auto-updater.patch @@ -0,0 +1,1653 @@ +From 160b7e30a26384fb69283ae95b91e7238d2d1c3c Mon Sep 17 00:00:00 2001 +From: uazo +Date: Thu, 7 Oct 2021 14:27:12 +0000 +Subject: [PATCH 092/192] Bromite auto updater + +Enable checking for new versions, with notifications and proxy support. +Restore InlineUpdateFlow feature. +Some parts authored by csagan5. + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + .../java/templates/BuildConfig.template | 2 + + build/config/android/rules.gni | 3 + + chrome/android/chrome_java_sources.gni | 3 + + .../java/res/xml/about_chrome_preferences.xml | 5 + + .../about_settings/AboutChromeSettings.java | 28 +- + .../chrome/browser/omaha/OmahaBase.java | 57 +++- + .../chrome/browser/omaha/UpdateConfigs.java | 30 +- + .../browser/omaha/UpdateMenuItemHelper.java | 82 +++++- + .../browser/omaha/UpdateStatusProvider.java | 161 ++++++++--- + .../browser/omaha/VersionNumberGetter.java | 3 +- + .../inline/BromiteInlineUpdateController.java | 272 ++++++++++++++++++ + .../omaha/inline/InlineUpdateController.java | 51 ++++ + .../inline/InlineUpdateControllerFactory.java | 21 ++ + chrome/browser/endpoint_fetcher/BUILD.gn | 2 + + .../endpoint_fetcher_android.cc | 50 ++++ + .../endpoint_fetcher/EndpointFetcher.java | 26 +- + .../EndpointHeaderResponse.java | 31 ++ + .../flags/android/chrome_feature_list.cc | 5 + + .../flags/android/chrome_feature_list.h | 1 + + .../browser/flags/ChromeFeatureList.java | 1 + + .../strings/android_chrome_strings.grd | 23 +- + .../endpoint_fetcher/endpoint_fetcher.cc | 103 ++++++- + .../endpoint_fetcher/endpoint_fetcher.h | 23 +- + 23 files changed, 933 insertions(+), 50 deletions(-) + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java + create mode 100644 chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java + +diff --git a/build/android/java/templates/BuildConfig.template b/build/android/java/templates/BuildConfig.template +index cfecb6fefdcba..f6521f42246d4 100644 +--- a/build/android/java/templates/BuildConfig.template ++++ b/build/android/java/templates/BuildConfig.template +@@ -92,4 +92,6 @@ public class BuildConfig { + #else + public static MAYBE_FINAL boolean IS_FOR_TEST MAYBE_FALSE; + #endif ++ ++ public static MAYBE_FINAL String BUILD_TARGET_CPU = _BUILD_TARGET_CPU; + } +diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni +index 758dbe2a66cbc..aea13f388bb36 100644 +--- a/build/config/android/rules.gni ++++ b/build/config/android/rules.gni +@@ -2144,6 +2144,9 @@ if (enable_java_templates && is_android) { + if (defined(testonly) && testonly) { + defines += [ "_IS_FOR_TEST" ] + } ++ ++ # add arch to org.chromium.build.BuildConfig ++ defines += [ "_BUILD_TARGET_CPU=\"${target_cpu}\"" ] + } + } + +diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni +index 4f5cf7b9337c0..f927e1a14b0c1 100644 +--- a/chrome/android/chrome_java_sources.gni ++++ b/chrome/android/chrome_java_sources.gni +@@ -858,6 +858,9 @@ chrome_java_sources = [ + "java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java", + "java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java", + "java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java", ++ "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java", ++ "java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java", ++ "java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java", + "java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java", + "java/src/org/chromium/chrome/browser/omaha/metrics/HistogramUtils.java", + "java/src/org/chromium/chrome/browser/omaha/metrics/TrackingProvider.java", +diff --git a/chrome/android/java/res/xml/about_chrome_preferences.xml b/chrome/android/java/res/xml/about_chrome_preferences.xml +index fb1dfed23052b..f055e0e91e4bd 100644 +--- a/chrome/android/java/res/xml/about_chrome_preferences.xml ++++ b/chrome/android/java/res/xml/about_chrome_preferences.xml +@@ -9,6 +9,11 @@ found in the LICENSE file. + ++ + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java +index f59f62fdf971b..5da0608895710 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java +@@ -21,14 +21,20 @@ import org.chromium.ui.widget.Toast; + + import java.util.Calendar; + ++import android.content.SharedPreferences; ++import org.chromium.chrome.browser.omaha.OmahaBase; ++import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; ++ + /** + * Settings fragment that displays information about Chrome. + */ + public class AboutChromeSettings +- extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener { ++ extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, ++ Preference.OnPreferenceChangeListener { + private static final int TAPS_FOR_DEVELOPER_SETTINGS = 7; + + private static final String PREF_APPLICATION_VERSION = "application_version"; ++ private static final String PREF_ALLOW_INLINE_UPDATE = "allow_inline_update"; // switch preference + private static final String PREF_OS_VERSION = "os_version"; + private static final String PREF_LEGAL_INFORMATION = "legal_information"; + +@@ -59,6 +65,13 @@ public class AboutChromeSettings + p = findPreference(PREF_LEGAL_INFORMATION); + int currentYear = Calendar.getInstance().get(Calendar.YEAR); + p.setSummary(getString(R.string.legal_information_summary, currentYear)); ++ ++ ChromeSwitchPreference allowInlineUpdate = ++ (ChromeSwitchPreference) findPreference(PREF_ALLOW_INLINE_UPDATE); ++ allowInlineUpdate.setChecked( ++ OmahaBase.getSharedPreferences() ++ .getBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, false)); ++ allowInlineUpdate.setOnPreferenceChangeListener(this); + } + + /** +@@ -122,4 +135,17 @@ public class AboutChromeSettings + } + return true; + } ++ ++ @Override ++ public boolean onPreferenceChange(Preference preference, Object newValue) { ++ String key = preference.getKey(); ++ if (PREF_ALLOW_INLINE_UPDATE.equals(key)) { ++ SharedPreferences.Editor sharedPreferenceEditor = OmahaBase.getSharedPreferences().edit(); ++ sharedPreferenceEditor.putBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, (boolean) newValue); ++ sharedPreferenceEditor.apply(); ++ ++ OmahaBase.resetUpdatePrefs(); ++ } ++ return true; ++ } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java +index 33e232ec8bdda..2e24639a03af2 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java +@@ -33,6 +33,8 @@ import java.net.HttpURLConnection; + import java.net.URL; + import java.util.Date; + ++import org.chromium.build.BuildConfig; ++ + /** + * Keeps tabs on the current state of Chrome, tracking if and when a request should be sent to the + * Omaha Server. +@@ -99,7 +101,10 @@ public class OmahaBase { + static final String PREF_TIMESTAMP_FOR_NEW_REQUEST = "timestampForNewRequest"; + static final String PREF_TIMESTAMP_FOR_NEXT_POST_ATTEMPT = "timestampForNextPostAttempt"; + static final String PREF_TIMESTAMP_OF_INSTALL = "timestampOfInstall"; +- static final String PREF_TIMESTAMP_OF_REQUEST = "timestampOfRequest"; ++ public static final String PREF_TIMESTAMP_OF_REQUEST = "timestampOfRequest"; ++ public static final String PREF_LATEST_MODIFIED_VERSION = "latestModifiedVersion"; ++ public static final String PREF_LATEST_UPSTREAM_VERSION = "latestUpstreamVersion"; ++ public static final String PREF_ALLOW_INLINE_UPDATE = "allowInlineUpdate"; + + private static final int UNKNOWN_DATE = -2; + +@@ -157,7 +162,8 @@ public class OmahaBase { + } + + static boolean isDisabled() { +- return sDisabledForTesting; ++ // do not enable version control via Omaha Update Server ++ return true; + } + + /** +@@ -567,6 +573,10 @@ public class OmahaBase { + /** Sends the request to the server and returns the response. */ + static String sendRequestToServer(HttpURLConnection urlConnection, String request) + throws RequestFailureException { ++ if ((true)) { ++ throw new RequestFailureException("Requests to Omaha server are forbidden.", ++ RequestFailureException.ERROR_CONNECTIVITY); ++ } + try { + OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream()); + OutputStreamWriter writer = new OutputStreamWriter(out); +@@ -637,4 +647,47 @@ public class OmahaBase { + // updateStatus is only used for the on-demand check. + null); + } ++ ++ public static boolean isNewVersionAvailableByVersion(VersionNumber latestVersion) { ++ VersionNumber mCurrentProductVersion = VersionNumber.fromString(VersionInfo.getProductVersion()); ++ if (mCurrentProductVersion == null) { ++ Log.e(TAG, "BromiteUpdater: current product version is null"); ++ return false; ++ } ++ ++ Log.i(TAG, "BromiteUpdater: currentProductVersion=%s, latestVersion=%s", ++ mCurrentProductVersion.toString(), latestVersion.toString()); ++ ++ return mCurrentProductVersion.isSmallerThan(latestVersion); ++ } ++ ++ public static void updateLastPushedTimeStamp(long timeMillis) { ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ SharedPreferences.Editor editor = preferences.edit(); ++ editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, timeMillis); ++ editor.apply(); ++ } ++ ++ public static void setLatestModifiedVersion(String version) { ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ SharedPreferences.Editor editor = preferences.edit(); ++ editor.putString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, version); ++ editor.apply(); ++ } ++ ++ public static void setLatestUpstreamVersion(String version) { ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ SharedPreferences.Editor editor = preferences.edit(); ++ editor.putString(OmahaBase.PREF_LATEST_UPSTREAM_VERSION, version); ++ editor.apply(); ++ } ++ ++ public static void resetUpdatePrefs() { ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ SharedPreferences.Editor editor = preferences.edit(); ++ editor.putLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, 0); ++ editor.putString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, ""); ++ editor.putString(OmahaBase.PREF_LATEST_UPSTREAM_VERSION, ""); ++ editor.apply(); ++ } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java +index b7296bac5b9e3..dd7d998373355 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateConfigs.java +@@ -11,6 +11,7 @@ import androidx.annotation.IntDef; + import androidx.annotation.Nullable; + + import org.chromium.base.CommandLine; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; + import org.chromium.chrome.browser.flags.ChromeSwitches; + import org.chromium.chrome.browser.omaha.UpdateStatusProvider.UpdateState; + import org.chromium.components.variations.VariationsAssociatedData; +@@ -36,10 +37,12 @@ public class UpdateConfigs { + private static final String UPDATE_AVAILABLE_SWITCH_VALUE = "update_available"; + private static final String UNSUPPORTED_OS_VERSION_SWITCH_VALUE = "unsupported_os_version"; + ++ private static final long DEFAULT_UPDATE_NOTIFICATION_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS; + private static final long DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS = 2 * DateUtils.DAY_IN_MILLIS; + + /** Possible update flow configurations. */ +- @IntDef({UpdateFlowConfiguration.NEVER_SHOW, UpdateFlowConfiguration.INTENT_ONLY}) ++ @IntDef({UpdateFlowConfiguration.NEVER_SHOW, UpdateFlowConfiguration.INTENT_ONLY, ++ UpdateFlowConfiguration.INLINE_ONLY}) + @Retention(RetentionPolicy.SOURCE) + public @interface UpdateFlowConfiguration { + /** Turns off all update indicators. */ +@@ -49,6 +52,12 @@ public class UpdateConfigs { + * Requires Omaha to say an update is available, and only ever Intents out to Play Store. + */ + int INTENT_ONLY = 2; ++ ++ /** ++ * Inline updates that contact Bromite official GitHub repository to say whether an update is available. ++ * Only ever uses the inline update flow. ++ */ ++ int INLINE_ONLY = 3; + } + + /** +@@ -123,6 +132,13 @@ public class UpdateConfigs { + return DEFAULT_UPDATE_ATTRIBUTION_WINDOW_MS; + } + ++ /** ++ * @return A time interval for scheduling update notification. Unit: mills. ++ */ ++ public static long getUpdateNotificationInterval() { ++ return DEFAULT_UPDATE_NOTIFICATION_INTERVAL; ++ } ++ + /** + * Gets a String VariationsAssociatedData parameter. Also checks for a command-line switch + * with the same name, for easy local testing. +@@ -137,4 +153,14 @@ public class UpdateConfigs { + } + return value; + } +-} +\ No newline at end of file ++ ++ @UpdateFlowConfiguration ++ public static int getConfiguration() { ++ if (!ChromeFeatureList.isEnabled(ChromeFeatureList.INLINE_UPDATE_FLOW)) { ++ // Always use the the old flow if the inline update flow feature is not enabled. ++ return UpdateFlowConfiguration.INLINE_ONLY; ++ } ++ ++ return UpdateFlowConfiguration.NEVER_SHOW; ++ } ++} +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java +index 55588e6b4375b..1e7f44b34affe 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java +@@ -12,6 +12,7 @@ import android.view.Choreographer; + + import androidx.annotation.NonNull; + import androidx.annotation.Nullable; ++import androidx.annotation.StringRes; + + import org.chromium.base.BuildInfo; + import org.chromium.base.Callback; +@@ -135,8 +136,21 @@ public class UpdateMenuItemHelper { + Log.e(TAG, "Failed to launch Activity for: %s", mStatus.updateUrl); + } + break; ++ case UpdateState.VULNERABLE_VERSION: ++ // Intentional fall through. ++ case UpdateState.INLINE_UPDATE_AVAILABLE: ++ UpdateStatusProvider.getInstance().startInlineUpdate(activity); ++ break; ++ case UpdateState.INLINE_UPDATE_READY: ++ UpdateStatusProvider.getInstance().finishInlineUpdate(); ++ break; ++ case UpdateState.INLINE_UPDATE_FAILED: ++ UpdateStatusProvider.getInstance().retryInlineUpdate(activity); ++ break; + case UpdateState.UNSUPPORTED_OS_VERSION: + // Intentional fall through. ++ case UpdateState.INLINE_UPDATE_DOWNLOADING: ++ // Intentional fall through. + default: + return; + } +@@ -182,7 +196,7 @@ public class UpdateMenuItemHelper { + + mMenuUiState = new MenuUiState(); + switch (mStatus.updateState) { +- case UpdateState.UPDATE_AVAILABLE: ++ case UpdateState.UPDATE_AVAILABLE: // this is not used in Bromite + // The badge is hidden if the update menu item has been clicked until there is an + // even newer version of Chrome available. + showBadge |= !TextUtils.equals( +@@ -237,6 +251,72 @@ public class UpdateMenuItemHelper { + mMenuUiState.itemState.icon = R.drawable.ic_error_24dp_filled; + mMenuUiState.itemState.enabled = false; + break; ++ case UpdateState.VULNERABLE_VERSION: ++ // Intentional fall through. ++ case UpdateState.INLINE_UPDATE_AVAILABLE: ++ // The badge is hidden if the update menu item has been clicked until there is an ++ // even newer version of Chrome available. ++ @StringRes int defaultUpdateSummary = R.string.menu_update_summary_default; ++ if (mStatus.updateState == UpdateState.VULNERABLE_VERSION) { ++ // always show badge in case of vulnerable version ++ showBadge = true; ++ mMenuUiState.buttonState = new MenuButtonState(); ++ mMenuUiState.buttonState.menuContentDescription = R.string.accessibility_toolbar_btn_menu_update; ++ mMenuUiState.buttonState.darkBadgeIcon = ++ R.drawable.ic_error_grey800_24dp_filled; ++ mMenuUiState.buttonState.lightBadgeIcon = R.drawable.ic_error_white_24dp_filled; ++ mMenuUiState.buttonState.adaptiveBadgeIcon = R.drawable.ic_error_24dp_filled; ++ defaultUpdateSummary = R.string.menu_update_summary_vulnerable; ++ } else { ++ showBadge |= !TextUtils.equals( ++ getPrefService().getString( ++ Pref.LATEST_VERSION_WHEN_CLICKED_UPDATE_MENU_ITEM), ++ mStatus.latestUnsupportedVersion); ++ if (showBadge) { ++ mMenuUiState.buttonState = new MenuButtonState(); ++ mMenuUiState.buttonState.menuContentDescription = R.string.accessibility_toolbar_btn_menu_update; ++ mMenuUiState.buttonState.darkBadgeIcon = R.drawable.badge_update_dark; ++ mMenuUiState.buttonState.lightBadgeIcon = R.drawable.badge_update_light; ++ mMenuUiState.buttonState.adaptiveBadgeIcon = R.drawable.badge_update; ++ } ++ } ++ ++ mMenuUiState.itemState = new MenuItemState(); ++ mMenuUiState.itemState.title = R.string.menu_update; ++ mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; ++ mMenuUiState.itemState.summary = UpdateConfigs.getCustomSummary(); ++ if (TextUtils.isEmpty(mMenuUiState.itemState.summary)) { ++ mMenuUiState.itemState.summary = ++ resources.getString(defaultUpdateSummary); ++ } ++ mMenuUiState.itemState.icon = R.drawable.ic_history_googblue_24dp; ++ mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; ++ mMenuUiState.itemState.enabled = true; ++ break; ++ case UpdateState.INLINE_UPDATE_DOWNLOADING: ++ mMenuUiState.itemState = new MenuItemState(); ++ mMenuUiState.itemState.title = R.string.menu_inline_update_downloading; ++ mMenuUiState.itemState.titleColorId = R.color.default_text_color_secondary_dark; ++ break; ++ case UpdateState.INLINE_UPDATE_READY: ++ mMenuUiState.itemState = new MenuItemState(); ++ mMenuUiState.itemState.title = R.string.menu_inline_update_ready; ++ mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; ++ mMenuUiState.itemState.summary = ++ resources.getString(R.string.menu_inline_update_ready_summary); ++ mMenuUiState.itemState.icon = R.drawable.infobar_chrome; ++ mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; ++ mMenuUiState.itemState.enabled = true; ++ break; ++ case UpdateState.INLINE_UPDATE_FAILED: ++ mMenuUiState.itemState = new MenuItemState(); ++ mMenuUiState.itemState.title = R.string.menu_inline_update_failed; ++ mMenuUiState.itemState.titleColorId = R.color.default_text_color_blue_dark; ++ mMenuUiState.itemState.summary = resources.getString(R.string.try_again); ++ mMenuUiState.itemState.icon = R.drawable.ic_history_googblue_24dp; ++ mMenuUiState.itemState.iconTintId = R.color.default_icon_color_blue_light; ++ mMenuUiState.itemState.enabled = true; ++ break; + case UpdateState.NONE: + // Intentional fall through. + default: +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java +index 10f69e965845d..27c75c368a446 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateStatusProvider.java +@@ -4,6 +4,7 @@ + + package org.chromium.chrome.browser.omaha; + ++import android.app.Activity; + import android.content.ActivityNotFoundException; + import android.content.Context; + import android.content.Intent; +@@ -15,7 +16,11 @@ import android.text.TextUtils; + import androidx.annotation.IntDef; + import androidx.annotation.NonNull; + import androidx.annotation.Nullable; ++import androidx.annotation.VisibleForTesting; + ++import org.chromium.base.ActivityState; ++import org.chromium.base.ApplicationStatus; ++import org.chromium.base.ApplicationStatus.ActivityStateListener; + import org.chromium.base.BuildInfo; + import org.chromium.base.Callback; + import org.chromium.base.ObserverList; +@@ -25,6 +30,10 @@ import org.chromium.base.task.AsyncTask; + import org.chromium.base.task.AsyncTask.Status; + import org.chromium.base.task.PostTask; + import org.chromium.base.task.TaskTraits; ++import org.chromium.chrome.browser.app.ChromeActivity; ++import org.chromium.chrome.browser.omaha.inline.BromiteInlineUpdateController; ++import org.chromium.chrome.browser.omaha.inline.InlineUpdateController; ++import org.chromium.chrome.browser.omaha.inline.InlineUpdateControllerFactory; + import org.chromium.chrome.browser.omaha.metrics.UpdateSuccessMetrics; + import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; + import org.chromium.chrome.browser.preferences.SharedPreferencesManager; +@@ -34,30 +43,37 @@ import java.io.File; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.base.Log; ++import android.content.SharedPreferences; ++import android.os.Build; ++import org.chromium.build.BuildConfig; ++ + /** + * Provides the current update state for Chrome. This update state is asynchronously determined and + * can change as Chrome runs. + * + * For manually testing this functionality, see {@link UpdateConfigs}. + */ +-public class UpdateStatusProvider { ++public class UpdateStatusProvider implements ActivityStateListener { + /** + * Possible update states. + * Treat this as append only as it is used by UMA. + */ +- @IntDef({UpdateState.NONE, UpdateState.UPDATE_AVAILABLE, UpdateState.UNSUPPORTED_OS_VERSION}) ++ @IntDef({UpdateState.NONE, UpdateState.UPDATE_AVAILABLE, UpdateState.UNSUPPORTED_OS_VERSION, ++ UpdateState.INLINE_UPDATE_AVAILABLE, UpdateState.INLINE_UPDATE_DOWNLOADING, ++ UpdateState.INLINE_UPDATE_READY, UpdateState.INLINE_UPDATE_FAILED, UpdateState.VULNERABLE_VERSION}) + @Retention(RetentionPolicy.SOURCE) + public @interface UpdateState { + int NONE = 0; + int UPDATE_AVAILABLE = 1; + int UNSUPPORTED_OS_VERSION = 2; +- // Inline updates are deprecated. +- // int INLINE_UPDATE_AVAILABLE = 3; +- // int INLINE_UPDATE_DOWNLOADING = 4; +- // int INLINE_UPDATE_READY = 5; +- // int INLINE_UPDATE_FAILED = 6; ++ int INLINE_UPDATE_AVAILABLE = 3; ++ int INLINE_UPDATE_DOWNLOADING = 4; ++ int INLINE_UPDATE_READY = 5; ++ int INLINE_UPDATE_FAILED = 6; ++ int VULNERABLE_VERSION = 7; + +- int NUM_ENTRIES = 7; ++ int NUM_ENTRIES = 8; + } + + /** A set of properties that represent the current update state for Chrome. */ +@@ -91,6 +107,12 @@ public class UpdateStatusProvider { + */ + private boolean mIsSimulated; + ++ /** ++ * Whether or not we are currently trying to simulate an inline flow. Used to allow ++ * overriding Omaha update state, which usually supersedes inline update states. ++ */ ++ private boolean mIsInlineSimulated; ++ + public UpdateStatus() {} + + UpdateStatus(UpdateStatus other) { +@@ -99,11 +121,13 @@ public class UpdateStatusProvider { + latestVersion = other.latestVersion; + latestUnsupportedVersion = other.latestUnsupportedVersion; + mIsSimulated = other.mIsSimulated; ++ mIsInlineSimulated = other.mIsInlineSimulated; + } + } + + private final ObserverList> mObservers = new ObserverList<>(); + ++ private final InlineUpdateController mInlineController; + private final UpdateQuery mOmahaQuery; + private final UpdateSuccessMetrics mMetrics; + private @Nullable UpdateStatus mStatus; +@@ -171,6 +195,30 @@ public class UpdateStatusProvider { + pingObservers(); + } + ++ /** ++ * Starts the inline update process, if possible. ++ * @param activity An {@link Activity} that will be used to interact with Play. ++ */ ++ public void startInlineUpdate(Activity activity) { ++ if (mStatus == null || (mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE && mStatus.updateState != UpdateState.VULNERABLE_VERSION)) return; ++ mInlineController.startUpdate(activity); ++ } ++ ++ /** ++ * Retries the inline update process, if possible. ++ * @param activity An {@link Activity} that will be used to interact with Play. ++ */ ++ public void retryInlineUpdate(Activity activity) { ++ if (mStatus == null || (mStatus.updateState != UpdateState.INLINE_UPDATE_AVAILABLE && mStatus.updateState != UpdateState.VULNERABLE_VERSION)) return; ++ mInlineController.startUpdate(activity); ++ } ++ ++ /** Finishes the inline update process, which may involve restarting the app. */ ++ public void finishInlineUpdate() { ++ if (mStatus == null || mStatus.updateState != UpdateState.INLINE_UPDATE_READY) return; ++ mInlineController.completeUpdate(); ++ } ++ + /** + * Starts the intent update process, if possible + * @param context An {@link Context} that will be used to fire off the update intent. +@@ -178,12 +226,11 @@ public class UpdateStatusProvider { + * @return Whether or not the update intent was sent and had a valid handler. + */ + public boolean startIntentUpdate(Context context, boolean newTask) { ++ // currently not used in Bromite + if (mStatus == null || mStatus.updateState != UpdateState.UPDATE_AVAILABLE) return false; + if (TextUtils.isEmpty(mStatus.updateUrl)) return false; + + try { +- mMetrics.startUpdate(); +- + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mStatus.updateUrl)); + if (newTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); +@@ -194,9 +241,29 @@ public class UpdateStatusProvider { + return true; + } + ++ // ApplicationStateListener implementation. ++ @Override ++ public void onActivityStateChange(Activity changedActivity, @ActivityState int newState) { ++ boolean hasActiveActivity = false; ++ ++ for (Activity activity : ApplicationStatus.getRunningActivities()) { ++ if (activity == null || !(activity instanceof ChromeActivity)) continue; ++ ++ hasActiveActivity |= ++ ApplicationStatus.getStateForActivity(activity) == ActivityState.RESUMED; ++ if (hasActiveActivity) break; ++ } ++ ++ mInlineController.setEnabled(hasActiveActivity); ++ } ++ + private UpdateStatusProvider() { ++ mInlineController = InlineUpdateControllerFactory.create(this::resolveStatus); + mOmahaQuery = new UpdateQuery(this::resolveStatus); + mMetrics = new UpdateSuccessMetrics(); ++ ++ // Note that as a singleton this class never unregisters. ++ ApplicationStatus.registerStateListenerForAllActivities(this); + } + + private void pingObservers() { +@@ -204,19 +271,36 @@ public class UpdateStatusProvider { + } + + private void resolveStatus() { +- if (mOmahaQuery.getStatus() != Status.FINISHED) { ++ if (mOmahaQuery.getStatus() != Status.FINISHED || mInlineController.getStatus() == null) { + return; + } + + // We pull the Omaha result once as it will never change. + if (mStatus == null) mStatus = new UpdateStatus(mOmahaQuery.getResult()); + +- if (!mStatus.mIsSimulated) { +- mStatus.updateState = mOmahaQuery.getResult().updateState; ++ if (mStatus.mIsSimulated) { // used only during tests ++ if (mStatus.mIsInlineSimulated) { ++ @UpdateState ++ int inlineState = mInlineController.getStatus(); ++ String updateUrl = mInlineController.getUpdateUrl(); ++ ++ if (inlineState == UpdateState.NONE) { ++ mStatus.updateState = mOmahaQuery.getResult().updateState; ++ } else { ++ mStatus.updateState = inlineState; ++ mStatus.updateUrl = updateUrl; ++ } ++ } ++ } else { ++ // used by Bromite to resolve update status ++ // ignores Omaha status ++ @UpdateState ++ int inlineState = mInlineController.getStatus(); ++ mStatus.updateState = inlineState; ++ mStatus.updateUrl = mInlineController.getUpdateUrl(); + } + + if (!mRecordedInitialStatus) { +- mMetrics.analyzeFirstStatus(); + mRecordedInitialStatus = true; + } + +@@ -228,6 +312,7 @@ public class UpdateStatusProvider { + } + + private static final class UpdateQuery extends AsyncTask { ++ static final String TAG = "UpdateStatusProvider"; + private final Runnable mCallback; + + private @Nullable UpdateStatus mStatus; +@@ -244,7 +329,7 @@ public class UpdateStatusProvider { + protected UpdateStatus doInBackground() { + UpdateStatus testStatus = getTestStatus(); + if (testStatus != null) return testStatus; +- return getRealStatus(); ++ return getActualStatus(); + } + + @Override +@@ -263,6 +348,8 @@ public class UpdateStatusProvider { + status.mIsSimulated = true; + status.updateState = forcedUpdateState; + ++ status.mIsInlineSimulated = forcedUpdateState == UpdateState.INLINE_UPDATE_AVAILABLE; ++ + // Push custom configurations for certain update states. + switch (forcedUpdateState) { + case UpdateState.UPDATE_AVAILABLE: +@@ -279,27 +366,33 @@ public class UpdateStatusProvider { + return status; + } + +- private UpdateStatus getRealStatus() { ++ private UpdateStatus getActualStatus() { + UpdateStatus status = new UpdateStatus(); + +- if (VersionNumberGetter.isNewerVersionAvailable()) { +- status.updateUrl = MarketURLGetter.getMarketUrl(); +- status.latestVersion = VersionNumberGetter.getInstance().getLatestKnownVersion(); +- +- boolean allowedToUpdate = checkForSufficientStorage() +- && PackageUtils.isPackageInstalled( +- GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE); +- status.updateState = +- allowedToUpdate ? UpdateState.UPDATE_AVAILABLE : UpdateState.NONE; +- +- SharedPreferencesManager.getInstance().removeKey( +- ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION); +- } else if (!VersionNumberGetter.isCurrentOsVersionSupported()) { +- status.updateState = UpdateState.UNSUPPORTED_OS_VERSION; +- status.latestUnsupportedVersion = SharedPreferencesManager.getInstance().readString( +- ChromePreferenceKeys.LATEST_UNSUPPORTED_VERSION, null); +- } else { +- status.updateState = UpdateState.NONE; ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ status.latestVersion = preferences.getString(OmahaBase.PREF_LATEST_MODIFIED_VERSION, ""); ++ ++ status.updateState = UpdateState.NONE; ++ if (status.latestVersion != null && status.latestVersion.length() != 0) { ++ VersionNumber latestVersion = VersionNumber.fromString(status.latestVersion); ++ if (latestVersion == null) { ++ Log.e(TAG, "BromiteUpdater: stored latest version '%s' is invalid", status.latestVersion); ++ } else if (OmahaBase.isNewVersionAvailableByVersion(latestVersion)) { ++ status.updateState = UpdateState.INLINE_UPDATE_AVAILABLE; ++ status.updateUrl = BromiteInlineUpdateController.getDownloadUrl(); ++ return status; ++ } ++ String latestUpstreamVersion = preferences.getString(OmahaBase.PREF_LATEST_UPSTREAM_VERSION, ""); ++ if (latestUpstreamVersion != null && latestUpstreamVersion.length() != 0) { ++ VersionNumber upstreamVersion = VersionNumber.fromString(latestUpstreamVersion); ++ if (upstreamVersion == null) { ++ Log.e(TAG, "BromiteUpdater: stored latest upstream version '%s' is invalid", latestUpstreamVersion); ++ } else if (OmahaBase.isNewVersionAvailableByVersion(upstreamVersion)) { ++ status.updateUrl = BromiteInlineUpdateController.VULNERABLE_VERSION_DOC_URL; ++ status.updateState = UpdateState.VULNERABLE_VERSION; ++ return status; ++ } ++ } + } + + return status; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java +index 90cfcd7661c43..9451d364347f8 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/VersionNumberGetter.java +@@ -50,7 +50,8 @@ public class VersionNumberGetter { + private static VersionNumberGetter sInstanceForTests; + + /** If false, OmahaClient will never report that a newer version is available. */ +- private static boolean sEnableUpdateDetection = true; ++ // it must be false to disable version control via Omaha server ++ private static boolean sEnableUpdateDetection = false; + + protected VersionNumberGetter() { } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java +new file mode 100644 +index 0000000000000..90692d394ebe9 +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/BromiteInlineUpdateController.java +@@ -0,0 +1,272 @@ ++// Copyright 2021 The Ungoogled Chromium Authors. All rights reserved. ++// ++// This file is part of Ungoogled Chromium Android. ++// ++// Ungoogled Chromium Android is free software: you can redistribute it ++// and/or modify it under the terms of the GNU General Public License as ++// published by the Free Software Foundation, either version 3 of the ++// License, or any later version. ++// ++// Ungoogled Chromium Android is distributed in the hope that it will be ++// useful, but WITHOUT ANY WARRANTY; without even the implied warranty ++// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++// GNU General Public License for more details. ++// ++// You should have received a copy of the GNU General Public License ++// along with Ungoogled Chromium Android. If not, ++// see . ++ ++package org.chromium.chrome.browser.omaha.inline; ++ ++import static org.chromium.chrome.browser.omaha.UpdateConfigs.getUpdateNotificationInterval; ++ ++import android.app.Activity; ++import android.content.SharedPreferences; ++import android.os.Build; ++import android.text.format.DateUtils; ++import org.chromium.build.BuildConfig; ++ ++import androidx.annotation.Nullable; ++ ++import org.chromium.base.Callback; ++import org.chromium.base.Log; ++import org.chromium.base.task.AsyncTask; ++import org.chromium.base.task.PostTask; ++import org.chromium.base.task.TaskTraits; ++import org.chromium.chrome.browser.app.ChromeActivity; ++import org.chromium.chrome.browser.omaha.OmahaBase; ++import org.chromium.chrome.browser.omaha.UpdateConfigs; ++import org.chromium.chrome.browser.omaha.UpdateStatusProvider; ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.chrome.browser.tab.TabLaunchType; ++import org.chromium.chrome.browser.tabmodel.TabCreator; ++import org.chromium.content_public.browser.LoadUrlParams; ++import org.chromium.ui.base.PageTransition; ++import org.chromium.net.NetworkTrafficAnnotationTag; ++ ++import java.io.BufferedReader; ++import java.io.IOException; ++import java.io.InputStreamReader; ++import java.io.InputStream; ++import java.net.MalformedURLException; ++import java.net.URL; ++import java.net.HttpURLConnection; ++import java.util.regex.Pattern; ++ ++import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher; ++import org.chromium.chrome.browser.endpoint_fetcher.EndpointResponse; ++import org.chromium.chrome.browser.omaha.VersionNumber; ++ ++public class BromiteInlineUpdateController implements InlineUpdateController { ++ ++ private static final String TAG = "BromiteInlineUpdateController"; ++ private final String REDIRECT_URL_PREFIX = "https://github.com/bromite/bromite/releases/download/"; ++ private static final String UPDATE_VERSION_URL = "https://github.com/bromite/bromite/releases/latest/download/"; ++ private final String UPSTREAM_VERSION_URL = "https://www.bromite.org/upstream.txt"; ++ public static final String VULNERABLE_VERSION_DOC_URL = "https://www.bromite.org/vulnerable-version"; ++ ++ public static String getDownloadUrl() { ++ return UPDATE_VERSION_URL + BuildConfig.BUILD_TARGET_CPU + "_ChromePublic.apk"; ++ } ++ ++ private static final NetworkTrafficAnnotationTag TRAFFIC_ANNOTATION = ++ NetworkTrafficAnnotationTag.createComplete("bromite_inline_update_controller", ++ "semantics {" ++ + " sender: 'Bromite Inline Update (Android)'" ++ + " description:" ++ + " 'Check for update'" ++ + " trigger: 'This request is made once, on first run'" ++ + " data: 'None.'" ++ + " destination: OTHER" ++ + " internal {" ++ + " contacts {" ++ + " email: 'uazo@users.noreply.github.com'" ++ + " }" ++ + " contacts {" ++ + " email: 'uazo@users.noreply.github.com'" ++ + " }" ++ + " }" ++ + " user_data {" ++ + " type: NONE" ++ + " }" ++ + " last_reviewed: '2023-01-01'" ++ + "}" ++ + "policy {" ++ + " cookies_allowed: NO" ++ + " setting: 'Can be disabled in Settings.'" ++ + " policy_exception_justification: 'Not implemented.'" ++ + "}"); ++ ++ private boolean mEnabled = true; ++ private Runnable mCallback; ++ private @Nullable @UpdateStatusProvider.UpdateState Integer mUpdateState = ++ UpdateStatusProvider.UpdateState.NONE; ++ private String mUpdateUrl = ""; ++ ++ BromiteInlineUpdateController(Runnable callback) { ++ mCallback = callback; ++ } ++ ++ @Override ++ public void setEnabled(boolean enabled) { ++ if (mEnabled == enabled) return; ++ ++ mEnabled = enabled; ++ // check for an update when state changes ++ if (mEnabled) pullCurrentState(); ++ } ++ ++ @Override ++ public @Nullable @UpdateStatusProvider.UpdateState Integer getStatus() { ++ if (mEnabled) pullCurrentState(); ++ return mUpdateState; ++ } ++ ++ @Override ++ public String getUpdateUrl() { ++ // relies on a prior call to getStatus() to have state and URL correctly pulled ++ return mUpdateUrl; ++ } ++ ++ @Override ++ public void startUpdate(Activity activity) { ++ assert ChromeActivity.class.isInstance(activity); ++ ChromeActivity thisActivity = (ChromeActivity) activity; ++ // Always open in new incognito tab ++ TabCreator tabCreator = thisActivity.getTabCreator(true); ++ tabCreator.createNewTab(new LoadUrlParams(mUpdateUrl, PageTransition.AUTO_BOOKMARK), ++ TabLaunchType.FROM_LINK, thisActivity.getActivityTab()); ++ } ++ ++ @Override ++ public void completeUpdate() { ++ } ++ ++ private void pullCurrentState() { ++ if (OmahaBase.getSharedPreferences() ++ .getBoolean(OmahaBase.PREF_ALLOW_INLINE_UPDATE, false) == false) { ++ Log.i(TAG, "BromiteUpdater: disabled by user"); ++ return; ++ } ++ ++ // do not pull state if there is already a state set ++ if (mUpdateState != UpdateStatusProvider.UpdateState.NONE) ++ return; ++ ++ if (shallUpdate() == false) ++ return; ++ ++ switch (mUpdateState) { ++ case UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE: ++ break; ++ case UpdateStatusProvider.UpdateState.NONE: ++ OmahaBase.resetUpdatePrefs(); ++ checkLatestVersion((latestVersion) -> { ++ if (latestVersion == null) return; ++ ++ if (OmahaBase.isNewVersionAvailableByVersion(latestVersion)) { ++ postStatus(UpdateStatusProvider.UpdateState.INLINE_UPDATE_AVAILABLE, getDownloadUrl()); ++ } else { ++ checkLatestUpstreamVersion((latestUpstreamVersion) -> { ++ if (latestUpstreamVersion == null) return; ++ if (OmahaBase.isNewVersionAvailableByVersion(latestUpstreamVersion)) { ++ postStatus(UpdateStatusProvider.UpdateState.VULNERABLE_VERSION, VULNERABLE_VERSION_DOC_URL); ++ } ++ }); ++ } ++ }); ++ break; ++ case UpdateStatusProvider.UpdateState.INLINE_UPDATE_READY: ++ // Intentional fall through. ++ case UpdateStatusProvider.UpdateState.INLINE_UPDATE_FAILED: ++ // Intentional fall through. ++ case UpdateStatusProvider.UpdateState.INLINE_UPDATE_DOWNLOADING: ++ // Intentional fall through. ++ case UpdateStatusProvider.UpdateState.UNSUPPORTED_OS_VERSION: ++ // Intentional fall through. ++ case UpdateStatusProvider.UpdateState.VULNERABLE_VERSION: ++ // Intentional fall through. ++ default: ++ return; ++ } ++ } ++ ++ private boolean shallUpdate() { ++ long currentTime = System.currentTimeMillis(); ++ SharedPreferences preferences = OmahaBase.getSharedPreferences(); ++ long lastPushedTimeStamp = preferences.getLong(OmahaBase.PREF_TIMESTAMP_OF_REQUEST, 0); ++ return currentTime - lastPushedTimeStamp >= getUpdateNotificationInterval(); ++ } ++ ++ private void checkLatestVersion(final Callback callback) { ++ assert UPDATE_VERSION_URL != null; ++ ++ String urlToCheck = getDownloadUrl(); ++ Log.i(TAG, "BromiteUpdater: fetching with HEAD '%s'", urlToCheck); ++ ++ EndpointFetcher.nativeHeadWithNoAuth( ++ (endpointResponse) -> { ++ boolean versionFound = false; ++ String redirectURL = endpointResponse.getRedirectUrl(); ++ if (redirectURL != null) { ++ Log.i(TAG, "BromiteUpdater: obtained response '%s' and redirect URL '%s'", endpointResponse.getResponseString(), redirectURL); ++ if (redirectURL.indexOf(REDIRECT_URL_PREFIX) == 0) { ++ redirectURL = redirectURL.substring(REDIRECT_URL_PREFIX.length()); ++ String[] parts = redirectURL.split(Pattern.quote("/")); ++ if (parts.length > 0) { ++ VersionNumber version = VersionNumber.fromString(parts[0]); ++ if (version != null) { ++ versionFound = true; ++ OmahaBase.setLatestModifiedVersion(parts[0]); ++ callback.onResult(version); ++ return; ++ } ++ } ++ } ++ } ++ if (!versionFound) { ++ // retry after 1 hour ++ OmahaBase.updateLastPushedTimeStamp( ++ System.currentTimeMillis() - getUpdateNotificationInterval() - ++ DateUtils.HOUR_IN_MILLIS); ++ Log.e(TAG, "BromiteUpdater: failed, will retry in 1 hour"); ++ } ++ ++ callback.onResult(null); ++ }, ++ Profile.getLastUsedRegularProfile(), ++ urlToCheck, /*timeout*/5000, /*follow_redirect*/true, TRAFFIC_ANNOTATION); ++ } ++ ++ private void checkLatestUpstreamVersion(final Callback callback) { ++ Log.i(TAG, "BromiteUpdater: fetching with GET '%s'", UPSTREAM_VERSION_URL); ++ ++ EndpointFetcher.nativeFetchWithNoAuth( ++ (endpointResponse) -> { ++ String response = endpointResponse.getResponseString().trim(); ++ Log.i(TAG, "BromiteUpdater: obtained upstream version update response '%s'", response); ++ VersionNumber version = VersionNumber.fromString(response); ++ if (version != null) { ++ OmahaBase.updateLastPushedTimeStamp(System.currentTimeMillis()); ++ OmahaBase.setLatestUpstreamVersion(response); ++ callback.onResult(version); ++ return; ++ } ++ // retry after 1 hour ++ OmahaBase.updateLastPushedTimeStamp( ++ System.currentTimeMillis() - getUpdateNotificationInterval() - ++ DateUtils.HOUR_IN_MILLIS); ++ Log.e(TAG, "BromiteUpdater: failed to fetch upstream version, will retry in 1 hour"); ++ ++ callback.onResult(null); ++ }, ++ Profile.getLastUsedRegularProfile(), ++ UPSTREAM_VERSION_URL, /*timeout*/5000, /*follow_redirect*/false, TRAFFIC_ANNOTATION); ++ } ++ ++ private void postStatus(@UpdateStatusProvider.UpdateState int status, String updateUrl) { ++ mUpdateState = status; ++ mUpdateUrl = updateUrl; ++ PostTask.postTask(TaskTraits.UI_DEFAULT, mCallback); ++ } ++} +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java +new file mode 100644 +index 0000000000000..260e5f1225f6f +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateController.java +@@ -0,0 +1,51 @@ ++// Copyright 2019 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++package org.chromium.chrome.browser.omaha.inline; ++ ++import android.app.Activity; ++import android.content.Intent; ++ ++import androidx.annotation.Nullable; ++ ++import org.chromium.chrome.browser.omaha.UpdateStatusProvider; ++ ++/** ++ * Helper for gluing interactions with the Play store's AppUpdateManager with Chrome. This ++ * involves hooking up to Play as a listener for install state changes, should only happen if we are ++ * in the foreground. ++ */ ++public interface InlineUpdateController { ++ /** ++ * Enables or disables the controller. It will trigger an update check when previously disabled. ++ * @param enabled true iff the controller should be enabled. ++ */ ++ void setEnabled(boolean enabled); ++ ++ /** ++ * @return The current state of the inline update process. May be {@code null} if the state ++ * hasn't been determined yet. ++ */ ++ @Nullable ++ @UpdateStatusProvider.UpdateState ++ Integer getStatus(); ++ ++ /** ++ * @return The current update URL for the inline update process. May be an empty string if the state ++ * hasn't been determined yet or if state does not specify one. ++ */ ++ String getUpdateUrl(); ++ ++ /** ++ * Starts the update, if possible. This will send an {@link Intent} out to play, which may ++ * cause Chrome to move to the background. ++ * @param activity The {@link Activity} to use to interact with Play. ++ */ ++ void startUpdate(Activity activity); ++ ++ /** ++ * Completes the Play installation process, if possible. This may cause Chrome to restart. ++ */ ++ void completeUpdate(); ++} +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java +new file mode 100644 +index 0000000000000..de41882343e21 +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/inline/InlineUpdateControllerFactory.java +@@ -0,0 +1,21 @@ ++// Copyright 2019 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++package org.chromium.chrome.browser.omaha.inline; ++ ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.flags.ChromeFeatureList; ++import org.chromium.chrome.browser.omaha.UpdateConfigs; ++ ++/** ++ * A factory that creates an {@link InlineUpdateController} instance. ++ */ ++public class InlineUpdateControllerFactory { ++ /** ++ * @return A new {@link InlineUpdateController}. ++ */ ++ public static InlineUpdateController create(Runnable callback) { ++ return new BromiteInlineUpdateController(callback); ++ } ++} +diff --git a/chrome/browser/endpoint_fetcher/BUILD.gn b/chrome/browser/endpoint_fetcher/BUILD.gn +index f263a5c38edc5..03707c32d2e91 100644 +--- a/chrome/browser/endpoint_fetcher/BUILD.gn ++++ b/chrome/browser/endpoint_fetcher/BUILD.gn +@@ -18,6 +18,7 @@ android_library("java") { + sources = [ + "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java", + "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java", ++ "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java", + ] + } + +@@ -25,5 +26,6 @@ generate_jni("jni_headers") { + sources = [ + "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java", + "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointResponse.java", ++ "java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java", + ] + } +diff --git a/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc b/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc +index a6c7802b3e15b..e33f87ba0aae0 100644 +--- a/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc ++++ b/chrome/browser/endpoint_fetcher/endpoint_fetcher_android.cc +@@ -9,6 +9,7 @@ + #include "base/android/jni_string.h" + #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointFetcher_jni.h" + #include "chrome/browser/endpoint_fetcher/jni_headers/EndpointResponse_jni.h" ++#include "chrome/browser/endpoint_fetcher/jni_headers/EndpointHeaderResponse_jni.h" + #include "chrome/browser/profiles/profile_android.h" + #include "chrome/browser/signin/identity_manager_factory.h" + #include "chrome/common/channel_info.h" +@@ -31,6 +32,24 @@ static void OnEndpointFetcherComplete( + base::android::AttachCurrentThread(), + std::move(endpoint_response->response)))); + } ++ ++static void OnEndpointFetcherHeadComplete( ++ const base::android::JavaRef& jcaller, ++ // Passing the endpoint_fetcher ensures the endpoint_fetcher's ++ // lifetime extends to the callback and is not destroyed ++ // prematurely (which would result in cancellation of the request). ++ std::unique_ptr endpoint_fetcher, ++ std::unique_ptr endpoint_response) { ++ base::android::RunObjectCallbackAndroid( ++ jcaller, Java_EndpointHeaderResponse_createEndpointResponse( ++ base::android::AttachCurrentThread(), ++ base::android::ConvertUTF8ToJavaString( ++ base::android::AttachCurrentThread(), ++ std::move(endpoint_response->response)), ++ base::android::ConvertUTF8ToJavaString( ++ base::android::AttachCurrentThread(), ++ std::move(endpoint_response->redirect_url)))); ++} + } // namespace + + // TODO(crbug.com/1077537) Create a KeyProvider so +@@ -113,6 +132,7 @@ static void JNI_EndpointFetcher_NativeFetchWithNoAuth( + JNIEnv* env, + const base::android::JavaParamRef& jprofile, + const base::android::JavaParamRef& jurl, ++ jlong jtimeout, jboolean intercept_redirect, + jint jannotation_hash_code, + const base::android::JavaParamRef& jcallback) { + auto endpoint_fetcher = std::make_unique( +@@ -120,6 +140,9 @@ static void JNI_EndpointFetcher_NativeFetchWithNoAuth( + ->GetDefaultStoragePartition() + ->GetURLLoaderFactoryForBrowserProcess(), + GURL(base::android::ConvertJavaStringToUTF8(env, jurl)), ++ "GET", ++ jtimeout, ++ intercept_redirect, + net::NetworkTrafficAnnotationTag::FromJavaAnnotation( + jannotation_hash_code)); + auto* const endpoint_fetcher_ptr = endpoint_fetcher.get(); +@@ -131,3 +154,30 @@ static void JNI_EndpointFetcher_NativeFetchWithNoAuth( + std::move(endpoint_fetcher)), + nullptr); + } ++ ++static void JNI_EndpointFetcher_NativeHeadWithNoAuth( ++ JNIEnv* env, ++ const base::android::JavaParamRef& jprofile, ++ const base::android::JavaParamRef& jurl, ++ jlong jtimeout, jboolean intercept_redirect, ++ jint jannotation_hash_code, ++ const base::android::JavaParamRef& jcallback) { ++ auto endpoint_fetcher = std::make_unique( ++ ProfileAndroid::FromProfileAndroid(jprofile) ++ ->GetDefaultStoragePartition() ++ ->GetURLLoaderFactoryForBrowserProcess(), ++ GURL(base::android::ConvertJavaStringToUTF8(env, jurl)), ++ "HEAD", ++ jtimeout, ++ intercept_redirect, ++ net::NetworkTrafficAnnotationTag::FromJavaAnnotation( ++ jannotation_hash_code)); ++ auto* const endpoint_fetcher_ptr = endpoint_fetcher.get(); ++ endpoint_fetcher_ptr->PerformRequest( ++ base::BindOnce(&OnEndpointFetcherHeadComplete, ++ base::android::ScopedJavaGlobalRef(jcallback), ++ // unique_ptr endpoint_fetcher is passed until the callback ++ // to ensure its lifetime across the request. ++ std::move(endpoint_fetcher)), ++ nullptr); ++} +diff --git a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java +index c28fc4d9793b4..435ecf26fd1be 100644 +--- a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java ++++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointFetcher.java +@@ -70,6 +70,24 @@ public final class EndpointFetcher { + postData, timeout, headers, annotation.getHashCode(), callback); + } + ++ @MainThread ++ public static void nativeHeadWithNoAuth( ++ Callback callback, Profile profile, ++ String url, long timeout, boolean allow_redirect, ++ NetworkTrafficAnnotationTag annotation) { ++ EndpointFetcherJni.get().nativeHeadWithNoAuth( ++ profile, url, timeout, allow_redirect, annotation.getHashCode(), callback); ++ } ++ ++ @MainThread ++ public static void nativeFetchWithNoAuth( ++ Callback callback, Profile profile, ++ String url, long timeout, boolean allow_redirect, ++ NetworkTrafficAnnotationTag annotation) { ++ EndpointFetcherJni.get().nativeFetchWithNoAuth( ++ profile, url, timeout, allow_redirect, annotation.getHashCode(), callback); ++ } ++ + @NativeMethods + public interface Natives { + void nativeFetchOAuth(Profile profile, String oathConsumerName, String url, +@@ -78,7 +96,13 @@ public final class EndpointFetcher { + void nativeFetchChromeAPIKey(Profile profile, String url, String httpsMethod, + String contentType, String postData, long timeout, String[] headers, + int annotationHashCode, Callback callback); +- void nativeFetchWithNoAuth(Profile profile, String url, int annotationHashCode, ++ void nativeFetchWithNoAuth( ++ Profile profile, String url, long timeout, boolean allow_redirect, ++ int annotationHashCode, + Callback callback); ++ void nativeHeadWithNoAuth( ++ Profile profile, String url, long timeout, boolean allow_redirect, ++ int annotationHashCode, ++ Callback callback); + } + } +diff --git a/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java +new file mode 100644 +index 0000000000000..a7bd35381ccd1 +--- /dev/null ++++ b/chrome/browser/endpoint_fetcher/java/src/org/chromium/chrome/browser/endpoint_fetcher/EndpointHeaderResponse.java +@@ -0,0 +1,31 @@ ++// Copyright 2019 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++package org.chromium.chrome.browser.endpoint_fetcher; ++ ++import org.chromium.base.annotations.CalledByNative; ++ ++public class EndpointHeaderResponse { ++ private final String mResponseString; ++ private final String mRedirectUrl; ++ ++ public EndpointHeaderResponse(String responseString, String redirectUrl) { ++ mResponseString = responseString; ++ mRedirectUrl = redirectUrl; ++ } ++ ++ public String getResponseString() { ++ return mResponseString; ++ } ++ ++ public String getRedirectUrl() { ++ return mRedirectUrl; ++ } ++ ++ @CalledByNative ++ private static EndpointHeaderResponse createEndpointResponse( ++ String response, String redirectUrl) { ++ return new EndpointHeaderResponse(response, redirectUrl); ++ } ++} +diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc +index db4d5e5031e64..26ad6f888a5e3 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.cc ++++ b/chrome/browser/flags/android/chrome_feature_list.cc +@@ -252,6 +252,7 @@ const base::Feature* const kFeaturesExposedToJava[] = { + &kNotificationPermissionBottomSheet, + &kPageAnnotationsService, + &kPreconnectOnTabCreation, ++ &kInlineUpdateFlow, + &kBookmarksImprovedSaveFlow, + &kBookmarksRefresh, + &kOmahaMinSdkVersionAndroid, +@@ -687,6 +688,10 @@ BASE_FEATURE(kContextMenuTranslateWithGoogleLens, + "ContextMenuTranslateWithGoogleLens", + base::FEATURE_DISABLED_BY_DEFAULT); + ++BASE_FEATURE(kInlineUpdateFlow, ++ "InlineUpdateFlow", ++ base::FEATURE_ENABLED_BY_DEFAULT); ++ + BASE_FEATURE(kLensCameraAssistedSearch, + "LensCameraAssistedSearch", + base::FEATURE_ENABLED_BY_DEFAULT); +diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h +index b9f8bb074b60f..120623eb985aa 100644 +--- a/chrome/browser/flags/android/chrome_feature_list.h ++++ b/chrome/browser/flags/android/chrome_feature_list.h +@@ -105,6 +105,7 @@ BASE_DECLARE_FEATURE(kFoldableJankFix); + BASE_DECLARE_FEATURE(kGridTabSwitcherLandscapeAspectRatioPhones); + BASE_DECLARE_FEATURE(kImprovedIncognitoScreenshot); + BASE_DECLARE_FEATURE(kIncognitoReauthenticationForAndroid); ++BASE_DECLARE_FEATURE(kInlineUpdateFlow); + BASE_DECLARE_FEATURE(kIncognitoScreenshot); + BASE_DECLARE_FEATURE(kInfobarScrollOptimization); + BASE_DECLARE_FEATURE(kImprovedA2HS); +diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +index 8817964921a27..c287e9b0bf4b8 100644 +--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java ++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +@@ -291,6 +291,7 @@ public abstract class ChromeFeatureList { + "IncognitoReauthenticationForAndroid"; + public static final String INCOGNITO_SCREENSHOT = "IncognitoScreenshot"; + public static final String INFOBAR_SCROLL_OPTIMIZATION = "InfobarScrollOptimization"; ++ public static final String INLINE_UPDATE_FLOW = "InlineUpdateFlow"; + public static final String INSTALLABLE_AMBIENT_BADGE_INFOBAR = "InstallableAmbientBadgeInfoBar"; + public static final String INSTALLABLE_AMBIENT_BADGE_MESSAGE = "InstallableAmbientBadgeMessage"; + public static final String INSTANCE_SWITCHER = "InstanceSwitcher"; +diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd +index 578abb03eb258..c004ab5d7a186 100644 +--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd ++++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd +@@ -2106,6 +2106,12 @@ Your Google account may have other forms of browsing history like searches and a + + Chrome updates are no longer supported for this version of Android + ++ ++ Allow checking for updates ++ ++ ++ Notify about new releases by periodically checking for their availability ++ + + + +@@ -3722,7 +3728,10 @@ To change this setting, <resetlink>reset sync

+ +- Update Chrome ++ Update Bromite ++ ++ ++ Update not available. Read more + + + Newer version is available +@@ -3733,6 +3742,18 @@ To change this setting, <resetlink>reset sync

+ Android version is unsupported + ++ ++ Downloading… ++ ++ ++ Couldn’t download ++ ++ ++ Update ready ++ ++ ++ Restart Bromite ++ + + New window + +diff --git a/components/endpoint_fetcher/endpoint_fetcher.cc b/components/endpoint_fetcher/endpoint_fetcher.cc +index 9ce615ea489f0..6fa68a102c1f2 100644 +--- a/components/endpoint_fetcher/endpoint_fetcher.cc ++++ b/components/endpoint_fetcher/endpoint_fetcher.cc +@@ -18,6 +18,11 @@ + #include "services/network/public/cpp/simple_url_loader.h" + #include "services/network/public/mojom/url_response_head.mojom.h" + ++// used for the Bromite customization ++#include "net/base/load_flags.h" ++#include "net/http/http_status_code.h" ++#include "services/network/public/cpp/resource_request.h" ++ + namespace { + const char kContentTypeKey[] = "Content-Type"; + const char kDeveloperKey[] = "X-Developer-Key"; +@@ -64,6 +69,7 @@ EndpointFetcher::EndpointFetcher( + http_method_(http_method), + content_type_(content_type), + timeout_ms_(timeout_ms), ++ intercept_redirect_(false), + post_data_(post_data), + headers_(headers), + annotation_tag_(annotation_tag), +@@ -82,6 +88,7 @@ EndpointFetcher::EndpointFetcher( + http_method_("GET"), + content_type_(std::string()), + timeout_ms_(0), ++ intercept_redirect_(false), + post_data_(std::string()), + annotation_tag_(annotation_tag), + url_loader_factory_(url_loader_factory), +@@ -107,6 +114,7 @@ EndpointFetcher::EndpointFetcher( + http_method_(http_method), + content_type_(content_type), + timeout_ms_(timeout_ms), ++ intercept_redirect_(false), + post_data_(post_data), + annotation_tag_(annotation_tag), + url_loader_factory_(url_loader_factory), +@@ -134,6 +142,7 @@ EndpointFetcher::EndpointFetcher( + http_method_(http_method), + content_type_(content_type), + timeout_ms_(timeout_ms), ++ intercept_redirect_(false), + post_data_(post_data), + headers_(headers), + cors_exempt_headers_(cors_exempt_headers), +@@ -146,11 +155,29 @@ EndpointFetcher::EndpointFetcher( + EndpointFetcher::EndpointFetcher( + const net::NetworkTrafficAnnotationTag& annotation_tag) + : timeout_ms_(kDefaultTimeOutMs), ++ intercept_redirect_(false), + annotation_tag_(annotation_tag), + identity_manager_(nullptr), + consent_level_(absl::nullopt), + sanitize_response_(true) {} + ++// constructor used by Bromite ++EndpointFetcher::EndpointFetcher( ++ const scoped_refptr& url_loader_factory, ++ const GURL& url, ++ const std::string& http_method, ++ int64_t timeout_ms, ++ const bool intercept_redirect, ++ const net::NetworkTrafficAnnotationTag& annotation_tag) ++ : url_(url), ++ http_method_(http_method), ++ timeout_ms_(timeout_ms), ++ intercept_redirect_(intercept_redirect), ++ annotation_tag_(annotation_tag), ++ url_loader_factory_(url_loader_factory), ++ identity_manager_(nullptr), ++ sanitize_response_(false) {} ++ + EndpointFetcher::~EndpointFetcher() = default; + + void EndpointFetcher::Fetch(EndpointFetcherCallback endpoint_fetcher_callback) { +@@ -209,6 +236,8 @@ void EndpointFetcher::PerformRequest( + resource_request->method = http_method_; + resource_request->url = url_; + resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; ++ resource_request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE ++ | net::LOAD_DO_NOT_SAVE_COOKIES; + if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) { + resource_request->headers.SetHeader(kContentTypeKey, content_type_); + } +@@ -239,25 +268,52 @@ void EndpointFetcher::PerformRequest( + default: + break; + } ++ ++ if (intercept_redirect_ == true) { ++ // will need manual mode to capture the landing page URL ++ resource_request->redirect_mode = network::mojom::RedirectMode::kManual; // default is kFollow ++ } ++ + // TODO(crbug.com/997018) Make simple_url_loader_ local variable passed to + // callback + simple_url_loader_ = network::SimpleURLLoader::Create( + std::move(resource_request), annotation_tag_); ++ simple_url_loader_->SetTimeoutDuration(base::Milliseconds(timeout_ms_)); ++ simple_url_loader_->SetAllowHttpErrorResults(true); ++ ++ if (!response_) { ++ //RFC: what is this for? ++ response_ = std::make_unique(); ++ } ++ if (intercept_redirect_ == true) { ++ // use a callback to capture landing page URL ++ simple_url_loader_->SetOnRedirectCallback(base::BindRepeating( ++ &EndpointFetcher::OnSimpleLoaderRedirect, base::Unretained(this))); ++ } + + if (base::EqualsCaseInsensitiveASCII(http_method_, "POST")) { + simple_url_loader_->AttachStringForUpload(post_data_, content_type_); + } + simple_url_loader_->SetRetryOptions(kNumRetries, + network::SimpleURLLoader::RETRY_ON_5XX); +- simple_url_loader_->SetTimeoutDuration(base::Milliseconds(timeout_ms_)); +- simple_url_loader_->SetAllowHttpErrorResults(true); +- network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback = ++ ++ LOG(INFO) << "performing " << http_method_ << " request to " << url_; ++ if (base::EqualsCaseInsensitiveASCII(http_method_, "HEAD")) { ++ endpoint_fetcher_callback_ = std::move(endpoint_fetcher_callback); ++ ++ simple_url_loader_->DownloadHeadersOnly( ++ url_loader_factory_.get(), ++ base::BindOnce(&EndpointFetcher::OnURLLoadComplete, ++ base::Unretained(this))); ++ } else { ++ network::SimpleURLLoader::BodyAsStringCallback body_as_string_callback = + base::BindOnce(&EndpointFetcher::OnResponseFetched, + weak_ptr_factory_.GetWeakPtr(), + std::move(endpoint_fetcher_callback)); +- simple_url_loader_->DownloadToString( +- url_loader_factory_.get(), std::move(body_as_string_callback), +- network::SimpleURLLoader::kMaxBoundedStringDownloadSize); ++ simple_url_loader_->DownloadToString( ++ url_loader_factory_.get(), std::move(body_as_string_callback), ++ network::SimpleURLLoader::kMaxBoundedStringDownloadSize); ++ } + } + + void EndpointFetcher::OnResponseFetched( +@@ -336,3 +392,38 @@ void EndpointFetcher::OnSanitizationResult( + std::string EndpointFetcher::GetUrlForTesting() { + return url_.spec(); + } ++ ++void EndpointFetcher::OnSimpleLoaderRedirect( ++ const GURL& url_before_redirect, ++ const net::RedirectInfo& redirect_info, ++ const network::mojom::URLResponseHead& response_head, ++ std::vector* removed_headers) { ++ url_ = redirect_info.new_url; ++ if (response_->redirect_url.empty()) { ++ response_->redirect_url = url_.spec(); ++ response_->response = std::to_string(redirect_info.status_code); ++ } else { ++ LOG(INFO) << "BromiteUpdater: redirect URL is not empty, status code is " << redirect_info.status_code; ++ } ++ ++ std::move(endpoint_fetcher_callback_).Run(std::move(response_)); ++} ++ ++void EndpointFetcher::OnURLLoadComplete( ++ scoped_refptr headers) { ++ if (!endpoint_fetcher_callback_) ++ return; ++ ++ if (headers) { ++ if (response_->redirect_url.empty()) { ++ std::string location; ++ if (simple_url_loader_->ResponseInfo()->headers->IsRedirect(&location)) { ++ response_->redirect_url = location; ++ } ++ } ++ } ++ ++ std::string net_error = net::ErrorToString(simple_url_loader_->NetError()); ++ response_->response = net_error; ++ std::move(endpoint_fetcher_callback_).Run(std::move(response_)); ++} +diff --git a/components/endpoint_fetcher/endpoint_fetcher.h b/components/endpoint_fetcher/endpoint_fetcher.h +index 1b31130514f6b..2e5738e03d6a4 100644 +--- a/components/endpoint_fetcher/endpoint_fetcher.h ++++ b/components/endpoint_fetcher/endpoint_fetcher.h +@@ -17,6 +17,8 @@ + #include "net/traffic_annotation/network_traffic_annotation.h" + #include "services/data_decoder/public/cpp/json_sanitizer.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "services/network/public/cpp/resource_request.h" ++#include "services/network/public/mojom/url_response_head.mojom.h" + + namespace network { + struct ResourceRequest; +@@ -39,6 +41,8 @@ enum class FetchErrorType { + + struct EndpointResponse { + std::string response; ++ long last_modified; ++ std::string redirect_url; + int http_status_code{-1}; + absl::optional error_type; + }; +@@ -92,6 +96,14 @@ class EndpointFetcher { + const GURL& url, + const net::NetworkTrafficAnnotationTag& annotation_tag); + ++ // Constructor if no authentication is needed, with timeout ++ EndpointFetcher(const scoped_refptr& url_loader_factory, ++ const GURL& url, ++ const std::string& http_method, ++ int64_t timeout_ms, ++ const bool intercept_redirect, ++ const net::NetworkTrafficAnnotationTag& annotation_tag); ++ + // Used for tests. Can be used if caller constructs their own + // url_loader_factory and identity_manager. + EndpointFetcher( +@@ -147,6 +159,11 @@ class EndpointFetcher { + void OnSanitizationResult(std::unique_ptr response, + EndpointFetcherCallback endpoint_fetcher_callback, + data_decoder::JsonSanitizer::Result result); ++ void OnURLLoadComplete(scoped_refptr headers); ++ void OnSimpleLoaderRedirect(const GURL& url_before_redirect, ++ const net::RedirectInfo& redirect_info, ++ const network::mojom::URLResponseHead& response_head, ++ std::vector* removed_headers); + + enum AuthType { CHROME_API_KEY, OAUTH, NO_AUTH }; + AuthType auth_type_; +@@ -154,10 +171,11 @@ class EndpointFetcher { + // Members set in constructor to be passed to network::ResourceRequest or + // network::SimpleURLLoader. + const std::string oauth_consumer_name_; +- const GURL url_; ++ GURL url_; + const std::string http_method_; + const std::string content_type_; + int64_t timeout_ms_; ++ const bool intercept_redirect_; + const std::string post_data_; + const std::vector headers_; + const std::vector cors_exempt_headers_; +@@ -181,6 +199,9 @@ class EndpointFetcher { + access_token_fetcher_; + std::unique_ptr simple_url_loader_; + ++ EndpointFetcherCallback endpoint_fetcher_callback_; ++ std::unique_ptr response_; ++ + base::WeakPtrFactory weak_ptr_factory_{this}; + }; + +-- +2.34.1 + diff --git a/build/bromite_patches/Bromite-package-name.patch b/build/bromite_patches/Bromite-package-name.patch new file mode 100644 index 0000000000000000000000000000000000000000..06de7bd07c08249811101aa836689c56e9998a3f --- /dev/null +++ b/build/bromite_patches/Bromite-package-name.patch @@ -0,0 +1,26 @@ +From 393b0ea90845d4f46fb38b6c148ae1ebd3cd3b38 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 8 Jan 2022 19:42:34 +0100 +Subject: [PATCH 023/192] Bromite package name + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/chrome_public_apk_tmpl.gni | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni +index 7d37081a3f317..8a9c993297776 100644 +--- a/chrome/android/chrome_public_apk_tmpl.gni ++++ b/chrome/android/chrome_public_apk_tmpl.gni +@@ -27,7 +27,7 @@ declare_args() { + # WebView providers which declare one of a handful of package names. See + # https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/build-instructions.md#Changing-package-name + # for details. +- chrome_public_manifest_package = "org.chromium.chrome" ++ chrome_public_manifest_package = "org.bromite.bromite" + if (use_stable_package_name_for_trichrome) { + chrome_public_manifest_package += ".stable" + } else if (android_channel != "default" && android_channel != "stable") { +-- +2.34.1 + diff --git a/build/bromite_patches/Bromite-subresource-adblocker.patch b/build/bromite_patches/Bromite-subresource-adblocker.patch new file mode 100644 index 0000000000000000000000000000000000000000..2229eda11e3e0cd96f665868b44f9ecde09b3a23 --- /dev/null +++ b/build/bromite_patches/Bromite-subresource-adblocker.patch @@ -0,0 +1,1996 @@ +From 8d9da224cf38c33591926b916b43719df7801235 Mon Sep 17 00:00:00 2001 +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat, 14 Sep 2019 10:20:08 +0200 +Subject: [PATCH 091/192] Bromite subresource adblocker + +Add option to configure the ad blocker filters URL +Disable look-alike, metrics, ablation and navigation throttles +Do not use experiments to enable/disable presets +Always enable ad filtering +Download filters by checking Last-Modified header first +Fix RestoreForeignSessionTab by recreating the tab (issue #681) +Enable AutomaticLazyFrameLoadingToAds and AutomaticLazyFrameLoadingToEmbeds features + +License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html +--- + chrome/android/BUILD.gn | 1 + + chrome/android/chrome_java_resources.gni | 2 + + chrome/android/chrome_java_sources.gni | 2 + + .../java/res/layout/adblock_editor.xml | 67 +++++ + chrome/android/java/res/values/styles.xml | 18 ++ + chrome/android/java/res/values/values.xml | 2 + + .../java/res/xml/adblock_preferences.xml | 25 ++ + .../android/java/res/xml/main_preferences.xml | 5 + + .../browser/settings/AdBlockEditor.java | 91 ++++++ + .../browser/settings/AdBlockPreferences.java | 61 ++++ + .../chrome/browser/tabmodel/TabModelImpl.java | 2 +- + chrome/app/generated_resources.grd | 10 + + chrome/browser/after_startup_task_utils.cc | 4 + + chrome/browser/browser_process.h | 6 + + chrome/browser/browser_process_impl.cc | 20 ++ + chrome/browser/browser_process_impl.h | 2 + + chrome/browser/chrome_browser_main.cc | 3 + + .../flags/android/cached_feature_flags.cc | 11 + + .../browser/flags/CachedFeatureFlags.java | 10 + + .../net/system_network_context_manager.cc | 3 + + .../sessions/session_restore_android.cc | 6 +- + .../strings/android_chrome_strings.grd | 14 + + chrome/common/pref_names.h | 3 + + .../strings/android/site_settings.grdp | 4 +- + components/component_updater/BUILD.gn | 7 + + .../adblock_updater_service.cc | 268 ++++++++++++++++++ + .../adblock_updater_service.h | 98 +++++++ + .../download_filters_task.cc | 239 ++++++++++++++++ + .../component_updater/download_filters_task.h | 129 +++++++++ + ...ent_subresource_filter_throttle_manager.cc | 15 + + ...tent_subresource_filter_throttle_manager.h | 2 + + .../content/browser/ruleset_service.cc | 42 ++- + .../content/browser/ruleset_service.h | 7 +- + .../content/browser/ruleset_version.h | 4 + + .../unindexed_ruleset_stream_generator.cc | 3 + + .../browser/verified_ruleset_dealer.cc | 4 + + .../browser/subresource_filter_features.cc | 116 +------- + .../core/common/indexed_ruleset.cc | 5 +- + third_party/blink/common/features.cc | 12 +- + 39 files changed, 1188 insertions(+), 135 deletions(-) + create mode 100644 chrome/android/java/res/layout/adblock_editor.xml + create mode 100644 chrome/android/java/res/xml/adblock_preferences.xml + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/settings/AdBlockEditor.java + create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/settings/AdBlockPreferences.java + create mode 100644 components/component_updater/adblock_updater_service.cc + create mode 100644 components/component_updater/adblock_updater_service.h + create mode 100644 components/component_updater/download_filters_task.cc + create mode 100644 components/component_updater/download_filters_task.h + +diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn +index ff2fb13bb9395..04622b7bf6f1f 100644 +--- a/chrome/android/BUILD.gn ++++ b/chrome/android/BUILD.gn +@@ -288,6 +288,7 @@ if (current_toolchain == default_toolchain) { + "//chrome/android/modules/image_editor/provider:java", + "//chrome/android/modules/stack_unwinder/provider:java", + "//chrome/android/webapk/libs/client:client_java", ++ "//chrome/browser/endpoint_fetcher:java", + "//chrome/android/webapk/libs/common:common_java", + "//chrome/android/webapk/libs/common:splash_java", + "//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java", +diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni +index 25dd9bddf366d..e47f8cb9f6ff6 100644 +--- a/chrome/android/chrome_java_resources.gni ++++ b/chrome/android/chrome_java_resources.gni +@@ -460,6 +460,7 @@ chrome_java_resources = [ + "java/res/layout/account_chooser_dialog_title.xml", + "java/res/layout/account_divider_preference.xml", + "java/res/layout/account_management_account_row.xml", ++ "java/res/layout/adblock_editor.xml", + "java/res/layout/auto_sign_in_first_run_dialog.xml", + "java/res/layout/autofill_billing_address_dropdown.xml", + "java/res/layout/autofill_card_name_and_number.xml", +@@ -656,6 +657,7 @@ chrome_java_resources = [ + "java/res/xml/about_chrome_preferences.xml", + "java/res/xml/account_management_preferences.xml", + "java/res/xml/ad_services_config.xml", ++ "java/res/xml/adblock_preferences.xml", + "java/res/xml/bookmark_widget_info.xml", + "java/res/xml/clear_browsing_data_preferences_tab.xml", + "java/res/xml/contextual_search_preferences.xml", +diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni +index c7972b041e693..4f5cf7b9337c0 100644 +--- a/chrome/android/chrome_java_sources.gni ++++ b/chrome/android/chrome_java_sources.gni +@@ -946,6 +946,8 @@ chrome_java_sources = [ + "java/src/org/chromium/chrome/browser/permissions/PermissionSettingsBridge.java", + "java/src/org/chromium/chrome/browser/permissions/PermissionUpdateRequester.java", + "java/src/org/chromium/chrome/browser/photo_picker/DecoderServiceImpl.java", ++ "java/src/org/chromium/chrome/browser/settings/AdBlockEditor.java", ++ "java/src/org/chromium/chrome/browser/settings/AdBlockPreferences.java", + "java/src/org/chromium/chrome/browser/policy/PolicyAuditor.java", + "java/src/org/chromium/chrome/browser/policy/PolicyAuditorBridge.java", + "java/src/org/chromium/chrome/browser/prerender/ChromePrerenderServiceImpl.java", +diff --git a/chrome/android/java/res/layout/adblock_editor.xml b/chrome/android/java/res/layout/adblock_editor.xml +new file mode 100644 +index 0000000000000..aced0dbca5a2b +--- /dev/null ++++ b/chrome/android/java/res/layout/adblock_editor.xml +@@ -0,0 +1,67 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml +index d130751c57765..d189cd1fcfba5 100644 +--- a/chrome/android/java/res/values/styles.xml ++++ b/chrome/android/java/res/values/styles.xml +@@ -232,6 +232,24 @@ found in the LICENSE file. + + + ++ ++ ++ ++ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ + + +- +diff --git a/chrome/browser/resources/downloads/icons.html b/chrome/browser/resources/downloads/icons.html +index 5315618cea58c..584e51cf493c1 100644 +--- a/chrome/browser/resources/downloads/icons.html ++++ b/chrome/browser/resources/downloads/icons.html +@@ -3,7 +3,7 @@ + + + + +diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js +index 9e241bd997d9a..7200af4193781 100644 +--- a/chrome/browser/resources/gaia_auth_host/authenticator.js ++++ b/chrome/browser/resources/gaia_auth_host/authenticator.js +@@ -768,7 +768,7 @@ export class Authenticator extends EventTarget { + url = appendParam( + url, 'continue', + data.gaiaUrl + 'programmatic_auth_chromeos?hl=' + data.hl + +- '&scope=https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthLogin&' + ++ '&scope=https%3A%2F%2Fwww.9oo91e.qjz9zk%2Faccounts%2FOAuthLogin&' + + 'client_id=' + encodeURIComponent(data.clientId) + + '&access_type=offline'); + +diff --git a/chrome/browser/resources/gaia_auth_host/saml_password_attributes.js b/chrome/browser/resources/gaia_auth_host/saml_password_attributes.js +index 59811ef53aa4a..ea9bacc1baf23 100644 +--- a/chrome/browser/resources/gaia_auth_host/saml_password_attributes.js ++++ b/chrome/browser/resources/gaia_auth_host/saml_password_attributes.js +@@ -17,7 +17,7 @@ import {decodeTimestamp} from './saml_timestamps.js'; + const MAX_SANE_XML_LENGTH = 50 * 1024; // 50 KB + + /** @const @private {string} Schema name prefix. */ +- const SCHEMA_NAME_PREFIX = 'http://schemas.google.com/saml/2019/'; ++ const SCHEMA_NAME_PREFIX = 'http://schemas.9oo91e.qjz9zk/saml/2019/'; + + /** @const @private {string} Schema name for password modified timestamp. */ + const PASSWORD_MODIFIED_TIMESTAMP = 'passwordmodifiedtimestamp'; +diff --git a/chrome/browser/resources/hangout_services/manifest.json b/chrome/browser/resources/hangout_services/manifest.json +index 788275469b586..bceaaae958834 100644 +--- a/chrome/browser/resources/hangout_services/manifest.json ++++ b/chrome/browser/resources/hangout_services/manifest.json +@@ -9,7 +9,7 @@ + "manifest_version": 2, + "externally_connectable": { + "matches": [ +- "https://*.google.com/*" ++ "https://*.9oo91e.qjz9zk/*" + ] + }, + "background": { +diff --git a/chrome/browser/resources/inspect/inspect.html b/chrome/browser/resources/inspect/inspect.html +index a1a0f3e4b826f..f8ba1cb5eb674 100644 +--- a/chrome/browser/resources/inspect/inspect.html ++++ b/chrome/browser/resources/inspect/inspect.html +@@ -46,7 +46,7 @@ found in the LICENSE file. + + +

+@@ -102,7 +102,7 @@ found in the LICENSE file. +
+ Define the listening port on your device that maps to a port accessible + from your development machine. +- Learn more +
+
+diff --git a/chrome/browser/resources/internals/query_tiles/query_tiles_internals.html b/chrome/browser/resources/internals/query_tiles/query_tiles_internals.html +index 948d73cd807ac..4eb7f3d5f0fa6 100644 +--- a/chrome/browser/resources/internals/query_tiles/query_tiles_internals.html ++++ b/chrome/browser/resources/internals/query_tiles/query_tiles_internals.html +@@ -24,7 +24,7 @@ +

Server address

+
+ +- ++ + + + +diff --git a/chrome/browser/resources/media/mei_preload/manifest.json b/chrome/browser/resources/media/mei_preload/manifest.json +index 1b4fa8a1e63eb..549b53788a027 100644 +--- a/chrome/browser/resources/media/mei_preload/manifest.json ++++ b/chrome/browser/resources/media/mei_preload/manifest.json +@@ -3,6 +3,6 @@ + "icons": {}, + "version": "1.0.7.1652906823", + "manifest_version": 2, +- "update_url": "https://clients2.google.com/service/update2/crx", ++ "update_url": "https://clients2.9oo91e.qjz9zk/service/update2/crx", + "description": "Contains preloaded data for Media Engagement" + } +diff --git a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html b/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html +index 4f5d7a7c0cb7f..b7d113ebba365 100644 +--- a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html ++++ b/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html +@@ -2,7 +2,7 @@ + + + + + +@@ -47,7 +47,7 @@ + + + + +diff --git a/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.html b/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.html +index 97fbfd0d2d2a8..0e9a2967b8ece 100644 +--- a/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.html ++++ b/chrome/browser/resources/nearby_share/shared/nearby_shared_share_type_icons.html +@@ -2,7 +2,7 @@ + + + + + +diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html +index 1fb63739664d9..51425cea9a76d 100644 +--- a/chrome/browser/resources/net_internals/index.html ++++ b/chrome/browser/resources/net_internals/index.html +@@ -21,7 +21,7 @@ found in the LICENSE file. +
+
+ The net-internals events viewer and related functionality has been removed. +- Please use chrome://net-export to save netlogs and the external netlog_viewer to view them. ++ Please use chrome://net-export to save netlogs and the external netlog_viewer to view them. +
+ +
+@@ -73,8 +73,8 @@ found in the LICENSE file. +
+ HSTS is HTTP Strict Transport Security: a way for sites to elect to + always use HTTPS. See +- +- https://www.chromium.org/hsts. PKP is Public Key Pinning: Chrome ++ ++ https://www.ch40m1um.qjz9zk/hsts. PKP is Public Key Pinning: Chrome + "pins" certain public keys for certain sites in official builds.
+ +

Add HSTS domain

+diff --git a/chrome/browser/resources/network_speech_synthesis/manifest.json b/chrome/browser/resources/network_speech_synthesis/manifest.json +index e4835edb53168..835db49f4f262 100644 +--- a/chrome/browser/resources/network_speech_synthesis/manifest.json ++++ b/chrome/browser/resources/network_speech_synthesis/manifest.json +@@ -10,7 +10,7 @@ + "permissions": [ + "systemPrivate", + "ttsEngine", +- "https://www.google.com/" ++ "https://www.9oo91e.qjz9zk/" + ], + "tts_engine": { + "voices": [ +diff --git a/chrome/browser/resources/network_speech_synthesis/tts_extension.js b/chrome/browser/resources/network_speech_synthesis/tts_extension.js +index 813e6afb81420..ffc1608591533 100644 +--- a/chrome/browser/resources/network_speech_synthesis/tts_extension.js ++++ b/chrome/browser/resources/network_speech_synthesis/tts_extension.js +@@ -30,7 +30,7 @@ TtsExtension.prototype = { + * @const + * @private + */ +- SPEECH_SERVER_URL_: 'https://www.google.com/speech-api/v2/synthesize?' + ++ SPEECH_SERVER_URL_: 'https://www.9oo91e.qjz9zk/speech-api/v2/synthesize?' + + 'enc=mpeg&client=chromium', + + /** +diff --git a/chrome/browser/resources/new_tab_page/modules/photos/module.html b/chrome/browser/resources/new_tab_page/modules/photos/module.html +index bee0651bf7f4f..b2f3d9db15003 100644 +--- a/chrome/browser/resources/new_tab_page/modules/photos/module.html ++++ b/chrome/browser/resources/new_tab_page/modules/photos/module.html +@@ -569,7 +569,7 @@ + + +