From 63538665ec27b1dc46e0b2f7a263e91a1812d1b6 Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:26:26 +0100 Subject: [PATCH 001/116] feat: OS-14592 Create BrightSign CI workflows * workflows: Copy across BrightSign CI workflows from dev. * workflows: Temp change to debug new auth method * workflows: Another temp hack to debug * workflows: Another temp hack to debug * workflows: Change to use Github App authentication instead of using a PAT token. * workflows: Use version of ec2 runner with a fix for authentication in stop case. * workflows: Update ec2 runner version again. * workflows: Update to an AMI image that has pkg-config installed. * workflows: Use new leave_ec2_instance_running debug parameter from the ec2 runner action * workflows: Temp hack to stop EC2 instances being terminated after test run to debug failures. * workflows: Update to the ami that has pkg-config installed. * workflows: Update to a newer ami image that has dependencies installed properly. * workflows: Remove hack that set leave_ec2_instance_running to true for debugging purposes. * workflows: Switch to correct AWS account. OS-14873: Switch to using chromium export_tarball.py (#10) * workflows: Switch to using chromium export_tarball.py to create src tarball for release * workflows: Use ELECTRON_VERSION env var when calling export_tarball.py * workflows: Add missing $ when accessing env.SOURCE_RELEASE_FILE_NAME * workflows: Set src tarball upload to be after gclient sync again * workflows: Fix electron_package_name from .tgz to .gz * workflows: Remove commented out line Cherry picked from: 23ffb1ea714c6bd45401bd35df217866dcf78453: feat: OS-14592 Create BrightSign CI workflows 5027fb73b93926afb8e9ab438e2b24bfb75b69fb: OS-14873: Switch to using chromium export_tarball.py (#10) --- .../workflows/bs_electron_build_and_test.yml | 299 ++++++++++++++++++ .github/workflows/bs_electron_ci_ec2.yml | 108 +++++++ .../bs_electron_ci_manual_process.yml | 41 +++ .../bs_electron_ci_release_process.yml | 35 ++ .../workflows/bs_electron_ci_test_process.yml | 27 ++ 5 files changed, 510 insertions(+) create mode 100644 .github/workflows/bs_electron_build_and_test.yml create mode 100644 .github/workflows/bs_electron_ci_ec2.yml create mode 100644 .github/workflows/bs_electron_ci_manual_process.yml create mode 100644 .github/workflows/bs_electron_ci_release_process.yml create mode 100644 .github/workflows/bs_electron_ci_test_process.yml diff --git a/.github/workflows/bs_electron_build_and_test.yml b/.github/workflows/bs_electron_build_and_test.yml new file mode 100644 index 0000000000000..ce2d56f23a9b3 --- /dev/null +++ b/.github/workflows/bs_electron_build_and_test.yml @@ -0,0 +1,299 @@ +name: 'BrightSign Build and Test Electron: Build and test workflow' +on: + workflow_call: + inputs: + runner_name: + required: true + type: string + + github_hosted_runner: + required: true + type: boolean + + build_type: + required: true + type: string + # "test" or "release" + + aws_arn_role: + required: true + type: string + + aws_region: + required: true + type: string + +jobs: + build-and-test-electron: + name: Build and Test Electron + runs-on: ${{ inputs.runner_name }} + defaults: + run: + working-directory: ./ + + steps: + # Cleanups only needed for self hosted runners + # - name: Clean up work dir (temp instead of colpal/actions-clean@v1, until docker usage issue with sock file permissions sorted) + # run: rm -rf * + + # - name: Cleanup work dir + # uses: colpal/actions-clean@v1 + # if: ${{ always() }} # To ensure this step runs even when earlier steps fail + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-region: ${{ inputs.aws_region}} + role-to-assume: ${{ inputs.aws_arn_role }} + # Set role-duration-seconds to 10 hours + role-duration-seconds: 36000 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Download config file and set env vars from it + run: | + aws s3 cp s3://electron-ci-config-bucket/config.json . + aws s3 cp s3://electron-ci-config-bucket/set_github_env_vars.py . + python3 set_github_env_vars.py --file config.json + + - name: Check build type + if: inputs.build_type != 'test' && inputs.build_type != 'release' + run: | + echo "ERROR> build_type is set to \"${{ inputs.build_type }}\" and should be set to \"test\" or \"release\"" + exit 1 + + - name: Install dependencies if Github hosted runner + if: inputs.github_hosted_runner == true + run: | + # From https://www.electronjs.org/docs/latest/development/build-instructions-linux + sudo apt-get update + sudo apt-get install -y build-essential clang libdbus-1-dev libgtk-3-dev \ + libnotify-dev libasound2-dev libcap-dev \ + libcups2-dev libxtst-dev \ + libxss1 libnss3-dev gcc-multilib g++-multilib curl \ + gperf bison python3-dbusmock openjdk-8-jre + + # virtual framebuffer for running tests. + sudo apt install -y xvfb + + # A fix for test case (to remove fonts-liberation that seems to be installed by default in Github default ubuntu image)... + # 2022-12-23T10:56:48.4520914Z not ok 2079 font fallback should use Helvetica for sans-serif on Mac, and Arial on Windows and Linux + # 2022-12-23T10:56:48.4522432Z AssertionError: expected 'Liberation Sans' to equal 'DejaVu Sans' + # 2022-12-23T10:56:48.4523556Z at Context. (electron/spec/chromium-spec.ts:2222:38) + sudo apt-get remove -y fonts-liberation + + - name: Install and setup sccache + run: | + # export SCCACHE_ERROR_LOG="$PWD"/sccache.log + # export SCCACHE_LOG="info,sccache::cache=debug" + + # curl -o sccache_package.tar.gz -L https://github.com/mozilla/sccache/releases/download/v0.3.3/sccache-v0.3.3-x86_64-unknown-linux-musl.tar.gz + # echo "427bd2151a1b01cd9b094d842e22c445b30f3c645f171a9a62ea55270f06bf23 sccache_package.tar.gz" | shasum -a 256 -c + + curl -o sccache_package.tar.gz -L https://github.com/mozilla/sccache/releases/download/v0.4.0-pre.6/sccache-v0.4.0-pre.6-x86_64-unknown-linux-musl.tar.gz + echo "be5e423c546de2756337ccc1990eb842d85fa5255fb1c4f10a8b5788e18d3e22 sccache_package.tar.gz" | shasum -a 256 -c + + tar xzf sccache_package.tar.gz --strip-components=1 + sudo ln -s "$PWD"/sccache /usr/local/bin/sccache + + # echo '/home/ubuntu/.cargo/bin/sccache' >> $GITHUB_PATH + echo 'SCCACHE_REGION=${{ inputs.aws_region }}' >> $GITHUB_ENV + echo 'SCCACHE_CACHE_SIZE=100G' >> $GITHUB_ENV + + - name: Checkout + uses: actions/checkout@v3 + with: + path: ./src/electron + fetch-depth: 0 + + - name: Get electron_version + run: echo "ELECTRON_VERSION=v$(python3 ./src/electron/script/get-git-version.py)" >> $GITHUB_ENV + + - name: Setup build type Testing + if: inputs.build_type == 'test' + run: | + echo "Setting up build type as testing" + echo 'OUTPUT_FOLDER=out/Testing' >> $GITHUB_ENV + echo 'GN_IMPORT=//electron/build/args/testing.gn' >> $GITHUB_ENV + echo 'SCCACHE_BUCKET=${{ env.SCCACHE_TEST_BUILD_BUCKET_NAME }}' >> $GITHUB_ENV + + - name: Setup build type Release + if: inputs.build_type == 'release' + run: | + echo "Setting up build type as release" + echo 'OUTPUT_FOLDER=out/Release' >> $GITHUB_ENV + echo 'GN_IMPORT=//electron/build/args/release.gn' >> $GITHUB_ENV + echo 'SOURCE_RELEASE_FILE_NAME=${{ env.ELECTRON_VERSION }}' >> $GITHUB_ENV + echo 'RELEASE_SRC_FOLDER=${{ env.ELECTRON_VERSION }}' >> $GITHUB_ENV + echo 'RELEASE_DST_FOLDER=${{ env.ELECTRON_VERSION }}' >> $GITHUB_ENV + echo 'SCCACHE_BUCKET=${{ env.SCCACHE_RELEASE_BUILD_BUCKET_NAME }}' >> $GITHUB_ENV + + - name: Display sccache config + run: | + # echo $SCCACHE_BUCKET $SCCACHE_REGION $SCCACHE_CACHE_SIZE + + sccache -s + + - name: Configure Git cache params + run: | + echo 'GIT_CACHE_FOLDER_NAME=git-cache' >> $GITHUB_ENV + echo 'GIT_CACHE_PATH='"$PWD"'/git-cache' >> $GITHUB_ENV + echo 'GIT_CACHE_FILENAME=git-cache-${{ hashFiles('src/electron/DEPS') }}.tar.gz' >> $GITHUB_ENV + + - name: Get depot tools + run: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git + + - name: Add depot tools to PATH + run: echo ''"$PWD"'/depot_tools' >> $GITHUB_PATH + + - name: Retrieve git-cache + run: | + aws s3api head-object --bucket ${{ env.GIT_CACHE_BUCKET_NAME }} --key $GIT_CACHE_FILENAME || not_exist=true + if [ $not_exist ]; then + echo "Git cache ${GIT_CACHE_FILENAME} not found in bucket ${{ env.GIT_CACHE_BUCKET_NAME }}" + echo "GIT_CACHE_RESTORED=false" >> $GITHUB_ENV + else + echo "Git cache ${GIT_CACHE_FILENAME} found in bucket ${{ env.GIT_CACHE_BUCKET_NAME }}" + echo "GIT_CACHE_RESTORED=true" >> $GITHUB_ENV + aws s3 cp s3://${{ env.GIT_CACHE_BUCKET_NAME }}/$GIT_CACHE_FILENAME $GIT_CACHE_FILENAME --quiet + echo "Downloaded ${GIT_CACHE_FILENAME}" + tar --use-compress-program=pigz -xf $GIT_CACHE_FILENAME + echo "Uncompressed ${GIT_CACHE_FILENAME}" + rm $GIT_CACHE_FILENAME + fi + + - name: GClient sync + run: | + gclient config --name "src/electron" --unmanaged git@github.com:${{ github.repository }}.git + gclient sync --with_branch_heads --with_tags + + # TEMP if you don't do gsync + # cd src/electron + # gclient sync -f + + - name: Save git-cache cache if needed + if: env.GIT_CACHE_RESTORED == 'false' + run: | + tar --use-compress-program=pigz -cf $GIT_CACHE_FILENAME ./$GIT_CACHE_FOLDER_NAME + aws s3 cp $GIT_CACHE_FILENAME s3://${{ env.GIT_CACHE_BUCKET_NAME }}/$GIT_CACHE_FILENAME --storage-class ONEZONE_IA --quiet + + - name: Create source tarball and copy to artifacts bucket + if: inputs.build_type == 'release' + run: | + mkdir -p ${{ env.RELEASE_SRC_FOLDER }} + + echo "Fetching chromium build tools to get export_tarball.py" + git clone https://chromium.googlesource.com/chromium/tools/build --depth 1 + + echo "Running export_tarball.py" + ./build/recipes/recipe_modules/chromium/resources/export_tarball.py $RELEASE_SRC_FOLDER/${{ env.SOURCE_RELEASE_FILE_NAME }} --basename src --src-dir=./src --version=${{ env.ELECTRON_VERSION }} --remove-nonessential-files + + echo "Uploading source tarball" + aws s3 cp ${{ env.RELEASE_SRC_FOLDER }}/${{ env.SOURCE_RELEASE_FILE_NAME }}.tar.xz s3://${{ env.ARTIFACT_BUCKET_NAME }}/${{ env.RELEASE_DST_FOLDER }}/ --acl public-read --quiet + + - name: Set CHROMIUM_BUILDTOOLS_PATH env var + run: echo 'CHROMIUM_BUILDTOOLS_PATH='"$PWD"'/src/buildtools' >> $GITHUB_ENV + + - name: Make sure electron git pack-refs file is present + # This is to stop ninja getting into an infinte loop of "[0/1] Regenerating ninja files" + # Found this file was the issue (as it was missing for some reason) by manually running ninja with -d explain + run: | + cd src/electron + git pack-refs --all + + - name: Run GN Gen + run: | + cd src + gn gen $OUTPUT_FOLDER --args="import(\"${GN_IMPORT}\") cc_wrapper=\"sccache\"" + + - name: Run Ninja Build + run: | + cd src + ninja -C $OUTPUT_FOLDER electron + + - name: Build other items for tests + if: inputs.build_type == 'test' + run: | + cd src + ninja -C $OUTPUT_FOLDER electron:node_headers shell_browser_ui_unittests + + - name: Display sccache stats + run: sccache -s + + - name: Run unit tests + if: inputs.build_type == 'test' + run: | + cd src/electron + xvfb-run npm run test + + - name: Run Node.js Smoke Tests + if: inputs.build_type == 'test' + run: | + cd src/electron + xvfb-run node script/node-spec-runner.js --default + + - name: Prepare release dist and upload + if: inputs.build_type == 'release' + run: | + cd src + ninja -C $OUTPUT_FOLDER electron:electron_dist_zip electron:node_headers third_party/electron_node:overlapped-checker electron:hunspell_dictionaries_zip + if [ "`uname`" == "Darwin" ]; then + target_os=mac + target_cpu=x64 + if [ x"$MAS_BUILD" == x"true" ]; then + target_os=mac_mas + fi + if [ "$TARGET_ARCH" == "arm64" ]; then + target_cpu=arm64 + fi + elif [ "`uname`" == "Linux" ]; then + target_os=linux + if [ x"$TARGET_ARCH" == x ]; then + target_cpu=x64 + else + target_cpu="$TARGET_ARCH" + fi + else + echo "Unknown system: `uname`" + exit 1 + fi + echo "Checking dist_zip.${target_os}.${target_cpu}.manifest" + echo "TARGET_OS_CPU=${target_os}-${target_cpu}" >> $GITHUB_ENV + electron/script/zip_manifests/check-zip-manifest.py $OUTPUT_FOLDER/dist.zip electron/script/zip_manifests/dist_zip.$target_os.$target_cpu.manifest + + - name: Prepare release package and upload to artifacts bucket + if: inputs.build_type == 'release' + run: | + cd ${{ env.RELEASE_SRC_FOLDER }} + mkdir package + cd package + unzip -q ../../src/out/Release/dist.zip -d ./dist + cp ../../src/electron/npm/* . + cp ../../src/electron/electron.d.ts . + cp ../../src/LICENSE . + + # Normally when electron is installed it will run install.js which will download the binary, create path.txt + # and copy fields 'name', 'repository', 'description', 'license', 'author', 'keywords' from the root package.json. + + # We will manually do these steps and clear install.js + + echo -n "electron" > path.txt + echo "" > install.js + + echo "$(jq '. += {"name": "electron"}' package.json)" > package.json + echo "$(jq '. += {"repository": "https://github.com/brightsign/electron"}' package.json)" > package.json + echo "$(jq '. += {"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS"}' package.json)" > package.json + echo "$(jq '. += {"license": "MIT"}' package.json)" > package.json + echo "$(jq '. += {"author": "Electron Community"}' package.json)" > package.json + echo "$(jq '. += {"keywords": [ "electron" ] }' package.json)" > package.json + echo "$(jq '. += {"version": "${{ env.ELECTRON_VERSION }}"}' package.json)" > package.json + + electron_package_name="electron-package-${{ env.TARGET_OS_CPU }}-${{ env.SOURCE_RELEASE_FILE_NAME }}.tar.gz" + echo "Creating $electron_package_name" + tar --use-compress-program=pigz -cf "../$electron_package_name" . + cd .. + + echo "Uploading $electron_package_name to s3://${{ env.ARTIFACT_BUCKET_NAME }}/${{ env.RELEASE_DST_FOLDER }}/" + aws s3 cp $electron_package_name s3://${{ env.ARTIFACT_BUCKET_NAME }}/${{ env.RELEASE_DST_FOLDER }}/ --acl public-read --quiet diff --git a/.github/workflows/bs_electron_ci_ec2.yml b/.github/workflows/bs_electron_ci_ec2.yml new file mode 100644 index 0000000000000..4cd5baa57c628 --- /dev/null +++ b/.github/workflows/bs_electron_ci_ec2.yml @@ -0,0 +1,108 @@ +name: 'BrightSign Build and Test Electron: EC2 controller' +on: + workflow_call: + inputs: + build_type: + description: 'Build Type' + required: true + type: string + + instance_type: + description: 'EC2 instance type' + required: false + type: string + default: c6a.4xlarge + + leave_ec2_instance_running: + description: 'Leave EC2 instance running after use' + type: boolean + default: false + + instance_name_postfix: + description: 'Name to add as postfix to the EC2 machine' + type: string + default: auto-triggered + + aws_arn_role: + required: true + type: string + + aws_region: + required: true + type: string + +jobs: + start-runner: + name: Start self-hosted EC2 runner + runs-on: ubuntu-latest + outputs: + label: ${{ steps.start-ec2-runner.outputs.label }} + ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-region: ${{ inputs.aws_region }} + role-to-assume: ${{ inputs.aws_arn_role }} + + - name: Download config file and set env vars from it + run: | + aws s3 cp s3://electron-ci-config-bucket/config.json . + aws s3 cp s3://electron-ci-config-bucket/set_github_env_vars.py . + python set_github_env_vars.py --file config.json + + - name: Start EC2 runner + id: start-ec2-runner + uses: brightsign/ec2-github-runner@0fa8b183dd4124fd191ccdbc48b68f0ea46a9634 + with: + mode: start + github-app-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + github-app-id: 287690 + ec2-image-id: ami-07b4a16cd5294af98 + ec2-instance-type: ${{ inputs.instance_type }} + subnet-id: ${{ env.VPC_SUBNET_ID }} + security-group-id: ${{ env.VPC_SG_ID }} + run-as-service-with-user: ubuntu + runner-home-dir: /home/ubuntu + # iam-role-name: my-role-name # optional, requires additional permissions + aws-resource-tags: > # optional, requires additional permissions + [ + {"Key": "Name", "Value": "github-runner-${{ inputs.instance_name_postfix }}"}, + {"Key": "GitHubRepository", "Value": "${{ github.repository }}"} + ] + + build-and-test-electron: + name: Build and Test Electron + needs: start-runner # required to start the main job when the runner is ready + uses: ./.github/workflows/bs_electron_build_and_test.yml + secrets: inherit + with: + runner_name: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runner + github_hosted_runner: false + build_type: ${{ inputs.build_type }} + aws_arn_role: ${{ inputs.aws_arn_role }} + aws_region: ${{ inputs.aws_region }} + + stop-runner: + name: Stop self-hosted EC2 runner + needs: + - start-runner # required to get output from the start-runner job + - build-and-test-electron # required to wait when the main job is done + runs-on: ubuntu-latest + if: ${{ always() }} # required to stop the runner even if the error happened in the previous jobs + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + role-to-assume: ${{ inputs.aws_arn_role }} + aws-region: ${{ inputs.aws_region }} + + - name: Stop EC2 runner + uses: brightsign/ec2-github-runner@0fa8b183dd4124fd191ccdbc48b68f0ea46a9634 + with: + mode: stop + github-app-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + github-app-id: 287690 + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} + leave-ec2-instance-running: ${{ inputs.leave_ec2_instance_running }} \ No newline at end of file diff --git a/.github/workflows/bs_electron_ci_manual_process.yml b/.github/workflows/bs_electron_ci_manual_process.yml new file mode 100644 index 0000000000000..7b5f7ff047d96 --- /dev/null +++ b/.github/workflows/bs_electron_ci_manual_process.yml @@ -0,0 +1,41 @@ +name: 'BrightSign Manual Build Process' +on: + workflow_dispatch: + inputs: + build_type: + description: 'Build Type' + type: choice + options: + - test + - release + + instance_type: + description: 'EC2 Instance Type' + type: choice + options: + - c6a.large + - c6a.xlarge + - c6a.2xlarge + - c6a.4xlarge + - c6a.8xlarge + + leave_ec2_instance_running: + description: 'Leave EC2 instance running after use' + type: boolean + default: false + +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + call-build-and-test: + uses: ./.github/workflows/bs_electron_ci_ec2.yml + secrets: inherit + with: + build_type: ${{ inputs.build_type }} + instance_type: ${{ inputs.instance_type }} + leave_ec2_instance_running: ${{ inputs.leave_ec2_instance_running }} + instance_name_postfix: manual--${{ github.actor }} + aws_arn_role: arn:aws:iam::195607249165:role/github-actions-electron-repo + aws_region: us-east-1 diff --git a/.github/workflows/bs_electron_ci_release_process.yml b/.github/workflows/bs_electron_ci_release_process.yml new file mode 100644 index 0000000000000..5ad725b0a3093 --- /dev/null +++ b/.github/workflows/bs_electron_ci_release_process.yml @@ -0,0 +1,35 @@ +name: 'BrightSign Release Processes' +on: + create: + delete: + tags: + +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + create-release: + if: github.event_name == 'create' && github.event.ref_type == 'tag' + uses: ./.github/workflows/bs_electron_ci_ec2.yml + secrets: inherit + with: + build_type: release + aws_arn_role: arn:aws:iam::195607249165:role/github-actions-electron-repo + aws_region: us-east-1 + + delete-release: + if: github.event_name == 'delete' && github.event.ref_type == 'tag' # startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Info + run: echo "TODO.. Delete release with Tag" ${{ github.event.ref }} + + # debug: + # runs-on: ubuntu-latest + # steps: + # - name: Dump GitHub context + # env: + # GITHUB_CONTEXT: ${{ toJson(github) }} + # run: | + # echo "$GITHUB_CONTEXT" diff --git a/.github/workflows/bs_electron_ci_test_process.yml b/.github/workflows/bs_electron_ci_test_process.yml new file mode 100644 index 0000000000000..d324bcf9a0eea --- /dev/null +++ b/.github/workflows/bs_electron_ci_test_process.yml @@ -0,0 +1,27 @@ +name: 'BrightSign Test Process' +on: + pull_request: + # branches: [ "main" ] + +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + test-pr: + if: github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'synchronize') + uses: ./.github/workflows/bs_electron_ci_ec2.yml + secrets: inherit + with: + build_type: test + aws_arn_role: arn:aws:iam::195607249165:role/github-actions-electron-repo + aws_region: us-east-1 + + # debug: + # runs-on: ubuntu-latest + # steps: + # - name: Dump GitHub context + # env: + # GITHUB_CONTEXT: ${{ toJson(github) }} + # run: | + # echo "$GITHUB_CONTEXT" From 67a9eecbbfff84f8839feefa607af95b2aee5a7f Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:29:50 +0100 Subject: [PATCH 002/116] feat: OS-14993 Make redirects from one file to another transparent (#12) * transparent redirect: Make redirects from one file to another transparent * webRequest: Add new test case to api-web-request-spec for transparent and normal redirects fix: Clear content-type and last modified from file fetches: OS-16593 (#39) Clear content-type and last modified from file fetches: OS-16593 Cherry picked from: b84c8359bafe7f77b2647dcd261819c4220d87a7: feat: OS-14993 Make redirects from one file to another transparent (#12) 558b42debb19932bbc2e2231c3d6cb92c8502a6c: fix: Clear content-type and last modified from file fetches: OS-16593 (#39) --- .../net/proxying_url_loader_factory.cc | 31 +++++++++++++ spec/api-web-request-spec.ts | 43 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index c317ad7059d2e..e5ff2af05bf7d 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -243,6 +243,26 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse( network::mojom::URLResponseHeadPtr head, mojo::ScopedDataPipeConsumerHandle body, absl::optional cached_metadata) { + // Chromium sets the kContentType and kLastModified fields in the header + // always. + if (request_.url.SchemeIs(url::kFileScheme) && + !request_.url.ExtractFileName().empty() && head->headers) { + if (request_.url.ExtractFileName().find(".") == std::string::npos && + head->headers->HasHeaderValue(net::HttpRequestHeaders::kContentType, + "text/plain")) { + // Having content-type set causes an issue for css style sheets with no + // file extension (asset pool files). In this case the content type is set + // to text/plain as the mime sniffer doesn't support detecting a css file, + // which causes the style sheet to be ignored. To fix this remove the + // content type. + head->headers->RemoveHeader(net::HttpRequestHeaders::kContentType); + } + if (head->headers->HasHeader(net::HttpResponseHeaders::kLastModified)) { + // We don't want to cache files so remove the last modified header. + head->headers->RemoveHeader(net::HttpResponseHeaders::kLastModified); + } + } + current_body_ = std::move(body); current_cached_metadata_ = std::move(cached_metadata); if (current_request_uses_header_client_) { @@ -437,6 +457,17 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders( return; } + // At this point we know that onBeforeRequest (if overridden) will have + // updated redirect_url_. Note: The callback used by OnBeforeRequest (in + // electron_api_web_request.cc) is passed in RestartInternal. If we have a + // redirect url and both the original and redirect urls are file schema, then + // perform a 'transparent' redirect. + if (request_.url.SchemeIs(url::kFileScheme) && !redirect_url_.is_empty() && + redirect_url_.SchemeIs(url::kFileScheme)) { + request_.url = redirect_url_; + // Clear the redirect_url_ to prevent a normal redirect being triggered + redirect_url_ = GURL(); + } if (!current_request_uses_header_client_ && !redirect_url_.is_empty()) { if (for_cors_preflight_) { // CORS preflight doesn't support redirect. diff --git a/spec/api-web-request-spec.ts b/spec/api-web-request-spec.ts index 3afaba1cd9180..4eb379cdaa458 100644 --- a/spec/api-web-request-spec.ts +++ b/spec/api-web-request-spec.ts @@ -168,6 +168,49 @@ describe('webRequest module', () => { expect(data).to.equal('/redirect'); }); + it('check normal and transparent redirections', async () => { + let onBeforeRedirectCount = 0; + let onBeforeRequestCount = 0; + const redirectFileURL = url.format({ + pathname: path.join(fixturesPath, 'blank.html').replace(/\\/g, '/'), + protocol: 'file', + slashes: true + }); + + ses.webRequest.onBeforeRedirect(() => { + onBeforeRedirectCount++; + }); + ses.webRequest.onBeforeRequest((details, callback) => { + onBeforeRequestCount++; + if (details.url === 'file:///fileTofile') { + callback({ redirectURL: redirectFileURL }); + } else if (details.url === 'file:///fileTohttp' || details.url === defaultURL) { + callback({ redirectURL: `${defaultURL}redirect` }); + } else { + callback({}); + } + }); + + let fileUrl = 'file:///fileTofile'; + let result = await ajax(fileUrl); + expect(result.status).to.equal(200); + expect(onBeforeRequestCount).to.equal(1); + expect(onBeforeRedirectCount).to.equal(0); + onBeforeRequestCount = onBeforeRedirectCount = 0; + + fileUrl = 'file:///fileTohttp'; + result = await ajax(fileUrl); + expect(result.data).to.equal('/redirect'); + expect(onBeforeRequestCount).to.equal(2); + expect(onBeforeRedirectCount).to.equal(1); + onBeforeRequestCount = onBeforeRedirectCount = 0; + + result = await ajax(defaultURL); + expect(result.data).to.equal('/redirect'); + expect(onBeforeRequestCount).to.equal(2); + expect(onBeforeRedirectCount).to.equal(1); + }); + it('does not crash for redirects', async () => { ses.webRequest.onBeforeRequest((details, callback) => { callback({ cancel: false }); From 96b26cfe2a09599f5ad93e89256189e4acffaa8f Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Mon, 20 Mar 2023 09:40:40 +0000 Subject: [PATCH 003/116] feat: OS-14967 Patch to force mime sniffing for file urls in Chromium (#11) file urls: Path to force mime sniffing for file urls in Chromium Cherry picked from: 572a4c5ac51374e5df267fa8c0c3a6307370bf4a: feat: OS-14967 Patch to force mime sniffing for file urls in Chromium (#11) --- patches/chromium/.patches | 1 + ...at_os_14967_force_sniffing_file_urls.patch | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 patches/chromium/feat_os_14967_force_sniffing_file_urls.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index b31db3b0e071b..81c8c9ebd1724 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -144,3 +144,4 @@ feat_allow_code_cache_in_custom_schemes.patch enable_partition_alloc_ref_count_size.patch reland_mojom_ts_generator_handle_empty_module_path_identically_to.patch ensure_an_axcontext_before_painting.patch +feat_os_14967_force_sniffing_file_urls.patch \ No newline at end of file diff --git a/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch b/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch new file mode 100644 index 0000000000000..4f25106f0776b --- /dev/null +++ b/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir +Date: Wed, 15 Mar 2023 11:37:56 +0000 +Subject: feat:os_14967_force_sniffing_file_urls + +This patch forces Mime Sniffing for file urls. + +diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc +index abf793bac1c39..495328dd78737 100644 +--- a/content/public/browser/content_browser_client.cc ++++ b/content/public/browser/content_browser_client.cc +@@ -359,7 +359,7 @@ bool ContentBrowserClient::IsFileAccessAllowed( + } + + bool ContentBrowserClient::ForceSniffingFileUrlsForHtml() { +- return false; ++ return true; + } + + std::string ContentBrowserClient::GetApplicationClientGUIDForQuarantineCheck() { From 571273c13b662ef04ea1c624bc4c88bfb71feda3 Mon Sep 17 00:00:00 2001 From: George Joseph Date: Wed, 5 Apr 2023 18:22:32 +0100 Subject: [PATCH 004/116] feat : backport, Allow partitions to have app set quotas OS-14197 (#14) Description of Change Allow the passing of a quota to the session.fromPartition() API. This patch allows partitions to have a quota size, which in turn persuades the storage manager to run a garbage collector(at specific times) to free reclaimable storage space. Checklist - [x] PR description included and stakeholders cc'd - [x] npm test passes - [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md) - [x] relevant documentation is changed or added - [x] [PR release notes](https://github.com/electron/clerk/blob/master/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/master/README.md#examples). Release Notes notes: Allow the passing of a quota to the session.fromPath() API. Cherry picked from: 9555dc949b812a47b12cb9d2b8f57a9da0386f32: feat : backport, Allow partitions to have app set quotas OS-14197 (#14) --- docs/api/session.md | 9 +- patches/chromium/.patches | 3 +- ...rage_quota_capability_for_partitions.patch | 232 ++++++++++++++++++ ...at_os_14967_force_sniffing_file_urls.patch | 2 +- shell/browser/electron_browser_context.cc | 12 + shell/browser/electron_browser_context.h | 2 + spec/api-session-spec.ts | 69 ++++++ 7 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 patches/chromium/feat_backported_add_storage_quota_capability_for_partitions.patch diff --git a/docs/api/session.md b/docs/api/session.md index 1a48b44d5a93d..8a206d6c517a0 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -46,7 +46,8 @@ of an existing `Session` object. * `path` string * `options` Object (optional) - * `cache` boolean - Whether to enable cache. + * `cache` boolean (optional) - Whether to enable cache. + * `quota` number (optional) - Use a quota size for a partition (in bytes). Returns `Session` - A session instance from the absolute path as specified by the `path` string. When there is an existing `Session` with the same absolute path, it @@ -56,7 +57,11 @@ be thrown if an empty string is provided. To create a `Session` with `options`, you have to ensure the `Session` with the `path` has never been used before. There is no way to change the `options` -of an existing `Session` object. +of an existing `Session` object. The optional `quota` parameter can be used to +specify the a quota size that can be used by the session, which will be returned as the quota provided by navigator.storage.estimate(). If the `quota` is not +provided, the size of the volume on which path is located will be used. A smaller +quota size will allow the LRU algorithm to evict more data from the cache, though there +is no guarantee that the cache will not exceed the quota size. ## Properties diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 81c8c9ebd1724..6b864c11159df 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -144,4 +144,5 @@ feat_allow_code_cache_in_custom_schemes.patch enable_partition_alloc_ref_count_size.patch reland_mojom_ts_generator_handle_empty_module_path_identically_to.patch ensure_an_axcontext_before_painting.patch -feat_os_14967_force_sniffing_file_urls.patch \ No newline at end of file +feat_os_14967_force_sniffing_file_urls.patch +feat_backported_add_storage_quota_capability_for_partitions.patch diff --git a/patches/chromium/feat_backported_add_storage_quota_capability_for_partitions.patch b/patches/chromium/feat_backported_add_storage_quota_capability_for_partitions.patch new file mode 100644 index 0000000000000..7aabf9a4a9de4 --- /dev/null +++ b/patches/chromium/feat_backported_add_storage_quota_capability_for_partitions.patch @@ -0,0 +1,232 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> +Date: Fri, 19 Apr 2024 17:36:50 +0100 +Subject: feat:(backported) add storage quota capability for partitions + +This patch adds the capability for a storage quota +to be set on partitions.This capability will be +exercised only when physical storage based +partitions are used. + +diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc +index b363364dc0648ee69ee4a8f84417d3003e3fd256..c45e6f50adcc4f1429998a558232732456433e30 100644 +--- a/content/browser/storage_partition_impl.cc ++++ b/content/browser/storage_partition_impl.cc +@@ -1378,6 +1378,8 @@ void StoragePartitionImpl::Initialize( + base::BindRepeating(&StoragePartitionImpl::GetQuotaSettings, + weak_factory_.GetWeakPtr())); + quota_manager_ = quota_context_->quota_manager(); ++ if (config_.quota()) ++ quota_manager_->SetQuota(config_.quota().value()); + scoped_refptr quota_manager_proxy = + quota_manager_->proxy(); + +diff --git a/content/public/browser/storage_partition_config.cc b/content/public/browser/storage_partition_config.cc +index 81013d6eb993a9a1bfbdf0bea388e249c9045c05..b1f43a545d234556400968da08ce52b6701af1df 100644 +--- a/content/public/browser/storage_partition_config.cc ++++ b/content/public/browser/storage_partition_config.cc +@@ -21,8 +21,10 @@ StoragePartitionConfig& StoragePartitionConfig::operator=( + + // static + StoragePartitionConfig StoragePartitionConfig::CreateDefault( +- BrowserContext* browser_context) { +- return StoragePartitionConfig("", "", browser_context->IsOffTheRecord()); ++ BrowserContext* browser_context, ++ absl::optional quota_size) { ++ return StoragePartitionConfig("", "", browser_context->IsOffTheRecord(), ++ quota_size); + } + + // static +@@ -30,22 +32,26 @@ StoragePartitionConfig StoragePartitionConfig::Create( + BrowserContext* browser_context, + const std::string& partition_domain, + const std::string& partition_name, +- bool in_memory) { ++ bool in_memory, ++ absl::optional quota_size) { + // If a caller tries to pass an empty partition_domain something is seriously + // wrong or the calling code is not explicitly signalling its desire to create + // a default partition by calling CreateDefault(). + CHECK(!partition_domain.empty()); + return StoragePartitionConfig(partition_domain, partition_name, +- in_memory || browser_context->IsOffTheRecord()); ++ in_memory || browser_context->IsOffTheRecord(), ++ quota_size); + } + + StoragePartitionConfig::StoragePartitionConfig( + const std::string& partition_domain, + const std::string& partition_name, +- bool in_memory) ++ bool in_memory, ++ absl::optional quota_size) + : partition_domain_(partition_domain), + partition_name_(partition_name), +- in_memory_(in_memory) {} ++ in_memory_(in_memory), ++ quota_size_(quota_size) {} + + absl::optional + StoragePartitionConfig::GetFallbackForBlobUrls() const { +@@ -55,7 +61,8 @@ StoragePartitionConfig::GetFallbackForBlobUrls() const { + return StoragePartitionConfig( + partition_domain_, "", + /*in_memory=*/fallback_to_partition_domain_for_blob_urls_ == +- FallbackMode::kFallbackPartitionInMemory); ++ FallbackMode::kFallbackPartitionInMemory, ++ quota_size_); + } + + bool StoragePartitionConfig::operator<( +diff --git a/content/public/browser/storage_partition_config.h b/content/public/browser/storage_partition_config.h +index e765f81673b08f51f6b7c3370c535fae03d41362..786d53df985637560737230d4c1182daf196d3bc 100644 +--- a/content/public/browser/storage_partition_config.h ++++ b/content/public/browser/storage_partition_config.h +@@ -28,7 +28,8 @@ class CONTENT_EXPORT StoragePartitionConfig { + + // Creates a default config for |browser_context|. If |browser_context| is an + // off-the-record profile, then the config will have |in_memory_| set to true. +- static StoragePartitionConfig CreateDefault(BrowserContext* browser_context); ++ static StoragePartitionConfig CreateDefault(BrowserContext* browser_context, ++ absl::optional quota_size = absl::nullopt); + + // Creates a config tied to a specific domain. + // The |partition_domain| is [a-z]* UTF-8 string, specifying the domain in +@@ -43,11 +44,13 @@ class CONTENT_EXPORT StoragePartitionConfig { + static StoragePartitionConfig Create(BrowserContext* browser_context, + const std::string& partition_domain, + const std::string& partition_name, +- bool in_memory); ++ bool in_memory, ++ absl::optional quota_size = absl::nullopt); + + std::string partition_domain() const { return partition_domain_; } + std::string partition_name() const { return partition_name_; } + bool in_memory() const { return in_memory_; } ++ absl::optional quota() const { return quota_size_; } + + // Returns true if this config was created by CreateDefault() or is + // a copy of a config created with that method. +@@ -94,11 +97,13 @@ class CONTENT_EXPORT StoragePartitionConfig { + + StoragePartitionConfig(const std::string& partition_domain, + const std::string& partition_name, +- bool in_memory); ++ bool in_memory, ++ absl::optional quota_size = absl::nullopt); + + std::string partition_domain_; + std::string partition_name_; + bool in_memory_ = false; ++ absl::optional quota_size_ = absl::nullopt; + FallbackMode fallback_to_partition_domain_for_blob_urls_ = + FallbackMode::kNone; + }; +diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc +index f1f1e97985b63b6d651046c667502784e138a334..f0daf26d55f7752e7b99024a0ad92b9fbc3ac3d8 100644 +--- a/storage/browser/quota/quota_manager_impl.cc ++++ b/storage/browser/quota/quota_manager_impl.cc +@@ -2808,7 +2808,7 @@ void QuotaManagerImpl::GetStorageCapacity(StorageCapacityCallback callback) { + db_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&QuotaManagerImpl::CallGetVolumeInfo, get_volume_info_fn_, +- profile_path_), ++ profile_path_,start_quota_), + base::BindOnce(&QuotaManagerImpl::DidGetStorageCapacity, + weak_factory_.GetWeakPtr())); + } +@@ -3117,16 +3117,25 @@ void QuotaManagerImpl::PostTaskAndReplyWithResultForDBThread( + std::move(reply)); + } + ++void QuotaManagerImpl::SetQuota(int start_quota) { ++ // Set the quota for a browser context */ ++ // If an electron::BrowserWindow uses a partition path ++ // with no existing browser context, then ++ // this quota takes effect ++ start_quota_ = start_quota; ++} ++ + // static + QuotaAvailability QuotaManagerImpl::CallGetVolumeInfo( + GetVolumeInfoFn get_volume_info_fn, +- const base::FilePath& path) { ++ const base::FilePath& path, ++ const absl::optional& quota_size) { + if (!base::CreateDirectory(path)) { + LOG(WARNING) << "Create directory failed for path" << path.value(); + return QuotaAvailability(0, 0); + } + +- const QuotaAvailability quotaAvailability = get_volume_info_fn(path); ++ const QuotaAvailability quotaAvailability = get_volume_info_fn(path,quota_size); + const auto total = quotaAvailability.total; + const auto available = quotaAvailability.available; + +@@ -3148,9 +3157,16 @@ QuotaAvailability QuotaManagerImpl::CallGetVolumeInfo( + } + + // static +-QuotaAvailability QuotaManagerImpl::GetVolumeInfo(const base::FilePath& path) { +- return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path), +- base::SysInfo::AmountOfFreeDiskSpace(path)); ++QuotaAvailability QuotaManagerImpl::GetVolumeInfo( ++ const base::FilePath& path, ++ const absl::optional& quota_size) { ++ if (!quota_size) { ++ return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path), ++ base::SysInfo::AmountOfFreeDiskSpace(path)); ++ } else { ++ return QuotaAvailability(base::SysInfo::AmountOfTotalDiskSpace(path), ++ quota_size.value()); ++ } + } + + void QuotaManagerImpl::AddObserver( +diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h +index 82476fcb3cb14c79947d9d6261db495b3730fef5..f878d345632df3fd25755b77e1f52b36f8b5be48 100644 +--- a/storage/browser/quota/quota_manager_impl.h ++++ b/storage/browser/quota/quota_manager_impl.h +@@ -160,7 +160,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl + // Function pointer type used to store the function which returns + // information about the volume containing the given FilePath. + // The value returned is the QuotaAvailability struct. +- using GetVolumeInfoFn = QuotaAvailability (*)(const base::FilePath&); ++ using GetVolumeInfoFn = QuotaAvailability (*)(const base::FilePath&, ++ const absl::optional&); + + static constexpr int64_t kGBytes = 1024 * 1024 * 1024; + static constexpr int64_t kNoLimit = INT64_MAX; +@@ -483,6 +484,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl + eviction_disabled_ = disable; + } + ++ void SetQuota(const int start_quota); ++ + // Testing support for handling corruption in the underlying database. + // + // Runs `corrupter` on the same sequence used to do database I/O, +@@ -765,8 +768,10 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl + bool is_bootstrap_task = false); + + static QuotaAvailability CallGetVolumeInfo(GetVolumeInfoFn get_volume_info_fn, +- const base::FilePath& path); +- static QuotaAvailability GetVolumeInfo(const base::FilePath& path); ++ const base::FilePath& path, ++ const absl::optional& quota_size = absl::nullopt); ++ static QuotaAvailability GetVolumeInfo(const base::FilePath& path, ++ const absl::optional& quota_size = absl::nullopt); + + const bool is_incognito_; + const base::FilePath profile_path_; +@@ -865,6 +870,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl + // QuotaManagerImpl::GetVolumeInfo. + GetVolumeInfoFn get_volume_info_fn_; + ++ absl::optional start_quota_ = absl::nullopt; ++ + std::unique_ptr eviction_helper_; + std::map> + bucket_set_data_deleters_; + \ No newline at end of file diff --git a/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch b/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch index 4f25106f0776b..f8b417adf9e39 100644 --- a/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch +++ b/patches/chromium/feat_os_14967_force_sniffing_file_urls.patch @@ -6,7 +6,7 @@ Subject: feat:os_14967_force_sniffing_file_urls This patch forces Mime Sniffing for file urls. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index abf793bac1c39..495328dd78737 100644 +index abf793bac1c3937dee803a6ec100e42c4bf78e31..495328dd787370992848a26b529191e2220bfa9a 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc @@ -359,7 +359,7 @@ bool ContentBrowserClient::IsFileAccessAllowed( diff --git a/shell/browser/electron_browser_context.cc b/shell/browser/electron_browser_context.cc index 77ed1e4e65140..1b830754dd0d4 100644 --- a/shell/browser/electron_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -239,6 +239,9 @@ ElectronBrowserContext::ElectronBrowserContext( &partition_location)) { const base::FilePath& partition_path = filepath_partition->get(); path_ = std::move(partition_path); + if (auto quota_size_opt = options.FindInt("quota")) { + user_set_quota_ = quota_size_opt.value(); + } } BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this); @@ -273,6 +276,15 @@ ElectronBrowserContext::~ElectronBrowserContext() { std::move(resource_context_)); } +content::StoragePartition* +ElectronBrowserContext::GetDefaultStoragePartition() { + if (user_set_quota_) { + return GetStoragePartition( + content::StoragePartitionConfig::CreateDefault(this, user_set_quota_)); + } + return BrowserContext::GetDefaultStoragePartition(); +} + void ElectronBrowserContext::InitPrefs() { auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences")); ScopedAllowBlockingForElectron allow_blocking; diff --git a/shell/browser/electron_browser_context.h b/shell/browser/electron_browser_context.h index 80581f2d3f540..5d1832d6a4ece 100644 --- a/shell/browser/electron_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -157,6 +157,7 @@ class ElectronBrowserContext : public content::BrowserContext { override; content::DownloadManagerDelegate* GetDownloadManagerDelegate() override; content::BrowserPluginGuestManager* GetGuestManager() override; + content::StoragePartition* GetDefaultStoragePartition(); content::PlatformNotificationService* GetPlatformNotificationService() override; content::PermissionControllerDelegate* GetPermissionControllerDelegate() @@ -257,6 +258,7 @@ class ElectronBrowserContext : public content::BrowserContext { base::FilePath path_; bool in_memory_ = false; bool use_cache_ = true; + absl::optional user_set_quota_ = absl::nullopt; int max_cache_size_ = 0; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts index 7a456fef39d69..8ee53dad3ee8a 100644 --- a/spec/api-session-spec.ts +++ b/spec/api-session-spec.ts @@ -1,6 +1,7 @@ import { expect } from 'chai'; import * as http from 'node:http'; import * as https from 'node:https'; +import * as os from 'node:os'; import * as path from 'node:path'; import * as fs from 'node:fs'; import * as ChildProcess from 'node:child_process'; @@ -36,6 +37,74 @@ describe('session module', () => { }); }); + describe('session.fromPath(path) with a quota.', () => { + const pathloc = 'testsession'; + const tmppath = require('electron').app.getPath('temp') + path.sep + pathloc; + + after(() => { + if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); } + }); + it('Assert that a set quota value is returned when a quota value is specified for a session', async () => { + const quotasize = 256000; + if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); } + fs.mkdirSync(tmppath); + const localsession = session.fromPath(tmppath, { quota: quotasize }); + const w = new BrowserWindow({ + show: false, + webPreferences: { + session: localsession + } + }); + + const readQuotaSize: any = () => { + return w.webContents.executeJavaScript(` + navigator.storage.estimate().then(estimate => estimate.quota).catch(err => err.message); + `); + }; + + await w.loadFile(path.join(fixtures, 'api', 'localstorage.html')); + const size = await readQuotaSize(); + expect(size).to.equal(quotasize); + }); + }); + + /* Note : This test cannot be up-streamed */ + describe('session.fromPath(path) with no quota.', () => { + const pathloc = 'sessionnoquota'; + const tmppath = require('electron').app.getPath('temp') + path.sep + pathloc; + + after(() => { + if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); } + }); + it('Assert that free space value is returned when a quota value is not specified for a session', async () => { + if (fs.existsSync(tmppath)) { fs.rmSync(tmppath, { recursive: true, force: true }); } + fs.mkdirSync(tmppath); + const localsession = session.fromPath(tmppath); + const w = new BrowserWindow({ + show: false, + webPreferences: { + session: localsession + } + }); + + const readQuotaSize: any = () => { + return w.webContents.executeJavaScript(` + navigator.storage.estimate().then(estimate => estimate.quota).catch(err => err.message); + `); + }; + + await w.loadFile(path.join(fixtures, 'api', 'localstorage.html')); + const size = await readQuotaSize(); + expect(size).to.be.greaterThan(0); + + if (os.platform() === 'linux') { + const output = ChildProcess.execSync(`df ${tmppath} --output=avail | tail -n1`); + const availablespace = parseInt(output.toString(), 10) * 1024; + expect(availablespace).to.be.greaterThan(0); + } + }); + }); + describe('ses.cookies', () => { const name = '0'; const value = '0'; From 0cf7a25d92c25076bb460fd23a7109d944917e93 Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Wed, 10 May 2023 10:42:59 +0100 Subject: [PATCH 005/116] feat: Add BrightSign custom set-z-index feature to Wayland (#17) * wayland: Add BrightSign custom set-z-index feature A custom feature has been added to BrightSign Weston server to enable a z-index to be set to top level xdg windows. This patch adds the functionality into third_party/wayland and chromium ui/ozone/platform/wayland. * wayland: Add fixup patch to add missing include * fixup: Fix for storage quota test case --- patches/chromium/.patches | 1 + ...et_z_order_custom_feature_to_wayland.patch | 198 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 patches/chromium/OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 6b864c11159df..23a2567d7e19a 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -146,3 +146,4 @@ reland_mojom_ts_generator_handle_empty_module_path_identically_to.patch ensure_an_axcontext_before_painting.patch feat_os_14967_force_sniffing_file_urls.patch feat_backported_add_storage_quota_capability_for_partitions.patch +OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch \ No newline at end of file diff --git a/patches/chromium/OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch b/patches/chromium/OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch new file mode 100644 index 0000000000000..6b0ca2a9c6449 --- /dev/null +++ b/patches/chromium/OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch @@ -0,0 +1,198 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> +Date: Fri, 19 Apr 2024 19:01:06 +0100 +Subject: OS-15118: add bs set z order custom feature to wayland + +A custom feature has been added to BrightSign Weston server to enable +a z-index to be set to top level xdg windows. +This patch adds the functionality into third_party/wayland-protocols and +chromium ui/ozone/platform/wayland. + +diff --git a/third_party/wayland-protocols/BUILD.gn b/third_party/wayland-protocols/BUILD.gn +index b0c0082769055..c59c5a182c8de 100644 +--- a/third_party/wayland-protocols/BUILD.gn ++++ b/third_party/wayland-protocols/BUILD.gn +@@ -11,6 +11,10 @@ wayland_protocol("alpha_compositing_protocol") { + sources = [ "unstable/alpha-compositing/alpha-compositing-unstable-v1.xml" ] + } + ++wayland_protocol("bs_z_order_protocol") { ++ sources = [ "unstable/bs-z-order/bs-z-order-unstable-v1.xml" ] ++} ++ + wayland_protocol("content_type_protocol") { + sources = [ "unstable/content-type/content-type-v1.xml" ] + } +diff --git a/third_party/wayland-protocols/unstable/bs-z-order/bs-z-order-unstable-v1.xml b/third_party/wayland-protocols/unstable/bs-z-order/bs-z-order-unstable-v1.xml +new file mode 100644 +index 0000000000000..da7ae7a6e2024 +--- /dev/null ++++ b/third_party/wayland-protocols/unstable/bs-z-order/bs-z-order-unstable-v1.xml +@@ -0,0 +1,14 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn +index 1ec252c342cb6..ee2574fa24219 100644 +--- a/ui/ozone/platform/wayland/BUILD.gn ++++ b/ui/ozone/platform/wayland/BUILD.gn +@@ -255,6 +255,7 @@ source_set("wayland") { + "//third_party/wayland:wayland_egl", + "//third_party/wayland:wayland_util", + "//third_party/wayland-protocols:alpha_compositing_protocol", ++ "//third_party/wayland-protocols:bs_z_order_protocol", + "//third_party/wayland-protocols:content_type_protocol", + "//third_party/wayland-protocols:cursor_shapes_protocol", + "//third_party/wayland-protocols:extended_drag", +diff --git a/ui/ozone/platform/wayland/common/wayland_object.cc b/ui/ozone/platform/wayland/common/wayland_object.cc +index bcc48aaeb186e..cf291424ff56d 100644 +--- a/ui/ozone/platform/wayland/common/wayland_object.cc ++++ b/ui/ozone/platform/wayland/common/wayland_object.cc +@@ -190,6 +190,7 @@ void (*ObjectTraits::deleter)(void*) = &wl_proxy_wrapper_destroy; + // For convenience, keep aphabetical order in this list. + IMPLEMENT_WAYLAND_OBJECT_TRAITS(augmented_surface) + IMPLEMENT_WAYLAND_OBJECT_TRAITS(augmented_sub_surface) ++IMPLEMENT_WAYLAND_OBJECT_TRAITS(bs_z_order_v1) + IMPLEMENT_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_device) + IMPLEMENT_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_device_manager) + IMPLEMENT_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_offer) +diff --git a/ui/ozone/platform/wayland/common/wayland_object.h b/ui/ozone/platform/wayland/common/wayland_object.h +index c84c084841ea9..9f2c034da7698 100644 +--- a/ui/ozone/platform/wayland/common/wayland_object.h ++++ b/ui/ozone/platform/wayland/common/wayland_object.h +@@ -6,6 +6,7 @@ + #define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_OBJECT_H_ + + #include ++#include + + #include "base/check.h" + #include "ui/ozone/platform/wayland/common/wayland.h" +@@ -105,6 +106,7 @@ bool CanBind(const std::string& interface, + // For convenience, keep aphabetical order in this list. + DECLARE_WAYLAND_OBJECT_TRAITS(augmented_surface) + DECLARE_WAYLAND_OBJECT_TRAITS(augmented_sub_surface) ++DECLARE_WAYLAND_OBJECT_TRAITS(bs_z_order_v1) + DECLARE_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_device) + DECLARE_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_device_manager) + DECLARE_WAYLAND_OBJECT_TRAITS(gtk_primary_selection_offer) +diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc +index a7c0943650f05..2a018c632898d 100644 +--- a/ui/ozone/platform/wayland/host/wayland_connection.cc ++++ b/ui/ozone/platform/wayland/host/wayland_connection.cc +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -75,6 +76,7 @@ namespace { + // The maximum supported versions for a given interface. + // The version bound will be the minimum of the value and the version + // advertised by the server. ++constexpr uint32_t kMaxBsZOrderVersion = 1; + constexpr uint32_t kMaxCompositorVersion = 4; + constexpr uint32_t kMaxKeyboardExtensionVersion = 2; + constexpr uint32_t kMaxXdgShellVersion = 5; +@@ -490,7 +492,7 @@ void WaylandConnection::OnGlobal(void* data, + uint32_t version) { + auto* self = static_cast(data); + DCHECK(self); +- self->HandleGlobal(registry, name, interface, version); ++ self->HandleGlobal(data, registry, name, interface, version); + } + + // static +@@ -528,13 +530,24 @@ void WaylandConnection::OnClockId(void* data, + connection->presentation_clk_id_ = clk_id; + } + +-void WaylandConnection::HandleGlobal(wl_registry* registry, ++void WaylandConnection::HandleGlobal(void* data, ++ wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) { ++ auto* connection = static_cast(data); ++ + auto factory_it = global_object_factories_.find(interface); + if (factory_it != global_object_factories_.end()) { + (*factory_it->second)(this, registry, name, interface, version); ++ } else if (!connection->bs_z_order_v1_ && ++ strcmp(interface, "bs_z_order_v1") == 0) { ++ connection->bs_z_order_v1_ = wl::Bind( ++ registry, name, std::min(version, kMaxBsZOrderVersion)); ++ if (!connection->bs_z_order_v1_) { ++ LOG(ERROR) << "Failed to bind to bs_z_order_v1 global"; ++ return; ++ } + } else if (!compositor_ && strcmp(interface, "wl_compositor") == 0) { + compositor_ = wl::Bind( + registry, name, std::min(version, kMaxCompositorVersion)); +diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h +index c78d900d03acd..2cb8078dbaab0 100644 +--- a/ui/ozone/platform/wayland/host/wayland_connection.h ++++ b/ui/ozone/platform/wayland/host/wayland_connection.h +@@ -110,6 +110,8 @@ class WaylandConnection { + // error. Called by WaylandEventWatcher. + void SetShutdownCb(base::OnceCallback shutdown_cb); + ++ bs_z_order_v1* bs_z_order() const { return bs_z_order_v1_.get(); } ++ + // A correct display must be chosen when creating objects or calling + // roundrips. That is, all the methods that deal with polling, pulling event + // queues, etc, must use original display. All the other methods that create +@@ -422,7 +424,8 @@ class WaylandConnection { + wp_presentation* presentation, + uint32_t clk_id); + +- void HandleGlobal(wl_registry* registry, ++ void HandleGlobal(void* data, ++ wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version); +@@ -430,6 +433,7 @@ class WaylandConnection { + base::flat_map global_object_factories_; + + uint32_t compositor_version_ = 0; ++ wl::Object bs_z_order_v1_; + wl::Object display_; + wl::Object wrapped_display_; + wl::Object event_queue_; +diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc +index bd3f0ce0d7e29..dd66fd2e2a635 100644 +--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc ++++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc +@@ -6,6 +6,7 @@ + + #include + #include ++#include + + #include "base/logging.h" + #include "base/notreached.h" +@@ -577,6 +578,10 @@ void XDGToplevelWrapperImpl::SetZOrder(ZOrderLevel z_order) { + zaura_toplevel_set_z_order(aura_toplevel_.get(), + ToZauraToplevelZOrderLevel(z_order)); + } ++ else if (!aura_toplevel_ && connection_->bs_z_order()) { ++ // The z-index we get if we are using xdg on a BS device is an absolute value. ++ bs_z_order_v1_set_z_index(connection_->bs_z_order(), wayland_window_->root_surface()->surface(), (int)z_order); ++ } + } + + bool XDGToplevelWrapperImpl::SupportsActivation() { From d78a7110a301fa711388cb1ba368a5077427c01b Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Fri, 12 May 2023 12:59:15 +0100 Subject: [PATCH 006/116] OS-14339: Support negative window offsets (#18) Patch to set the x & y offsets from the window bounds in the SetWindowGeometry call. Cherry picked from: 059bbdef9776118c09e25f39e226555a06c538bd: OS-14339: Support negative window offsets (#18) --- patches/chromium/.patches | 3 +- ...pass_window_boundary_x_and_y_offsets.patch | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 patches/chromium/os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 23a2567d7e19a..04ac8b1bb3789 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -146,4 +146,5 @@ reland_mojom_ts_generator_handle_empty_module_path_identically_to.patch ensure_an_axcontext_before_painting.patch feat_os_14967_force_sniffing_file_urls.patch feat_backported_add_storage_quota_capability_for_partitions.patch -OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch \ No newline at end of file +OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch +os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch \ No newline at end of file diff --git a/patches/chromium/os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch b/patches/chromium/os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch new file mode 100644 index 0000000000000..8f564164948da --- /dev/null +++ b/patches/chromium/os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir +Date: Thu, 11 May 2023 17:53:08 +0100 +Subject: OS-14339: BrightSign change to pass window boundary x and y offsets + to SetGeometry + +Patch to set the x & y offsets from the window bounds in the SetWindowGeometry call. +This ensures that the offsets are passed to the Wayland compositor through the +xdg_surface_set_window_geometry call. This is because we don't support aura shell +and hence cannot use RequestWindowBounds calls to set the offset. + +diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +index 8d35bbc02a649..695531d1d2805 100644 +--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc ++++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +@@ -715,6 +715,12 @@ void WaylandToplevelWindow::SetWindowGeometry(gfx::Size size_dip) { + if (state_ == PlatformWindowState::kNormal && !insets.IsEmpty()) { + geometry_dip.Inset(insets); + ++ // BrightSign modification to set the x & y offsets from the window bounds ++ // in the SetWindowGeometry call. This is because we don't support aura shell ++ // and hence cannot use RequestWindowBounds calls to set the offset. ++ geometry_dip.set_x(GetBoundsInDIP().x()); ++ geometry_dip.set_y(GetBoundsInDIP().y()); ++ + // Shrinking the bounds by the decoration insets might result in empty + // bounds. For the reasons already explained in WaylandWindow::Initialize(), + // we mustn't request an empty window geometry. From f59a20c6e4d620d34626659fc17718924ced44ea Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Mon, 15 May 2023 10:57:01 +0100 Subject: [PATCH 007/116] feat: Add hideScrollBars BrowserWindow constructor param (#19) * OS-15351: Add hideScrollBars BrowserWindow constructor param * tests: Add hideScrollBars test case to BrowserWindow spec * tests: Add vertical scrollbar check Cherry picked from: cd238ba4e93b232f2dccf96c2a9cfcadd696c900: feat: Add hideScrollBars BrowserWindow constructor param (#19) --- docs/api/structures/web-preferences.md | 2 ++ shell/browser/web_contents_preferences.cc | 3 ++ shell/browser/web_contents_preferences.h | 1 + shell/common/options_switches.cc | 2 ++ shell/common/options_switches.h | 1 + spec/api-browser-window-spec.ts | 37 +++++++++++++++++++++++ spec/fixtures/pages/scrollbars.html | 18 +++++++++++ 7 files changed, 64 insertions(+) create mode 100644 spec/fixtures/pages/scrollbars.html diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index 9c6a3ecc8fc8c..a29dcdc40cdc3 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -143,6 +143,8 @@ contain the layout of the document—without requiring scrolling. Enabling this will cause the `preferred-size-changed` event to be emitted on the `WebContents` when the preferred size changes. Default is `false`. +* `hideScrollBars` boolean (optional) - Sets the Chromium WebPreferences + hide_scrollbars field. Default is `false`. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index 4cab07cac41b6..bb65f26d9296c 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -127,6 +127,7 @@ void WebContentsPreferences::Clear() { webgl_ = true; enable_websql_ = true; enable_preferred_size_mode_ = false; + hide_scroll_bars_ = false; web_security_ = true; allow_running_insecure_content_ = false; offscreen_ = false; @@ -187,6 +188,7 @@ void WebContentsPreferences::SetFromDictionary( web_preferences.Get(options::kEnableWebSQL, &enable_websql_); web_preferences.Get(options::kEnablePreferredSizeMode, &enable_preferred_size_mode_); + web_preferences.Get(options::kHideScrollBars, &hide_scroll_bars_); web_preferences.Get(options::kWebSecurity, &web_security_); if (!web_preferences.Get(options::kAllowRunningInsecureContent, &allow_running_insecure_content_) && @@ -493,6 +495,7 @@ void WebContentsPreferences::OverrideWebkitPrefs( prefs->enable_websql = enable_websql_; prefs->v8_cache_options = v8_cache_options_; + prefs->hide_scrollbars = hide_scroll_bars_; } WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPreferences); diff --git a/shell/browser/web_contents_preferences.h b/shell/browser/web_contents_preferences.h index e46ee17bbbda7..2b3a180da3fdc 100644 --- a/shell/browser/web_contents_preferences.h +++ b/shell/browser/web_contents_preferences.h @@ -112,6 +112,7 @@ class WebContentsPreferences bool webgl_; bool enable_websql_; bool enable_preferred_size_mode_; + bool hide_scroll_bars_; bool web_security_; bool allow_running_insecure_content_; bool offscreen_; diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index f353e23b2589f..7041904c046ff 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -188,6 +188,8 @@ const char kEnableWebSQL[] = "enableWebSQL"; const char kEnablePreferredSizeMode[] = "enablePreferredSizeMode"; +const char kHideScrollBars[] = "hideScrollBars"; + const char ktitleBarOverlay[] = "titleBarOverlay"; } // namespace options diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index bb7936a3b14ef..7941fdf044ff4 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -89,6 +89,7 @@ extern const char kWebGL[]; extern const char kNavigateOnDragDrop[]; extern const char kEnableWebSQL[]; extern const char kEnablePreferredSizeMode[]; +extern const char kHideScrollBars[]; extern const char kHiddenPage[]; diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index a76215278aee3..a3a515990dc70 100644 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -6568,4 +6568,41 @@ describe('BrowserWindow module', () => { expect(areColorsSimilar(centerColor, HexColors.BLUE)).to.be.true(); }); }); + + describe('hideScrollBars webpreference', () => { + const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures'); + let w: BrowserWindow; + + afterEach(closeAllWindows); + + it('shows scrollbar by default', async () => { + w = new BrowserWindow({ width: 400, height: 400 }); + const url = `file://${fixturesPath}/pages/scrollbars.html`; + await w.loadURL(url); + const { scrollWidth } = await w.webContents.executeJavaScript('({scrollWidth: window.innerWidth - document.documentElement.clientWidth})'); + expect(scrollWidth).to.be.greaterThan(0); + const { scrollHeight } = await w.webContents.executeJavaScript('({scrollHeight: window.innerHeight - document.documentElement.clientHeight})'); + expect(scrollHeight).to.be.greaterThan(0); + }); + + it('shows scrollbar when set to false', async () => { + w = new BrowserWindow({ width: 400, height: 400, webPreferences: { hideScrollBars: false } }); + const url = `file://${fixturesPath}/pages/scrollbars.html`; + await w.loadURL(url); + const { scrollWidth } = await w.webContents.executeJavaScript('({scrollWidth: window.innerWidth - document.documentElement.clientWidth})'); + expect(scrollWidth).to.be.greaterThan(0); + const { scrollHeight } = await w.webContents.executeJavaScript('({scrollHeight: window.innerHeight - document.documentElement.clientHeight})'); + expect(scrollHeight).to.be.greaterThan(0); + }); + + it('hides scrollbar when set to true', async () => { + w = new BrowserWindow({ width: 400, height: 400, webPreferences: { hideScrollBars: true } }); + const url = `file://${fixturesPath}/pages/scrollbars.html`; + await w.loadURL(url); + const { scrollWidth } = await w.webContents.executeJavaScript('({scrollWidth: window.innerWidth - document.documentElement.clientWidth})'); + expect(scrollWidth).to.be.equal(0); + const { scrollHeight } = await w.webContents.executeJavaScript('({scrollHeight: window.innerHeight - document.documentElement.clientHeight})'); + expect(scrollHeight).to.be.equal(0); + }); + }); }); diff --git a/spec/fixtures/pages/scrollbars.html b/spec/fixtures/pages/scrollbars.html new file mode 100644 index 0000000000000..432309524da45 --- /dev/null +++ b/spec/fixtures/pages/scrollbars.html @@ -0,0 +1,18 @@ + + + + + Scrollbars Test + + + +

Scrollbars Test

+

This is some long content that will cause scrolling.

+ + \ No newline at end of file From 6127ae41fe79706dbaebf7c04f9bb186e10b30c8 Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:15:30 +0100 Subject: [PATCH 008/116] feat: Use remote-debugging-address in devtools (#26) Use remote-debugging-address in devtools Cherry picked from: 87abff9781afe5013932be6fe3e15cf7f0c3c4b7: feat: Use remote-debugging-address in devtools (#26) --- patches/chromium/.patches | 3 +- ...ebugging-address_to_content_switches.patch | 39 +++++++++++++++++++ shell/browser/ui/devtools_manager_delegate.cc | 11 +++++- spec/chromium-spec.ts | 32 +++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 patches/chromium/os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 04ac8b1bb3789..ef23bd6f602f2 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -147,4 +147,5 @@ ensure_an_axcontext_before_painting.patch feat_os_14967_force_sniffing_file_urls.patch feat_backported_add_storage_quota_capability_for_partitions.patch OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch -os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch \ No newline at end of file +os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch +os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch diff --git a/patches/chromium/os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch b/patches/chromium/os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch new file mode 100644 index 0000000000000..a087753bb6355 --- /dev/null +++ b/patches/chromium/os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir +Date: Wed, 28 Jun 2023 16:37:03 +0100 +Subject: OS-14354 BrightSign: add remote-debugging-address to content switches + +A change has been added to Electron TCPServerSocketFactory to allow a custom +address to be used for remote debugging (i.e. use 0.0.0.0 instead of 127.0.0.1). +This change adds the remote-debugging-address switch to the content switches to +allow it to be used in Electron. + +diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc +index a11a8178ada26..6f0df20012edb 100644 +--- a/content/public/common/content_switches.cc ++++ b/content/public/common/content_switches.cc +@@ -669,6 +669,12 @@ const char kRegisterPepperPlugins[] = "register-pepper-plugins"; + // "JSON" (the default) or "CBOR". + const char kRemoteDebuggingPipe[] = "remote-debugging-pipe"; + ++// Use the given address instead of the default loopback for accepting remote ++// debugging connections. Should be used together with --remote-debugging-port. ++// Note that the remote debugging protocol does not perform any authentication, ++// so exposing it too widely can be a security risk. ++const char kRemoteDebuggingAddress[] = "remote-debugging-address"; ++ + // Enables remote debug over HTTP on the specified port. + const char kRemoteDebuggingPort[] = "remote-debugging-port"; + +diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h +index b8f1c93f56c31..f02d9b86a900a 100644 +--- a/content/public/common/content_switches.h ++++ b/content/public/common/content_switches.h +@@ -189,6 +189,7 @@ CONTENT_EXPORT extern const char kReduceUserAgentPlatformOsCpu[]; + CONTENT_EXPORT extern const char kRegisterPepperPlugins[]; + CONTENT_EXPORT extern const char kRemoteDebuggingPipe[]; + CONTENT_EXPORT extern const char kRemoteDebuggingPort[]; ++CONTENT_EXPORT extern const char kRemoteDebuggingAddress[]; + CONTENT_EXPORT extern const char kRemoteAllowOrigins[]; + CONTENT_EXPORT extern const char kRendererClientId[]; + extern const char kRendererCmdPrefix[]; diff --git a/shell/browser/ui/devtools_manager_delegate.cc b/shell/browser/ui/devtools_manager_delegate.cc index c4113d37e38de..b2accb3b1268c 100644 --- a/shell/browser/ui/devtools_manager_delegate.cc +++ b/shell/browser/ui/devtools_manager_delegate.cc @@ -80,7 +80,16 @@ std::unique_ptr CreateSocketFactory() { DLOG(WARNING) << "Invalid http debugger port number " << temp_port; } } - return std::make_unique("127.0.0.1", port); + net::IPAddress address; + std::string address_str = "127.0.0.1"; + if (command_line.HasSwitch(switches::kRemoteDebuggingAddress)) { + address_str = + command_line.GetSwitchValueASCII(switches::kRemoteDebuggingAddress); + if (!address.AssignFromIPLiteral(address_str)) { + DLOG(WARNING) << "Invalid devtools server address: " << address_str; + } + } + return std::make_unique(address_str, port); } const char kBrowserCloseMethod[] = "Browser.close"; diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 6d21d1a0c7f0e..36127ea05061f 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -552,6 +552,38 @@ describe('command line switches', () => { }); }); }); + + describe('--remote-debugging-address and remote-debugging-port switches', () => { + it('should display the discovery page', (done) => { + const electronPath = process.execPath; + let output = ''; + appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-address=0.0.0.0', '--remote-debugging-port=']); + appProcess.stdout.on('data', (data) => { + console.log(data); + }); + + appProcess.stderr.on('data', (data) => { + console.log(data); + output += data; + const m = /DevTools listening on ws:\/\/0.0.0.0:(\d+)\//.exec(output); + if (m) { + appProcess!.stderr.removeAllListeners('data'); + const port = m[1]; + http.get(`http://127.0.0.1:${port}`, (res) => { + try { + expect(res.statusCode).to.eql(200); + expect(parseInt(res.headers['content-length']!)).to.be.greaterThan(0); + done(); + } catch (e) { + done(e); + } finally { + res.destroy(); + } + }); + } + }); + }); + }); }); describe('chromium features', () => { From 370cbab97cf9226c1512ae5f2a4e505601395b5c Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:02:52 +0100 Subject: [PATCH 009/116] feat: Add a webpreference to disable pinch to zoom (#24) OS-14345: Add a webpreference to disable pinch to zoom In chromium pinch to zoom can be disabled globally using the command line switch disable-pinch. BrightSign has a requirement to beable to disable pinch to zoom at a browser window level. This patch adds enable_pinch_zoom as a webpreference and sets its value in RenderWidgetHostViewEventHandler. The webpreference will present on all platforms, but the change to set it is only present in aura (linux). The webpreference will only be used if disable-pinch has not been set on the command line. Cherry picked from: c3ca6fbbc34af90ce4eb8d92dda6b752ad3d4188: feat: Add a webpreference to disable pinch to zoom (#24) Signed-off-by: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> --- docs/api/structures/web-preferences.md | 1 + patches/chromium/.patches | 1 + ...bpreference_to_disable_pinch_to_zoom.patch | 81 +++++++++++++++++++ shell/browser/web_contents_preferences.cc | 3 + shell/browser/web_contents_preferences.h | 1 + shell/common/options_switches.cc | 2 + shell/common/options_switches.h | 1 + 7 files changed, 90 insertions(+) create mode 100644 patches/chromium/os-14345_brightsign_add_a_webpreference_to_disable_pinch_to_zoom.patch diff --git a/docs/api/structures/web-preferences.md b/docs/api/structures/web-preferences.md index a29dcdc40cdc3..ae98aca0a6133 100644 --- a/docs/api/structures/web-preferences.md +++ b/docs/api/structures/web-preferences.md @@ -145,6 +145,7 @@ `WebContents` when the preferred size changes. Default is `false`. * `hideScrollBars` boolean (optional) - Sets the Chromium WebPreferences hide_scrollbars field. Default is `false`. +* `enablePinchZoom` boolean (optional) - Sets whether to enable pinch to zoom in Chromium. Default is `true`. [chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment [runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5 diff --git a/patches/chromium/.patches b/patches/chromium/.patches index ef23bd6f602f2..b6649384b8f4f 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -148,4 +148,5 @@ feat_os_14967_force_sniffing_file_urls.patch feat_backported_add_storage_quota_capability_for_partitions.patch OS-15118_add_bs_set_z_order_custom_feature_to_wayland.patch os-14339_brightsign_change_to_pass_window_boundary_x_and_y_offsets.patch +os-14345_brightsign_add_a_webpreference_to_disable_pinch_to_zoom.patch os-14354_brightsign_add_remote-debugging-address_to_content_switches.patch diff --git a/patches/chromium/os-14345_brightsign_add_a_webpreference_to_disable_pinch_to_zoom.patch b/patches/chromium/os-14345_brightsign_add_a_webpreference_to_disable_pinch_to_zoom.patch new file mode 100644 index 0000000000000..6f14b7fe8d655 --- /dev/null +++ b/patches/chromium/os-14345_brightsign_add_a_webpreference_to_disable_pinch_to_zoom.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tariq Bashir +Date: Thu, 22 Jun 2023 14:23:48 +0100 +Subject: OS-14345: BrightSign Add a webpreference to disable pinch to zoom + +In chromium pinch to zoom can be disabled globally using the command line switch +disable-pinch. BrightSign has a requirement to beable to disable pinch to zoom +at a browser window level. This patch adds enable_pinch_zoom as a webpreference +and sets its value in RenderWidgetHostViewEventHandler. The webpreference will +present on all platforms, but the change to set it is only present in aura (linux). +The webpreference will only be used if disable-pinch has not been set on the +command line. + +diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc +index 06bfc3927dd049404ead14e98e1f09f0326da8f0..6a072f39fd366f8dde564e2776c460f8159164cd 100644 +--- a/content/browser/renderer_host/render_widget_host_view_aura.cc ++++ b/content/browser/renderer_host/render_widget_host_view_aura.cc +@@ -299,6 +299,8 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura( + double_tap_to_zoom_enabled_ = + owner_delegate->GetWebkitPreferencesForWidget() + .double_tap_to_zoom_enabled; ++ ++ event_handler_->SetPinchZoomEnabled(owner_delegate->GetWebkitPreferencesForWidget().enable_pinch_zoom); + } + + host()->render_frame_metadata_provider()->AddObserver(this); +diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc +index fe5280864311410d4f74bffe395ca95a1263ba2d..7cc1c8e4d29f558071906eac2614087dc439a89d 100644 +--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc ++++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc +@@ -126,6 +126,16 @@ RenderWidgetHostViewEventHandler::~RenderWidgetHostViewEventHandler() { + DCHECK(!mouse_locked_); + } + ++void RenderWidgetHostViewEventHandler::SetPinchZoomEnabled(bool pinch_zoom_enabled) { ++ // Pinch to zoom is controlled globally by a command line switch and (as a BrightSign ++ // feature) also by a webpreference. If the command line switch has been used, this will ++ // disable pinch to zoom and override the web preference. Otherwise we use the web ++ // preference value that is passed. ++ if (content::IsPinchToZoomEnabled()) { ++ pinch_zoom_enabled_ = pinch_zoom_enabled; ++ } ++} ++ + void RenderWidgetHostViewEventHandler::SetPopupChild( + RenderWidgetHostViewBase* popup_child_host_view, + ui::EventHandler* popup_child_event_handler) { +diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h +index 0ba031d1ec6bdbef7725a576673abb146cb6b411..e07d006d04c6030a6c6df2c961bed724c495b100 100644 +--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h ++++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h +@@ -180,6 +180,8 @@ class CONTENT_EXPORT RenderWidgetHostViewEventHandler + .set_max_time_between_phase_ended_and_momentum_phase_began(timeout); + } + ++ void SetPinchZoomEnabled(bool pinch_zoom_enabled); ++ + private: + FRIEND_TEST_ALL_PREFIXES(InputMethodResultAuraTest, + FinishImeCompositionSession); +@@ -278,7 +280,7 @@ class CONTENT_EXPORT RenderWidgetHostViewEventHandler + + // Whether pinch-to-zoom should be enabled and pinch events forwarded to the + // renderer. +- const bool pinch_zoom_enabled_; ++ bool pinch_zoom_enabled_; + + // This flag when set ensures that we send over a notification to blink that + // the current view has focus. +diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h +index b47ba60ec901460db3b1a8a2f81f5b7a9006c647..ff0b46e28ccbbb6c3fc253cd32a1044ae0e8f4f2 100644 +--- a/third_party/blink/public/common/web_preferences/web_preferences.h ++++ b/third_party/blink/public/common/web_preferences/web_preferences.h +@@ -88,6 +88,7 @@ struct BLINK_COMMON_EXPORT WebPreferences { + bool privileged_webgl_extensions_enabled; + bool webgl_errors_to_console_enabled; + bool hide_scrollbars; ++ bool enable_pinch_zoom; + // If false, ignore ::-webkit-scrollbar-* CSS pseudo-elements in stylesheets. + bool enable_webkit_scrollbar_styling = true; + bool accelerated_2d_canvas_enabled; diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index bb65f26d9296c..fdc8da125b811 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -127,6 +127,7 @@ void WebContentsPreferences::Clear() { webgl_ = true; enable_websql_ = true; enable_preferred_size_mode_ = false; + enable_pinch_zoom_ = true; hide_scroll_bars_ = false; web_security_ = true; allow_running_insecure_content_ = false; @@ -188,6 +189,7 @@ void WebContentsPreferences::SetFromDictionary( web_preferences.Get(options::kEnableWebSQL, &enable_websql_); web_preferences.Get(options::kEnablePreferredSizeMode, &enable_preferred_size_mode_); + web_preferences.Get(options::kEnablePinchZoom, &enable_pinch_zoom_); web_preferences.Get(options::kHideScrollBars, &hide_scroll_bars_); web_preferences.Get(options::kWebSecurity, &web_security_); if (!web_preferences.Get(options::kAllowRunningInsecureContent, @@ -496,6 +498,7 @@ void WebContentsPreferences::OverrideWebkitPrefs( prefs->v8_cache_options = v8_cache_options_; prefs->hide_scrollbars = hide_scroll_bars_; + prefs->enable_pinch_zoom = enable_pinch_zoom_; } WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPreferences); diff --git a/shell/browser/web_contents_preferences.h b/shell/browser/web_contents_preferences.h index 2b3a180da3fdc..2cf340e95346f 100644 --- a/shell/browser/web_contents_preferences.h +++ b/shell/browser/web_contents_preferences.h @@ -112,6 +112,7 @@ class WebContentsPreferences bool webgl_; bool enable_websql_; bool enable_preferred_size_mode_; + bool enable_pinch_zoom_; bool hide_scroll_bars_; bool web_security_; bool allow_running_insecure_content_; diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index 7041904c046ff..6ca84eae3cf30 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -192,6 +192,8 @@ const char kHideScrollBars[] = "hideScrollBars"; const char ktitleBarOverlay[] = "titleBarOverlay"; +const char kEnablePinchZoom[] = "enablePinchZoom"; + } // namespace options namespace switches { diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index 7941fdf044ff4..5c6070b7cb592 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -90,6 +90,7 @@ extern const char kNavigateOnDragDrop[]; extern const char kEnableWebSQL[]; extern const char kEnablePreferredSizeMode[]; extern const char kHideScrollBars[]; +extern const char kEnablePinchZoom[]; extern const char kHiddenPage[]; From 566d8c77b24e349126dec0567c09ef2b4fb9fab2 Mon Sep 17 00:00:00 2001 From: Tariq Bashir <120014322+t-bashir-bs@users.noreply.github.com> Date: Thu, 6 Jul 2023 16:34:38 +0100 Subject: [PATCH 010/116] feat: Add will-open-dialog event to webcontents (#30) * Add will-open-dialog event to webcontent * Added test cases to api-web-contents-spec * Fix originUrl param being set as the path Cherry picked from: faae34809db71bd01a68c1bb7ea5d4bdfc2b6c1d: feat: Add will-open-dialog event to webcontents (#30) --- docs/api/web-contents.md | 22 +++++ .../electron_javascript_dialog_manager.cc | 83 ++++++++++++++++++- .../electron_javascript_dialog_manager.h | 14 ++++ spec/api-web-contents-spec.ts | 61 ++++++++++++++ 4 files changed, 176 insertions(+), 4 deletions(-) diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 5a2ce344b896e..1315ff3aa58d5 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -997,6 +997,28 @@ Returns: Emitted when the [mainFrame](web-contents.md#contentsmainframe-readonly), an `