diff --git a/.github/workflows/cherry_pick_into_release_branch.yml b/.github/workflows/cherry_pick_into_release_branch.yml index b0f635a69b199..980cd0a3c77bc 100644 --- a/.github/workflows/cherry_pick_into_release_branch.yml +++ b/.github/workflows/cherry_pick_into_release_branch.yml @@ -26,7 +26,7 @@ jobs: echo "Version is not a two digit semver version" exit 1 fi - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: ref: release-${{ github.event.inputs.version }} fetch-depth: 0 @@ -65,7 +65,7 @@ jobs: app-id: ${{ vars.PLAYWRIGHT_APP_ID }} private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} - name: Create Pull Request - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ steps.app-token.outputs.token }} script: | diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 54cf652ae21b6..06a85aed69b94 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -8,8 +8,8 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: "22" - run: npm ci diff --git a/.github/workflows/create_test_report.yml b/.github/workflows/create_test_report.yml index 6758556deff7e..5afe85e0c72d9 100644 --- a/.github/workflows/create_test_report.yml +++ b/.github/workflows/create_test_report.yml @@ -14,10 +14,10 @@ jobs: if: ${{ github.event.workflow_run.event == 'pull_request' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci env: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 diff --git a/.github/workflows/infra.yml b/.github/workflows/infra.yml index 0a03f10f1338a..fa6f958a57515 100644 --- a/.github/workflows/infra.yml +++ b/.github/workflows/infra.yml @@ -18,10 +18,10 @@ jobs: name: "docs & lint" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci - run: npm run build - run: npx playwright install --with-deps @@ -40,17 +40,17 @@ jobs: name: "Lint snippets" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 - - uses: actions/setup-python@v5 + node-version: 20 + - uses: actions/setup-python@v6 with: python-version: '3.11' - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: '21' diff --git a/.github/workflows/pr_check_client_side_changes.yml b/.github/workflows/pr_check_client_side_changes.yml index 12b7e5dff9131..2389d56e85cf8 100644 --- a/.github/workflows/pr_check_client_side_changes.yml +++ b/.github/workflows/pr_check_client_side_changes.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-24.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/create-github-app-token@v2 id: app-token with: @@ -27,7 +27,7 @@ jobs: playwright-java playwright-dotnet - name: Create GitHub issue - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ steps.app-token.outputs.token }} script: | diff --git a/.github/workflows/publish_canary.yml b/.github/workflows/publish_release.yml similarity index 66% rename from .github/workflows/publish_canary.yml rename to .github/workflows/publish_release.yml index 87ebcf27b3a5e..b2f9cb2e7dcb1 100644 --- a/.github/workflows/publish_canary.yml +++ b/.github/workflows/publish_release.yml @@ -1,4 +1,4 @@ -name: "publish canary" +name: "publish release - npm, driver, trace viewer" on: workflow_dispatch: @@ -7,48 +7,52 @@ on: push: branches: - release-* + release: + types: [published] env: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 jobs: - publish-canary: - name: "publish canary NPM" + publish-npm-and-driver: + name: "publish NPM and driver" runs-on: ubuntu-24.04 if: github.repository == 'microsoft/playwright' permissions: - id-token: write # This is required for OIDC login (azure/login) to succeed + id-token: write # This is required for OIDC login (azure/login, NPM publish) to succeed contents: read # This is required for actions/checkout to succeed environment: allow-publish-driver-to-cdn # This is required for OIDC login (azure/login) steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 registry-url: 'https://registry.npmjs.org' + # Ensure npm 11.5.1 or later is installed (for OIDC npm publishing) + - name: Update npm + run: npm install -g npm@latest - run: npm ci - run: npm run build + - name: "@next: publish with commit timestamp (triggered manually)" if: contains(github.ref, 'main') && github.event_name == 'workflow_dispatch' run: | node utils/build/update_canary_version.js --alpha --commit-timestamp utils/publish_all_packages.sh --alpha - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: "@next: publish with today's date (triggered automatically)" - if: contains(github.ref, 'main') && github.event_name != 'workflow_dispatch' + if: contains(github.ref, 'main') && github.event.schedule run: | node utils/build/update_canary_version.js --alpha --today-date utils/publish_all_packages.sh --alpha - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: "@beta: publish with commit timestamp (triggered automatically)" - if: contains(github.ref, 'release') && github.event_name != 'workflow_dispatch' + if: contains(github.ref, 'release') && github.event_name == 'push' run: | node utils/build/update_canary_version.js --beta --commit-timestamp utils/publish_all_packages.sh --beta - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: "publish release to NPM" + if: github.event_name == 'release' && github.event.action == 'published' + run: utils/publish_all_packages.sh --release + - name: Azure Login uses: azure/login@v2 with: @@ -57,7 +61,7 @@ jobs: subscription-id: ${{ secrets.AZURE_PW_CDN_SUBSCRIPTION_ID }} - name: build & publish driver env: - AZ_UPLOAD_FOLDER: driver/next + AZ_UPLOAD_FOLDER: ${{ github.event_name == 'release' && 'driver' || 'driver/next' }} run: | utils/build/build-playwright-driver.sh utils/build/upload-playwright-driver.sh @@ -67,10 +71,10 @@ jobs: runs-on: ubuntu-24.04 if: github.repository == 'microsoft/playwright' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - uses: actions/create-github-app-token@v2 id: app-token with: @@ -82,8 +86,13 @@ jobs: if: contains(github.ref, 'main') env: GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} - - name: Deploy BETA + - name: Deploy Beta run: bash utils/build/deploy-trace-viewer.sh --beta - if: contains(github.ref, 'release') + if: contains(github.ref, 'release') && github.event_name == 'push' + env: + GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} + - name: Deploy Stable + run: bash utils/build/deploy-trace-viewer.sh --stable + if: contains(github.ref, 'release') && github.event_name == 'release' env: GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/publish_release_docker.yml b/.github/workflows/publish_release_docker.yml index c9603bffcdf21..f038bd5aad300 100644 --- a/.github/workflows/publish_release_docker.yml +++ b/.github/workflows/publish_release_docker.yml @@ -18,10 +18,10 @@ jobs: if: github.repository == 'microsoft/playwright' environment: allow-publishing-docker-to-acr steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 registry-url: 'https://registry.npmjs.org' - name: Set up Docker QEMU for arm64 docker builds uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/publish_release_driver.yml b/.github/workflows/publish_release_driver.yml deleted file mode 100644 index 328deaf01cb7a..0000000000000 --- a/.github/workflows/publish_release_driver.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: "publish release - driver" - -on: - release: - types: [published] - -env: - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - -jobs: - publish-driver-release: - name: "publish playwright driver to CDN" - runs-on: ubuntu-24.04 - if: github.repository == 'microsoft/playwright' - permissions: - id-token: write # This is required for OIDC login (azure/login) to succeed - contents: read # This is required for actions/checkout to succeed - environment: allow-publish-driver-to-cdn # This is required for OIDC login (azure/login) - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 18 - registry-url: 'https://registry.npmjs.org' - - run: npm ci - - run: npm run build - - run: utils/build/build-playwright-driver.sh - - name: Azure Login - uses: azure/login@v2 - with: - client-id: ${{ secrets.AZURE_PW_CDN_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_PW_CDN_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_PW_CDN_SUBSCRIPTION_ID }} - - run: utils/build/upload-playwright-driver.sh - env: - AZ_UPLOAD_FOLDER: driver diff --git a/.github/workflows/publish_release_npm.yml b/.github/workflows/publish_release_npm.yml deleted file mode 100644 index cab5e13596467..0000000000000 --- a/.github/workflows/publish_release_npm.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "publish release - NPM" - -on: - release: - types: [published] - -env: - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - -jobs: - publish-npm-release: - name: "publish to NPM" - runs-on: ubuntu-24.04 - if: github.repository == 'microsoft/playwright' - permissions: - contents: read - id-token: write - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 18 - registry-url: 'https://registry.npmjs.org' - - run: npm ci - - run: npm run build - - run: utils/publish_all_packages.sh --release-candidate - if: ${{ github.event.release.prerelease }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - run: utils/publish_all_packages.sh --release - if: ${{ !github.event.release.prerelease }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish_release_traceviewer.yml b/.github/workflows/publish_release_traceviewer.yml deleted file mode 100644 index b497f5290cdcc..0000000000000 --- a/.github/workflows/publish_release_traceviewer.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: "publish release - TraceViewer" - -on: - release: - types: [published] - -jobs: - publish-trace-viewer: - name: "publish Trace Viewer to trace.playwright.dev" - runs-on: ubuntu-24.04 - if: github.repository == 'microsoft/playwright' - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 18 - - uses: actions/create-github-app-token@v2 - id: app-token - with: - app-id: ${{ vars.PLAYWRIGHT_APP_ID }} - private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} - repositories: trace.playwright.dev - - name: Deploy Stable - run: bash utils/build/deploy-trace-viewer.sh --stable - env: - GH_SERVICE_ACCOUNT_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/roll_browser_into_playwright.yml b/.github/workflows/roll_browser_into_playwright.yml index 3dc822638e361..154f52feaf302 100644 --- a/.github/workflows/roll_browser_into_playwright.yml +++ b/.github/workflows/roll_browser_into_playwright.yml @@ -19,10 +19,10 @@ jobs: roll: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci - run: npm run build - name: Install dependencies @@ -57,7 +57,7 @@ jobs: app-id: ${{ vars.PLAYWRIGHT_APP_ID }} private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} - name: Create Pull Request - uses: actions/github-script@v7 + uses: actions/github-script@v8 if: ${{ steps.prepare-branch.outputs.exists == '0' }} with: github-token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/roll_driver_nodejs.yml b/.github/workflows/roll_driver_nodejs.yml index ee993f980f12d..baafef164a91a 100644 --- a/.github/workflows/roll_driver_nodejs.yml +++ b/.github/workflows/roll_driver_nodejs.yml @@ -12,10 +12,10 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: node utils/build/update-playwright-driver-version.mjs - name: Prepare branch id: prepare-branch @@ -40,7 +40,7 @@ jobs: private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} - name: Create Pull Request if: ${{ steps.prepare-branch.outputs.HAS_CHANGES == '1' }} - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ steps.app-token.outputs.token }} script: | diff --git a/.github/workflows/roll_stable_test_runner.yml b/.github/workflows/roll_stable_test_runner.yml index 2c908520444d8..08c126cb0b548 100644 --- a/.github/workflows/roll_stable_test_runner.yml +++ b/.github/workflows/roll_stable_test_runner.yml @@ -12,8 +12,8 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: 22 - run: | @@ -45,7 +45,7 @@ jobs: private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }} - name: Create Pull Request if: ${{ steps.prepare-branch.outputs.HAS_CHANGES == '1' }} - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: github-token: ${{ steps.app-token.outputs.token }} script: | diff --git a/.github/workflows/tests_bidi.yml b/.github/workflows/tests_bidi.yml index 4d6dc437325ce..60235d95e3a14 100644 --- a/.github/workflows/tests_bidi.yml +++ b/.github/workflows/tests_bidi.yml @@ -34,17 +34,19 @@ jobs: matrix: channel: [bidi-chromium, moz-firefox-nightly] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 if: github.event_name != 'workflow_dispatch' - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 if: github.event_name == 'workflow_dispatch' with: ref: ${{ github.event.inputs.ref }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 20 - run: npm ci - run: npm run build + # Needed for video tests + - run: npx playwright install ffmpeg - run: npx playwright install --with-deps chromium if: matrix.channel == 'bidi-chromium' - if: matrix.channel == 'moz-firefox-nightly' @@ -62,6 +64,14 @@ jobs: path: test-results/report.csv retention-days: 7 + - name: Upload json report to GitHub + if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: json-report-${{ matrix.channel }} + path: test-results/report.json + retention-days: 7 + - name: Azure Login if: ${{ !cancelled() && github.ref == 'refs/heads/main' }} uses: azure/login@v2 diff --git a/.github/workflows/tests_components.yml b/.github/workflows/tests_components.yml index 093d5de0c406d..f403ad8d0c276 100644 --- a/.github/workflows/tests_components.yml +++ b/.github/workflows/tests_components.yml @@ -9,6 +9,8 @@ on: paths-ignore: - 'browser_patches/**' - 'docs/**' + - 'packages/playwright/src/mcp/**' + - 'tests/mcp/**' branches: - main - release-* @@ -32,8 +34,8 @@ jobs: node-version: 22 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }} - run: npm ci diff --git a/.github/workflows/tests_mcp.yml b/.github/workflows/tests_mcp.yml new file mode 100644 index 0000000000000..1beebe5249d9c --- /dev/null +++ b/.github/workflows/tests_mcp.yml @@ -0,0 +1,50 @@ +name: MCP + +on: + push: + branches: + - main + - release-* + pull_request: + paths-ignore: + - 'browser_patches/**' + - 'docs/**' + branches: + - main + - release-* + +concurrency: + # For pull requests, cancel all currently-running jobs for this workflow + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + # Force terminal colors. @see https://www.npmjs.com/package/colors + FORCE_COLOR: 1 + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + DEBUG_GIT_COMMIT_INFO: 1 + +jobs: + test_mcp: + name: ${{ matrix.os }} + # Used for authentication of flakiness upload + environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-15, windows-latest] + runs-on: ${{ matrix.os }} + permissions: + id-token: write # This is required for OIDC login (azure/login) to succeed + contents: read # This is required for actions/checkout to succeed + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/run-test + with: + node-version: "20" + command: npm run test-mcp + bot-name: "mcp-${{ matrix.os }}" + flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} + flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }} + flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }} diff --git a/.github/workflows/tests_others.yml b/.github/workflows/tests_others.yml index ed18e1541c635..f2fc2b0b8343c 100644 --- a/.github/workflows/tests_others.yml +++ b/.github/workflows/tests_others.yml @@ -27,8 +27,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: 20 - run: npm ci @@ -63,8 +63,8 @@ jobs: id-token: write # This is required for OIDC login (azure/login) to succeed contents: read # This is required for actions/checkout to succeed steps: - - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-dotnet@v5 with: dotnet-version: '8.0.x' - run: dotnet build @@ -97,7 +97,7 @@ jobs: clock: [frozen, realtime] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: node-version: 20 @@ -122,7 +122,7 @@ jobs: matrix: clock: [frozen, realtime] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: node-version: 20 @@ -146,7 +146,7 @@ jobs: contents: read # This is required for actions/checkout to succeed runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed if: ${{ runner.os == 'Linux' }} run: | diff --git a/.github/workflows/tests_primary.yml b/.github/workflows/tests_primary.yml index ec575d808e798..2cb96d9ad6148 100644 --- a/.github/workflows/tests_primary.yml +++ b/.github/workflows/tests_primary.yml @@ -9,6 +9,8 @@ on: paths-ignore: - 'browser_patches/**' - 'docs/**' + - 'packages/playwright/src/mcp/**' + - 'tests/mcp/**' branches: - main - release-* @@ -50,7 +52,7 @@ jobs: id-token: write # This is required for OIDC login (azure/login) to succeed contents: read # This is required for actions/checkout to succeed steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: node-version: ${{ matrix.node-version }} @@ -73,7 +75,7 @@ jobs: id-token: write # This is required for OIDC login (azure/login) to succeed contents: read # This is required for actions/checkout to succeed steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: chromium-tip-of-tree @@ -125,7 +127,7 @@ jobs: id-token: write # This is required for OIDC login (azure/login) to succeed contents: read # This is required for actions/checkout to succeed steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: node-version: ${{matrix.node-version}} @@ -141,10 +143,10 @@ jobs: name: Web Components runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci - run: npm run build @@ -177,10 +179,10 @@ jobs: PWTEST_BOT_NAME: "vscode-extension" DEBUG_GIT_COMMIT_INFO: "" steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci env: DEBUG: pw:install @@ -218,12 +220,12 @@ jobs: - macos-latest - windows-latest runs-on: ${{ matrix.os }} - timeout-minutes: 30 + timeout-minutes: 45 permissions: id-token: write # This is required for OIDC login (azure/login) to succeed contents: read # This is required for actions/checkout to succeed steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: npm install -g yarn@1 - run: npm install -g pnpm@8 - name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index eafe68cd60f3e..136eb4a462294 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -34,7 +34,7 @@ jobs: os: [ubuntu-24.04] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium @@ -52,16 +52,14 @@ jobs: matrix: # Intel: *-large # Arm64: *-xlarge - os: [macos-13-large, macos-13-xlarge, macos-14-large, macos-14-xlarge] + os: [macos-14-large, macos-14-xlarge, macos-15-large, macos-15-xlarge] browser: [chromium, firefox, webkit] include: - - os: macos-15-large - browser: webkit - - os: macos-15-xlarge + - os: macos-26-xlarge browser: webkit runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium @@ -80,7 +78,7 @@ jobs: browser: [chromium, firefox, webkit] runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium @@ -106,7 +104,7 @@ jobs: node_version: 24 timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: npm install -g yarn@1 - run: npm install -g pnpm@8 - name: Setup Ubuntu Binary Installation # TODO: Remove when https://github.com/electron/electron/issues/42510 is fixed @@ -139,7 +137,7 @@ jobs: os: ubuntu-22.04 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium @@ -158,7 +156,7 @@ jobs: mode: [driver, service] runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: chromium @@ -189,7 +187,7 @@ jobs: channel: chromium-tip-of-tree runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium ${{ matrix.channel }} @@ -212,7 +210,7 @@ jobs: channel: [chrome, chrome-beta, msedge, msedge-beta, msedge-dev] runs-on: [ubuntu-22.04, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.channel }} @@ -238,7 +236,7 @@ jobs: - os: ubuntu-22.04 headed: '' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: chromium-tip-of-tree @@ -259,7 +257,7 @@ jobs: matrix: os: [ubuntu-22.04] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: chromium-tip-of-tree-headless-shell @@ -280,7 +278,7 @@ jobs: matrix: os: [ubuntu-22.04, windows-latest, macos-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: firefox-beta chromium @@ -296,10 +294,10 @@ jobs: name: "build-playwright-driver" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: - node-version: 18 + node-version: 20 - run: npm ci - run: npm run build - run: utils/build/build-playwright-driver.sh @@ -313,7 +311,7 @@ jobs: runs-on: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: # TODO: this should pass --no-shell. diff --git a/.github/workflows/tests_video.yml b/.github/workflows/tests_video.yml index cc303d79c5aae..1095e293eb79d 100644 --- a/.github/workflows/tests_video.yml +++ b/.github/workflows/tests_video.yml @@ -25,7 +25,7 @@ jobs: contents: read # This is required for actions/checkout to succeed runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: ./.github/actions/run-test with: browsers-to-install: ${{ matrix.browser }} chromium diff --git a/.gitignore b/.gitignore index 8e41e318108ae..d695893a270e2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ node_modules/ .DS_Store *.swp *.pyc -.vscode +/.vscode .mono .idea yarn.lock diff --git a/README.md b/README.md index b3441ab8c93a8..2c176f164f0c3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🎭 Playwright -[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-140.0.7339.16-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-141.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-26.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-informational)](https://aka.ms/playwright/discord) +[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-141.0.7390.37-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-142.0.1-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-26.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-informational)](https://aka.ms/playwright/discord) ## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) @@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 140.0.7339.16 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 141.0.7390.37 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 26.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | -| Firefox 141.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 142.0.1 | :white_check_mark: | :white_check_mark: | :white_check_mark: | Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details. diff --git a/browser_patches/firefox/UPSTREAM_CONFIG.sh b/browser_patches/firefox/UPSTREAM_CONFIG.sh index 0513b8920832a..9d6ff4f566b95 100644 --- a/browser_patches/firefox/UPSTREAM_CONFIG.sh +++ b/browser_patches/firefox/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/mozilla-firefox/firefox" BASE_BRANCH="release" -BASE_REVISION="00656c9425c51ee035578ca6ebebe13c755b0375" +BASE_REVISION="361373160356d92cb5cd4d67783a3806c776ee78" diff --git a/browser_patches/firefox/juggler/JugglerFrameParent.jsm b/browser_patches/firefox/juggler/JugglerFrameParent.jsm index 9c3b58898955e..9d41842a3b896 100644 --- a/browser_patches/firefox/juggler/JugglerFrameParent.jsm +++ b/browser_patches/firefox/juggler/JugglerFrameParent.jsm @@ -16,7 +16,7 @@ export class JugglerFrameParent extends JSWindowActorParent { // Actors are registered per the WindowGlobalParent / WindowGlobalChild pair. We are only // interested in those WindowGlobalParent actors that are matching current browsingContext // window global. - // See https://github.com/mozilla/gecko-dev/blob/cd2121e7d83af1b421c95e8c923db70e692dab5f/testing/mochitest/BrowserTestUtils/BrowserTestUtilsParent.sys.mjs#L15 + // See https://github.com/mozilla-firefox/firefox/blob/35e22180b0b61413dd8eccf6c00b1c6fac073eee/testing/mochitest/BrowserTestUtils/BrowserTestUtilsParent.sys.mjs#L15 if (!this.manager?.isCurrentGlobal) return; diff --git a/browser_patches/firefox/juggler/TargetRegistry.js b/browser_patches/firefox/juggler/TargetRegistry.js index 4bdf162ab64f7..5cddac6f64388 100644 --- a/browser_patches/firefox/juggler/TargetRegistry.js +++ b/browser_patches/firefox/juggler/TargetRegistry.js @@ -1023,9 +1023,9 @@ class BrowserContext { if (ignoreHTTPSErrors) { Preferences.set("network.stricttransportsecurity.preloadlist", false); Preferences.set("security.cert_pinning.enforcement_level", 0); - certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(true, this.userContextId); + certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(this.userContextId, true); } else { - certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(false, this.userContextId); + certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(this.userContextId, false); } } @@ -1167,6 +1167,7 @@ class BrowserContext { getCookies() { const result = []; const sameSiteToProtocol = { + [Ci.nsICookie.SAMESITE_UNSET]: 'None', [Ci.nsICookie.SAMESITE_NONE]: 'None', [Ci.nsICookie.SAMESITE_LAX]: 'Lax', [Ci.nsICookie.SAMESITE_STRICT]: 'Strict', diff --git a/browser_patches/firefox/juggler/components/Juggler.js b/browser_patches/firefox/juggler/components/Juggler.js index 6121c4d3928bc..7595958619381 100644 --- a/browser_patches/firefox/juggler/components/Juggler.js +++ b/browser_patches/firefox/juggler/components/Juggler.js @@ -65,7 +65,7 @@ export class Juggler { } // This flow is taken from Remote agent and Marionette. - // See https://github.com/mozilla/gecko-dev/blob/0c1b4921830e6af8bc951da01d7772de2fe60a08/remote/components/RemoteAgent.jsm#L302 + // See https://github.com/mozilla-firefox/firefox/blob/35e22180b0b61413dd8eccf6c00b1c6fac073eee/remote/components/RemoteAgent.sys.mjs#L417 async observe(subject, topic) { switch (topic) { case "profile-after-change": diff --git a/browser_patches/firefox/juggler/content/main.js b/browser_patches/firefox/juggler/content/main.js index 68fb364eadb4e..462566e626562 100644 --- a/browser_patches/firefox/juggler/content/main.js +++ b/browser_patches/firefox/juggler/content/main.js @@ -21,7 +21,7 @@ export function initialize(browsingContext, docShell) { const applySetting = { geolocation: (geolocation) => { if (geolocation) { - docShell.setGeolocationOverride({ + browsingContext.setGeolocationServiceOverride({ coords: { latitude: geolocation.latitude, longitude: geolocation.longitude, @@ -31,14 +31,12 @@ export function initialize(browsingContext, docShell) { heading: NaN, speed: NaN, }, - address: null, timestamp: Date.now() }); } else { - docShell.setGeolocationOverride(null); + browsingContext.setGeolocationServiceOverride(); } }, - bypassCSP: (bypassCSP) => { docShell.bypassCSPEnabled = bypassCSP; }, @@ -101,16 +99,20 @@ export function initialize(browsingContext, docShell) { }, async awaitViewportDimensions({width, height}) { - const win = docShell.domWindow; - if (win.innerWidth === width && win.innerHeight === height) - return; await new Promise(resolve => { - const listener = helper.addEventListener(win, 'resize', () => { - if (win.innerWidth === width && win.innerHeight === height) { - helper.removeListeners([listener]); + const listeners = []; + const check = () => { + helper.removeListeners(listeners); + if (docShell.domWindow.innerWidth === width && docShell.domWindow.innerHeight === height) { resolve(); + return; } - }); + // Note: "domWindow" listeners are often removed upon navigation, as specced. + // To survive viewport changes across navigations, re-install listeners upon commit. + listeners.push(helper.addEventListener(docShell.domWindow, 'resize', check)); + listeners.push(helper.addEventListener(data.frameTree, 'navigationcommitted', check)); + }; + check(); }); }, diff --git a/browser_patches/firefox/juggler/protocol/BrowserHandler.js b/browser_patches/firefox/juggler/protocol/BrowserHandler.js index 035a53bb688c9..970d8f8822867 100644 --- a/browser_patches/firefox/juggler/protocol/BrowserHandler.js +++ b/browser_patches/firefox/juggler/protocol/BrowserHandler.js @@ -5,6 +5,7 @@ "use strict"; const {AddonManager} = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs"); +const {XPIProvider} = ChromeUtils.importESModule("resource://gre/modules/addons/XPIProvider.sys.mjs"); const {TargetRegistry} = ChromeUtils.importESModule("chrome://juggler/content/TargetRegistry.js"); const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js'); const {PageHandler} = ChromeUtils.importESModule("chrome://juggler/content/protocol/PageHandler.js"); @@ -147,6 +148,10 @@ export class BrowserHandler { ]); } await this._startCompletePromise; + await Promise.all([ + ...XPIProvider.startupPromises, + ...XPIProvider.enabledAddonsStartupPromises, + ]); this._onclose(); Services.startup.quit(Ci.nsIAppStartup.eForceQuit); } @@ -166,7 +171,9 @@ export class BrowserHandler { ['Browser.clearCache']() { // Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147 Services.cache2.clear(); - ChromeUtils.clearStyleSheetCache(); + ChromeUtils.clearResourceCache({ + types: ["stylesheet"], + }); } ['Browser.setHTTPCredentials']({browserContextId, credentials}) { diff --git a/browser_patches/firefox/juggler/screencast/nsScreencastService.cpp b/browser_patches/firefox/juggler/screencast/nsScreencastService.cpp index 7256c8c8ea416..fa9d1a965c570 100644 --- a/browser_patches/firefox/juggler/screencast/nsScreencastService.cpp +++ b/browser_patches/firefox/juggler/screencast/nsScreencastService.cpp @@ -4,6 +4,7 @@ #include "nsScreencastService.h" +#include "gfxPlatform.h" #include "ScreencastEncoder.h" #include "HeadlessWidget.h" #include "HeadlessWindowCapturer.h" diff --git a/browser_patches/firefox/patches/bootstrap.diff b/browser_patches/firefox/patches/bootstrap.diff index 6f70b0342c8ec..523cda6f45274 100644 --- a/browser_patches/firefox/patches/bootstrap.diff +++ b/browser_patches/firefox/patches/bootstrap.diff @@ -12,13 +12,13 @@ index 1bcd464d3bd6b8465f78c62b074b0d57dbc6a082..f878ac9db2d800542dabcc2f48e8ae47 virtual ~NotificationController(); diff --git a/accessible/interfaces/nsIAccessibleDocument.idl b/accessible/interfaces/nsIAccessibleDocument.idl -index 1886621c373fe1fd5ff54092afc4c64e9ca9a8fd..a0febf72885410b45227171c63e823eca118cb37 100644 +index 6c932c14a03e9cbf70723171dc9a1006bd2c017d..6312131c94c6d7e994a4a49dd64b2b4c5777251f 100644 --- a/accessible/interfaces/nsIAccessibleDocument.idl +++ b/accessible/interfaces/nsIAccessibleDocument.idl -@@ -67,4 +67,9 @@ interface nsIAccessibleDocument : nsISupports - * Return the child document accessible at the given index. +@@ -73,4 +73,9 @@ interface nsIAccessibleDocument : nsISupports + * The BrowsingContext of this document. */ - nsIAccessibleDocument getChildDocumentAt(in unsigned long index); + readonly attribute BrowsingContext browsingContext; + + /** + * Return whether it is updating. @@ -26,11 +26,11 @@ index 1886621c373fe1fd5ff54092afc4c64e9ca9a8fd..a0febf72885410b45227171c63e823ec + readonly attribute boolean isUpdatePendingForJugglerAccessibility; }; diff --git a/accessible/xpcom/xpcAccessibleDocument.cpp b/accessible/xpcom/xpcAccessibleDocument.cpp -index d616e476b2149de5703077563680905e40db0459..7a8a48d5e7303a298a3e2e9fdf64558b3cdbe654 100644 +index 3229ee415b93b9a47d7d40605ed7b509744c94ad..24a0837c508f7bc2709538fc28fdafe83ed524f8 100644 --- a/accessible/xpcom/xpcAccessibleDocument.cpp +++ b/accessible/xpcom/xpcAccessibleDocument.cpp -@@ -131,6 +131,13 @@ xpcAccessibleDocument::GetChildDocumentAt(uint32_t aIndex, - return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG; +@@ -151,6 +151,13 @@ xpcAccessibleDocument::GetBrowsingContext( + return NS_OK; } +NS_IMETHODIMP @@ -44,12 +44,12 @@ index d616e476b2149de5703077563680905e40db0459..7a8a48d5e7303a298a3e2e9fdf64558b // xpcAccessibleDocument diff --git a/accessible/xpcom/xpcAccessibleDocument.h b/accessible/xpcom/xpcAccessibleDocument.h -index 8e9bf2b413585b5a3db9370eee5d57fb6c6716ed..5a3b194b54e3813c89989f13a214c989a409f0f6 100644 +index 9b6effc38fbecb50897a93b49c41d4453b10fb7f..939ff9615bd422eec186d8164e2ac7e643f39beb 100644 --- a/accessible/xpcom/xpcAccessibleDocument.h +++ b/accessible/xpcom/xpcAccessibleDocument.h -@@ -47,6 +47,8 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText, - NS_IMETHOD GetChildDocumentAt(uint32_t aIndex, +@@ -48,6 +48,8 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText, nsIAccessibleDocument** aDocument) final; + NS_IMETHOD GetBrowsingContext(dom::BrowsingContext** aBrowsingContext) final; + NS_IMETHOD GetIsUpdatePendingForJugglerAccessibility(bool* aUpdating) final; + @@ -89,10 +89,10 @@ index 8167d2b81c918e02ce757f7f448f22e07c29d140..3ae798880acfd8aa965ae08051f2f818 DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT; diff --git a/browser/installer/allowed-dupes.mn b/browser/installer/allowed-dupes.mn -index a097c2c56a665204ff7b5593c7faf836366801cf..235a4e224e08c22870c6913e335f0b6020b3e7da 100644 +index a315ebcb8b60875b4eddbb5d654764603e0e7bec..fa67db1ff5a331dda4eaff4457418c306869d011 100644 --- a/browser/installer/allowed-dupes.mn +++ b/browser/installer/allowed-dupes.mn -@@ -67,6 +67,12 @@ browser/features/webcompat@mozilla.org/shims/empty-shim.txt +@@ -59,6 +59,12 @@ browser/chrome/browser/builtin-addons/webcompat/shims/empty-shim.txt removed-files #endif @@ -106,10 +106,10 @@ index a097c2c56a665204ff7b5593c7faf836366801cf..235a4e224e08c22870c6913e335f0b60 browser/chrome/browser/content/activity-stream/data/content/tippytop/favicons/allegro-pl.ico browser/defaults/settings/main/search-config-icons/96327a73-c433-5eb4-a16d-b090cadfb80b diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 89cff6b562a82bd3a9a5d268abd4373199b31fac..e58dbf0ab0b06e84f6dac64698d11c6268091204 100644 +index 6ebf2f62c47757f16841d04c164d2d86447fcbd0..cae3f3ac6f6ced0b59fe850643bbb90d619750d0 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in -@@ -196,6 +196,9 @@ +@@ -192,6 +192,9 @@ @RESPATH@/chrome/remote.manifest #endif @@ -118,7 +118,7 @@ index 89cff6b562a82bd3a9a5d268abd4373199b31fac..e58dbf0ab0b06e84f6dac64698d11c62 + ; [Extensions] @RESPATH@/components/extensions-toolkit.manifest - @RESPATH@/browser/components/extensions-browser.manifest + diff --git a/devtools/server/socket/websocket-server.js b/devtools/server/socket/websocket-server.js index d49c6fbf1bf83b832795fa674f6b41f223eef812..7ea3540947ff5f61b15f27fbf4b955649f8e9ff9 100644 --- a/devtools/server/socket/websocket-server.js @@ -167,10 +167,10 @@ index d49c6fbf1bf83b832795fa674f6b41f223eef812..7ea3540947ff5f61b15f27fbf4b95564 const transportProvider = { setListener(upgradeListener) { diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp -index 2425779a7767e9350ee2afc4aea111a090c7f909..393eb86bf2d9a8f778bfce560a9fb3bf528ba558 100644 +index 1a33afa481826b1a53c507d5ea596bcb629d8ac4..8f4f13ff1325b104af80e319cf092be9fd2baab0 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp -@@ -108,8 +108,15 @@ struct ParamTraits +@@ -109,8 +109,15 @@ struct ParamTraits template <> struct ParamTraits @@ -188,7 +188,7 @@ index 2425779a7767e9350ee2afc4aea111a090c7f909..393eb86bf2d9a8f778bfce560a9fb3bf template <> struct ParamTraits -@@ -2891,6 +2898,32 @@ void BrowsingContext::DidSet(FieldIndex, +@@ -2915,6 +2922,32 @@ void BrowsingContext::DidSet(FieldIndex, PresContextAffectingFieldChanged(); } @@ -222,10 +222,10 @@ index 2425779a7767e9350ee2afc4aea111a090c7f909..393eb86bf2d9a8f778bfce560a9fb3bf nsString&& aOldValue) { MOZ_ASSERT(IsTop()); diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h -index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7e65d06f5 100644 +index 0bd3efa16dcae42ff02f42bddce378a7f0c7b621..c8cc1bce7d9901aced6032d0e2e2285cf9eb394b 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h -@@ -203,10 +203,10 @@ struct EmbedderColorSchemes { +@@ -205,10 +205,10 @@ struct EmbedderColorSchemes { FIELD(GVInaudibleAutoplayRequestStatus, GVAutoplayRequestStatus) \ /* ScreenOrientation-related APIs */ \ FIELD(CurrentOrientationAngle, float) \ @@ -238,7 +238,7 @@ index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7 FIELD(EmbedderElementType, Maybe) \ FIELD(MessageManagerGroup, nsString) \ FIELD(MaxTouchPointsOverride, uint8_t) \ -@@ -246,6 +246,9 @@ struct EmbedderColorSchemes { +@@ -245,6 +245,9 @@ struct EmbedderColorSchemes { * embedder element. */ \ FIELD(EmbedderColorSchemes, EmbedderColorSchemes) \ FIELD(DisplayMode, dom::DisplayMode) \ @@ -248,7 +248,7 @@ index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7 /* The number of entries added to the session history because of this \ * browsing context. */ \ FIELD(HistoryEntryCount, uint32_t) \ -@@ -950,6 +953,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { +@@ -967,6 +970,14 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { return GetForcedColorsOverride(); } @@ -263,7 +263,7 @@ index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7 bool IsInBFCache() const; bool AllowJavascript() const { return GetAllowJavascript(); } -@@ -1112,6 +1123,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { +@@ -1131,6 +1142,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { return IsTop(); } @@ -275,7 +275,7 @@ index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7 bool CanSet(FieldIndex, dom::ForcedColorsOverride, ContentParent*) { return IsTop(); -@@ -1130,10 +1146,22 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { +@@ -1149,10 +1165,22 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { void DidSet(FieldIndex, dom::ForcedColorsOverride aOldValue); @@ -299,13 +299,13 @@ index eb183cb5c0751e43ea674f9e52441a5a82f186e0..79c5d8110faa89779dd0c16ba00620e7 bool CanSet(FieldIndex, bool, ContentParent*) { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp -index bef42c91d6f88922c8c101f3675325d828872aaf..8eb68c441fbef8ecbe5e90c118ccc00813564577 100644 +index 1c5c421d6ec153ab61d3aae29200f8abb02b1a33..68751ec0190f308c20aae6b6c58687dcf0fa225d 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp -@@ -324,6 +324,8 @@ void CanonicalBrowsingContext::ReplacedBy( - txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart()); +@@ -325,6 +325,8 @@ void CanonicalBrowsingContext::ReplacedBy( txn.SetForceOffline(GetForceOffline()); txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP()); + txn.SetIPAddressSpace(GetIPAddressSpace()); + txn.SetPrefersReducedMotionOverride(GetPrefersReducedMotionOverride()); + txn.SetForcedColorsOverride(GetForcedColorsOverride()); @@ -325,10 +325,10 @@ index bef42c91d6f88922c8c101f3675325d828872aaf..8eb68c441fbef8ecbe5e90c118ccc008 } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp -index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0d41c43cb 100644 +index 5bcbbfbcb522adb9988ba421a4f590ee2105e1d3..cb9bc09a7901334b6ba73834971498f4472cce2d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp -@@ -15,6 +15,12 @@ +@@ -16,6 +16,12 @@ # include // for getpid() #endif @@ -341,7 +341,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" -@@ -66,6 +72,7 @@ +@@ -67,6 +73,7 @@ #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/FragmentDirective.h" @@ -349,7 +349,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 #include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/Navigation.h" -@@ -94,6 +101,7 @@ +@@ -95,6 +102,7 @@ #include "mozilla/dom/DocumentBinding.h" #include "mozilla/glean/DocshellMetrics.h" #include "mozilla/ipc/ProtocolUtils.h" @@ -357,7 +357,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 #include "mozilla/net/DocumentChannel.h" #include "mozilla/net/DocumentChannelChild.h" #include "mozilla/net/ParentChannelWrapper.h" -@@ -117,6 +125,7 @@ +@@ -118,6 +126,7 @@ #include "nsIDocumentViewer.h" #include "mozilla/dom/Document.h" #include "nsHTMLDocument.h" @@ -365,7 +365,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 #include "nsIDocumentLoaderFactory.h" #include "nsIDOMWindow.h" #include "nsIEditingSession.h" -@@ -211,6 +220,7 @@ +@@ -212,6 +221,7 @@ #include "nsGlobalWindowInner.h" #include "nsGlobalWindowOuter.h" #include "nsJSEnvironment.h" @@ -373,7 +373,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsObjectLoadingContent.h" -@@ -352,6 +362,14 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, +@@ -353,6 +363,14 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, mAllowDNSPrefetch(true), mAllowWindowControl(true), mCSSErrorReportingEnabled(false), @@ -388,7 +388,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 mAllowAuth(mItemType == typeContent), mAllowKeywordFixup(false), mDisableMetaRefreshWhenInactive(false), -@@ -3024,6 +3042,232 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { +@@ -3025,6 +3043,232 @@ nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { return NS_OK; } @@ -621,7 +621,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; -@@ -4731,7 +4975,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { +@@ -4790,7 +5034,7 @@ nsDocShell::GetVisibility(bool* aVisibility) { } void nsDocShell::ActivenessMaybeChanged() { @@ -630,7 +630,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 if (RefPtr presShell = GetPresShell()) { presShell->ActivenessMaybeChanged(); } -@@ -6658,6 +6902,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, +@@ -6723,6 +6967,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType, return false; // no entry to save into } @@ -641,7 +641,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 MOZ_ASSERT(!mozilla::SessionHistoryInParent(), "mOSHE cannot be non-null with SHIP"); nsCOMPtr viewer = mOSHE->GetDocumentViewer(); -@@ -8399,6 +8647,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { +@@ -8464,6 +8712,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { true, // aForceNoOpener getter_AddRefs(newBC)); MOZ_ASSERT(!newBC); @@ -654,7 +654,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 return rv; } -@@ -9572,6 +9826,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, +@@ -9754,6 +10008,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr); nsCOMPtr req; @@ -671,7 +671,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req)); if (NS_SUCCEEDED(rv)) { -@@ -12791,6 +13055,9 @@ class OnLinkClickEvent : public Runnable { +@@ -12970,6 +13234,9 @@ class OnLinkClickEvent : public Runnable { mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied, mTriggeringPrincipal); } @@ -681,7 +681,7 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 return NS_OK; } -@@ -12877,6 +13144,8 @@ nsresult nsDocShell::OnLinkClick( +@@ -13083,6 +13350,8 @@ nsresult nsDocShell::OnLinkClick( nsCOMPtr ev = new OnLinkClickEvent( this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal); @@ -691,18 +691,18 @@ index 32c537d6be90247af8d778048c6d27f3800d4b02..b72196b0694828489f8ad27c209f49f0 } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h -index f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a97978f02 100644 +index 32d1673ea09a2c9f6bb14f6dcd8a9c1c8f9831c4..a301ecd30fcad4d79d370ab316fa0d8e5e509795 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h -@@ -15,6 +15,7 @@ - #include "mozilla/UniquePtr.h" +@@ -16,6 +16,7 @@ #include "mozilla/WeakPtr.h" #include "mozilla/dom/BrowsingContext.h" + #include "mozilla/dom/NavigationBinding.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/WindowProxyHolder.h" #include "nsCOMPtr.h" #include "nsCharsetSource.h" -@@ -77,6 +78,7 @@ class nsCommandManager; +@@ -78,6 +79,7 @@ class nsCommandManager; class nsDocShellEditorData; class nsDOMNavigationTiming; class nsDSURIContentListener; @@ -710,7 +710,7 @@ index f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a class nsGlobalWindowOuter; class FramingChecker; -@@ -403,6 +405,15 @@ class nsDocShell final : public nsDocLoader, +@@ -408,6 +410,15 @@ class nsDocShell final : public nsDocLoader, void SetWillChangeProcess() { mWillChangeProcess = true; } bool WillChangeProcess() { return mWillChangeProcess; } @@ -726,7 +726,7 @@ index f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a // Create a content viewer within this nsDocShell for the given // `WindowGlobalChild` actor. nsresult CreateDocumentViewerForActor( -@@ -1006,6 +1017,8 @@ class nsDocShell final : public nsDocLoader, +@@ -1013,6 +1024,8 @@ class nsDocShell final : public nsDocLoader, bool CSSErrorReportingEnabled() const { return mCSSErrorReportingEnabled; } @@ -735,7 +735,7 @@ index f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a // Handles retrieval of subframe session history for nsDocShell::LoadURI. If a // load is requested in a subframe of the current DocShell, the subframe // loadType may need to reflect the loadType of the parent document, or in -@@ -1285,6 +1298,17 @@ class nsDocShell final : public nsDocLoader, +@@ -1301,6 +1314,17 @@ class nsDocShell final : public nsDocLoader, bool mAllowDNSPrefetch : 1; bool mAllowWindowControl : 1; bool mCSSErrorReportingEnabled : 1; @@ -754,7 +754,7 @@ index f22a333733322ad17f097d7edd46af21a687906c..6bcf8ca9f9cd64dc9f5695d00e0a3e6a bool mAllowKeywordFixup : 1; bool mDisableMetaRefreshWhenInactive : 1; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl -index 84e821e33e8164829dfee4f05340784e189b90ee..aa690eb747cb73bc6bff40a62546037c2e64c485 100644 +index 159daa1dd936e84f1bf3ce413643382e4d37f592..607224a5270995abccf439992ec577f11178e522 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -44,6 +44,7 @@ interface nsIURI; @@ -812,10 +812,10 @@ index 84e821e33e8164829dfee4f05340784e189b90ee..aa690eb747cb73bc6bff40a62546037c * This attempts to save any applicable layout history state (like * scroll position) in the nsISHEntry. This is normally done diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp -index fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c639a74ac 100644 +index a16bef739fcde0f14ba7e53e0acfa3aa2ee1dd3a..c4747478156c973307b7866f84c65520e4bff9d1 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp -@@ -3752,6 +3752,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { +@@ -3818,6 +3818,9 @@ void Document::SendToConsole(nsCOMArray& aMessages) { } void Document::ApplySettingsFromCSP(bool aSpeculative) { @@ -825,7 +825,7 @@ index fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c nsresult rv = NS_OK; if (!aSpeculative) { // 1) apply settings from regular CSP -@@ -3809,6 +3812,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { +@@ -3875,6 +3878,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { MOZ_ASSERT(!mScriptGlobalObject, "CSP must be initialized before mScriptGlobalObject is set!"); @@ -837,7 +837,7 @@ index fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c // If this is a data document - no need to set CSP. if (mLoadedAsData) { return NS_OK; -@@ -4617,6 +4625,10 @@ bool Document::HasFocus(ErrorResult& rv) const { +@@ -4699,6 +4707,10 @@ bool Document::HasFocus(ErrorResult& rv) const { return false; } @@ -848,7 +848,7 @@ index fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c if (!fm->IsInActiveWindow(bc)) { return false; } -@@ -19688,6 +19700,35 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { +@@ -20107,6 +20119,35 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const { return PreferenceSheet::PrefsFor(*this).mColorScheme; } @@ -885,10 +885,10 @@ index fd2d0be5f7755e64fc134515ea138c4ed0d28daf..ae48159ddbfb98d03e538d077a33260c if (!sLoadingForegroundTopLevelContentDocument) { return false; diff --git a/dom/base/Document.h b/dom/base/Document.h -index 622f54e369d324a4cc2800dd4b331bd628339bed..2ef6ed20cf35febeff75b22dfa5bb2fbb4e295fe 100644 +index f9f8089ad484330a9d863fac7559b94aa34d80fd..75b6f3b03a6fe11511cc1e9ebdc6aa16936bbbcb 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h -@@ -4140,6 +4140,8 @@ class Document : public nsINode, +@@ -4224,6 +4224,8 @@ class Document : public nsINode, // color-scheme meta tag. ColorScheme DefaultColorScheme() const; @@ -898,10 +898,10 @@ index 622f54e369d324a4cc2800dd4b331bd628339bed..2ef6ed20cf35febeff75b22dfa5bb2fb static bool AutomaticStorageAccessPermissionCanBeGranted( diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp -index a13cae5b990fb2f750e62f5117ad63aa981d787f..bc0f2d674aadd8eba867f56e873595a8885d1798 100644 +index fac275953573368e91e99bc8a72a885fb1c75521..7c1bcfdba325f8310239fc69921aaa0f14255f33 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp -@@ -344,14 +344,18 @@ void Navigator::GetAppName(nsAString& aAppName) const { +@@ -347,14 +347,18 @@ void Navigator::GetAppName(nsAString& aAppName) const { * for more detail. */ /* static */ @@ -922,7 +922,7 @@ index a13cae5b990fb2f750e62f5117ad63aa981d787f..bc0f2d674aadd8eba867f56e873595a8 // Split values on commas. for (nsDependentSubstring lang : -@@ -403,7 +407,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) { +@@ -406,7 +410,13 @@ void Navigator::GetLanguage(nsAString& aLanguage) { } void Navigator::GetLanguages(nsTArray& aLanguages) { @@ -937,7 +937,7 @@ index a13cae5b990fb2f750e62f5117ad63aa981d787f..bc0f2d674aadd8eba867f56e873595a8 // The returned value is cached by the binding code. The window listens to the // accept languages change and will clear the cache when needed. It has to -@@ -2298,7 +2308,8 @@ bool Navigator::Webdriver() { +@@ -2309,7 +2319,8 @@ bool Navigator::Webdriver() { } #endif @@ -948,10 +948,10 @@ index a13cae5b990fb2f750e62f5117ad63aa981d787f..bc0f2d674aadd8eba867f56e873595a8 AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) { diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h -index 6abf6cef230c97815f17f6b7abf9f1b1de274a6f..46ead1f32e0d710b5b32e61dff72a4f772d5421e 100644 +index 893947475fbb8688becb1c1495385e4048d4927d..dfb50acf689223fdab7ef6f42afbbd53341e1b0b 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h -@@ -218,7 +218,7 @@ class Navigator final : public nsISupports, public nsWrapperCache { +@@ -220,7 +220,7 @@ class Navigator final : public nsISupports, public nsWrapperCache { StorageManager* Storage(); @@ -961,10 +961,10 @@ index 6abf6cef230c97815f17f6b7abf9f1b1de274a6f..46ead1f32e0d710b5b32e61dff72a4f7 dom::MediaCapabilities* MediaCapabilities(); dom::MediaSession* MediaSession(); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp -index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764cb5c68d11 100644 +index 787d4c4e22715a72197e5d06831bd6d284129c2c..75fc73ce2863f994ce703b0f822acb924bee4f3d 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp -@@ -9151,11 +9151,13 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -9432,11 +9432,13 @@ nsresult nsContentUtils::SendMouseEvent( int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized, @@ -979,7 +979,7 @@ index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764c if (aType.EqualsLiteral("mousedown")) { msg = eMouseDown; } else if (aType.EqualsLiteral("mouseup")) { -@@ -9181,6 +9183,12 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -9462,6 +9464,12 @@ nsresult nsContentUtils::SendMouseEvent( msg = eMouseHitTest; } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) { msg = eMouseExploreByTouch; @@ -992,7 +992,7 @@ index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764c } else { return NS_ERROR_FAILURE; } -@@ -9191,7 +9199,14 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -9472,7 +9480,14 @@ nsresult nsContentUtils::SendMouseEvent( Maybe pointerEvent; Maybe mouseEvent; @@ -1008,7 +1008,7 @@ index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764c MOZ_ASSERT(!aIsWidgetEventSynthesized, "The event shouldn't be dispatched as a synthesized event"); if (MOZ_UNLIKELY(aIsWidgetEventSynthesized)) { -@@ -9210,8 +9225,11 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -9491,8 +9506,11 @@ nsresult nsContentUtils::SendMouseEvent( contextMenuKey ? WidgetMouseEvent::eContextMenuKey : WidgetMouseEvent::eNormal); } @@ -1020,7 +1020,7 @@ index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764c mouseOrPointerEvent.pointerId = aIdentifier; mouseOrPointerEvent.mModifiers = GetWidgetModifiers(aModifiers); mouseOrPointerEvent.mButton = aButton; -@@ -9224,6 +9242,8 @@ nsresult nsContentUtils::SendMouseEvent( +@@ -9505,6 +9523,8 @@ nsresult nsContentUtils::SendMouseEvent( mouseOrPointerEvent.mClickCount = aClickCount; mouseOrPointerEvent.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized; mouseOrPointerEvent.mExitFrom = exitFrom; @@ -1030,10 +1030,10 @@ index f362a444a0f5ed247582646754dffd54d0b4540a..bbd72dab7ff4fbac8c247961e530764c nsPresContext* presContext = aPresShell->GetPresContext(); if (!presContext) return NS_ERROR_FAILURE; diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h -index 779cd9e544bfb2d135f12c3558c0ca8164b37029..041eba4bcbf40f51fc40ce7609ea81408e6a788d 100644 +index 7d89626774660fb9e0f564808270e3059e4d7b3c..c7f748e6f33cbcd72b0c97d437b2abbcbe4242be 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h -@@ -3015,8 +3015,9 @@ class nsContentUtils { +@@ -3078,8 +3078,9 @@ class nsContentUtils { int32_t aButton, int32_t aButtons, int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow, @@ -1046,10 +1046,10 @@ index 779cd9e544bfb2d135f12c3558c0ca8164b37029..041eba4bcbf40f51fc40ce7609ea8140 static void FirePageShowEventForFrameLoaderSwap( nsIDocShellTreeItem* aItem, diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp -index 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee204fcabde 100644 +index e85140d5afebf57cf56bf16ef0c43c425c8d50c7..3062737c3319e35cdc4786c06750a1ac2a99565f 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp -@@ -710,6 +710,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) { +@@ -707,6 +707,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) { return NS_ERROR_FAILURE; } @@ -1076,7 +1076,7 @@ index 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee2 NS_IMETHODIMP nsDOMWindowUtils::SendMouseEvent( const nsAString& aType, float aX, float aY, int32_t aButton, -@@ -724,7 +744,7 @@ nsDOMWindowUtils::SendMouseEvent( +@@ -721,7 +741,7 @@ nsDOMWindowUtils::SendMouseEvent( aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, false, aPreventDefault, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true, aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false, @@ -1085,7 +1085,7 @@ index 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee2 } NS_IMETHODIMP -@@ -742,7 +762,7 @@ nsDOMWindowUtils::SendMouseEventToWindow( +@@ -739,7 +759,7 @@ nsDOMWindowUtils::SendMouseEventToWindow( aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, true, nullptr, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true, aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false, @@ -1094,7 +1094,7 @@ index 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee2 } NS_IMETHODIMP -@@ -751,7 +771,7 @@ nsDOMWindowUtils::SendMouseEventCommon( +@@ -748,7 +768,7 @@ nsDOMWindowUtils::SendMouseEventCommon( int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure, unsigned short aInputSourceArg, uint32_t aPointerId, bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized, @@ -1103,7 +1103,7 @@ index 15fe1db8a28ed2592b61aaf2006ddaa656f12389..2642c18bebcdfdd467be557171ba4ee2 RefPtr presShell = GetPresShell(); if (!presShell) { return NS_ERROR_FAILURE; -@@ -768,7 +788,7 @@ nsDOMWindowUtils::SendMouseEventCommon( +@@ -765,7 +785,7 @@ nsDOMWindowUtils::SendMouseEventCommon( presShell, widget, aType, refPoint, aButton, aButtons, aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure, aInputSourceArg, aPointerId, aToWindow, aPreventDefault, aIsDOMEventSynthesized, @@ -1126,10 +1126,10 @@ index a8a48d28fc4ef612f8234bc2490a41672f1704f5..f702b0c9a0783ec547a41bbefd68e18a MOZ_CAN_RUN_SCRIPT nsresult SendTouchEventCommon( diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp -index 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39ac9b42e51 100644 +index be89a1c984982ea005e9bf7f440f5c8a2e8bab55..f8154882b9af02c5e9d7181e7a15b78824be8df9 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp -@@ -1720,6 +1720,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent, +@@ -1719,6 +1719,10 @@ Maybe nsFocusManager::SetFocusInner(Element* aNewContent, (GetActiveBrowsingContext() == newRootBrowsingContext); } @@ -1140,7 +1140,7 @@ index 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39a // Exit fullscreen if a website focuses another window if (StaticPrefs::full_screen_api_exit_on_windowRaise() && !isElementInActiveWindow && (aFlags & FLAG_RAISE)) { -@@ -2306,6 +2310,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear, +@@ -2305,6 +2309,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear, bool aIsLeavingDocument, bool aAdjustWidget, bool aRemainActive, Element* aElementToFocus, uint64_t aActionId) { @@ -1148,7 +1148,7 @@ index 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39a LOGFOCUS(("<>", aActionId)); // hold a reference to the focused content, which may be null -@@ -2352,6 +2357,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear, +@@ -2351,6 +2356,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear, return true; } @@ -1160,7 +1160,7 @@ index 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39a // Keep a ref to presShell since dispatching the DOM event may cause // the document to be destroyed. RefPtr presShell = docShell->GetPresShell(); -@@ -3066,7 +3076,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow, +@@ -3061,7 +3071,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow, } } @@ -1172,10 +1172,10 @@ index 555a08b4b3fcd0d0c7986014d2e3516c6e5991c0..74a39e000b0e68042f1f51f6fafbc39a // care of lowering the present active window. This happens in // a separate runnable to avoid touching multiple windows in diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp -index 99a5049a3aff2efb208895d60622fd9c8d7f337a..9a9b039a46f1294a8b4af0613fb4f4173ac6a8a0 100644 +index 42ee50b53a666c05c6540a1ddcf3745694aaed2d..b15150f7d2e293c2e338f8cd3ada927c052b30b2 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp -@@ -2512,10 +2512,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, +@@ -2511,10 +2511,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument, }(); if (!isContentAboutBlankInChromeDocshell) { @@ -1196,7 +1196,7 @@ index 99a5049a3aff2efb208895d60622fd9c8d7f337a..9a9b039a46f1294a8b4af0613fb4f417 } } -@@ -2635,6 +2641,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() { +@@ -2634,6 +2640,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() { } } @@ -1217,7 +1217,7 @@ index 99a5049a3aff2efb208895d60622fd9c8d7f337a..9a9b039a46f1294a8b4af0613fb4f417 void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) { diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h -index 0453a18ec10c1434d1692f10b1b4acee754e6b7e..ee7bad691515bb51f6b4345e88944b02ad173180 100644 +index a846e57a8786e77e055d17474c5d910a6844cd5f..02815da6a94e98d452e8b4781a998fc0d5ed1124 100644 --- a/dom/base/nsGlobalWindowOuter.h +++ b/dom/base/nsGlobalWindowOuter.h @@ -320,6 +320,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget, @@ -1229,10 +1229,10 @@ index 0453a18ec10c1434d1692f10b1b4acee754e6b7e..ee7bad691515bb51f6b4345e88944b02 // Outer windows only. virtual void EnsureSizeAndPositionUpToDate() override; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp -index 8e2bbed21c13f23745e2eaad4ded831106ebd930..a17b0c7b9730737f178c05703b08d0f5f6d9ecd1 100644 +index 7d5e58f27b2ae07f8e8ac23c6d12bf90d2fdd8b7..2510572d9bfe8ea2909c48d9c3e86aa02c69e28e 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp -@@ -1449,6 +1449,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, +@@ -1498,6 +1498,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions, mozilla::GetBoxQuadsFromWindowOrigin(this, aOptions, aResult, aRv); } @@ -1295,10 +1295,10 @@ index 8e2bbed21c13f23745e2eaad4ded831106ebd930..a17b0c7b9730737f178c05703b08d0f5 DOMQuad& aQuad, const GeometryNode& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h -index eb75f281630f8ca1b901686207c9fc97336d675f..e607f0ae454d52fc2bfe19046b492352a84b4ebd 100644 +index 6a982b3f278bf810dd582b3d5ebc33967323047e..4e991ef317c572c4a79c053d468f14df6c8880b5 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h -@@ -2397,6 +2397,10 @@ class nsINode : public mozilla::dom::EventTarget { +@@ -2417,6 +2417,10 @@ class nsINode : public mozilla::dom::EventTarget { nsTArray>& aResult, ErrorResult& aRv); @@ -1338,10 +1338,10 @@ index f32e21752d5013bf143eb45391ab9218debab08e..83763d2354dade2f8d2b7930ba18ae91 static bool DumpEnabled(); diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl -index 6ec88536141126c97c9b599e3237bb5670d42ce8..41c6cc56738bdaf711adf2cf5b00c7fad5d71ba8 100644 +index 7923fcfb3e70aabddf343ab3ec2f25313bbd227e..cf02ce032d11d85a13bcf91e93e98882eaa9f7c7 100644 --- a/dom/chrome-webidl/BrowsingContext.webidl +++ b/dom/chrome-webidl/BrowsingContext.webidl -@@ -61,6 +61,26 @@ enum ForcedColorsOverride { +@@ -62,6 +62,26 @@ enum ForcedColorsOverride { "active", }; @@ -1368,7 +1368,7 @@ index 6ec88536141126c97c9b599e3237bb5670d42ce8..41c6cc56738bdaf711adf2cf5b00c7fa /** * Allowed overrides of platform/pref default behaviour for touch events. */ -@@ -220,6 +240,12 @@ interface BrowsingContext { +@@ -226,6 +246,12 @@ interface BrowsingContext { // Forced-colors simulation, for DevTools [SetterThrows] attribute ForcedColorsOverride forcedColorsOverride; @@ -1382,10 +1382,10 @@ index 6ec88536141126c97c9b599e3237bb5670d42ce8..41c6cc56738bdaf711adf2cf5b00c7fa * A unique identifier for the browser element that is hosting this * BrowsingContext tree. Every BrowsingContext in the element's tree will diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp -index 0bd219694282347309680fc9b53b945e1fd0ad92..c5c2e2d32a380ec72379b05a8b84f187431f7309 100644 +index 2a29279a6d74770a2ec5cee80891bff9eadf1d13..bce59065615a33cf020aae4b20750ce8b1be66ce 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp -@@ -701,6 +701,12 @@ already_AddRefed FetchRequest(nsIGlobalObject* aGlobal, +@@ -702,6 +702,12 @@ already_AddRefed FetchRequest(nsIGlobalObject* aGlobal, ipcArgs.hasCSPEventListener() = false; ipcArgs.isWorkerRequest() = false; @@ -1399,7 +1399,7 @@ index 0bd219694282347309680fc9b53b945e1fd0ad92..c5c2e2d32a380ec72379b05a8b84f187 mozilla::glean::networking::fetch_keepalive_request_count.Get("main"_ns) diff --git a/dom/fetch/FetchService.cpp b/dom/fetch/FetchService.cpp -index b5e60bbd27fbb2f033d233e9ae2ebc728f442512..0adc568ece34d2c0f35eeacd81e2db9125b7c327 100644 +index fcdddf2f772af305c68cc169ba891386a1dba982..d8b19d35719fff3f9c55c199718f4dc1d15cdfe9 100644 --- a/dom/fetch/FetchService.cpp +++ b/dom/fetch/FetchService.cpp @@ -268,6 +268,14 @@ RefPtr FetchService::FetchInstance::Fetch() { @@ -1417,110 +1417,11 @@ index b5e60bbd27fbb2f033d233e9ae2ebc728f442512..0adc568ece34d2c0f35eeacd81e2db91 if (mArgsType == FetchArgsType::WorkerFetch) { auto& args = mArgs.as(); mFetchDriver->SetWorkerScript(args.mWorkerScript); -diff --git a/dom/geolocation/Geolocation.cpp b/dom/geolocation/Geolocation.cpp -index 7c653fe131dc34d35ffdc030950071adb31a9fc9..b23442a42ba8ee270e8e38930e59ae15c2a29039 100644 ---- a/dom/geolocation/Geolocation.cpp -+++ b/dom/geolocation/Geolocation.cpp -@@ -28,6 +28,7 @@ - #include "nsComponentManagerUtils.h" - #include "nsContentPermissionHelper.h" - #include "nsContentUtils.h" -+#include "nsDocShell.h" - #include "nsGlobalWindowInner.h" - #include "mozilla/dom/Document.h" - #include "nsINamed.h" -@@ -428,10 +429,8 @@ nsGeolocationRequest::Allow(JS::Handle aChoices) { - return NS_OK; - } - -- RefPtr gs = -- nsGeolocationService::GetGeolocationService(); -- -- bool canUseCache = false; -+ nsGeolocationService* gs = mLocator->GetGeolocationService(); -+ bool canUseCache = gs != nsGeolocationService::sService.get(); - CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition(); - if (lastPosition.position) { - EpochTimeStamp cachedPositionTime_ms; -@@ -639,8 +638,7 @@ void nsGeolocationRequest::Shutdown() { - // If there are no other high accuracy requests, the geolocation service will - // notify the provider to switch to the default accuracy. - if (mOptions && mOptions->mEnableHighAccuracy) { -- RefPtr gs = -- nsGeolocationService::GetGeolocationService(); -+ nsGeolocationService* gs = mLocator ? mLocator->GetGeolocationService() : nullptr; - if (gs) { - gs->UpdateAccuracy(); - } -@@ -957,8 +955,14 @@ void nsGeolocationService::StopDevice() { - StaticRefPtr nsGeolocationService::sService; - - already_AddRefed --nsGeolocationService::GetGeolocationService() { -+nsGeolocationService::GetGeolocationService(nsDocShell* docShell) { - RefPtr result; -+ if (docShell) { -+ result = docShell->GetGeolocationServiceOverride(); -+ if (result) -+ return result.forget(); -+ } -+ - if (nsGeolocationService::sService) { - result = nsGeolocationService::sService; - -@@ -1050,7 +1054,9 @@ nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) { - // If no aContentDom was passed into us, we are being used - // by chrome/c++ and have no mOwner, no mPrincipal, and no need - // to prompt. -- mService = nsGeolocationService::GetGeolocationService(); -+ nsCOMPtr doc = aContentDom ? aContentDom->GetDoc() : nullptr; -+ mService = nsGeolocationService::GetGeolocationService( -+ doc ? static_cast(doc->GetDocShell()) : nullptr); - if (mService) { - mService->AddLocator(this); - } -diff --git a/dom/geolocation/Geolocation.h b/dom/geolocation/Geolocation.h -index 992de29b5d2d09c19e55ebb2502215ec9d05a171..cdc20567b693283b0fd5a5923f7ea54210bd1712 100644 ---- a/dom/geolocation/Geolocation.h -+++ b/dom/geolocation/Geolocation.h -@@ -31,6 +31,7 @@ - - #include "nsIGeolocationProvider.h" - #include "mozilla/Attributes.h" -+#include "nsDocShell.h" - - class nsGeolocationService; - class nsGeolocationRequest; -@@ -51,13 +52,14 @@ struct CachedPositionAndAccuracy { - bool isHighAccuracy; - }; - -+ - /** - * Singleton that manages the geolocation provider - */ - class nsGeolocationService final : public nsIGeolocationUpdate, - public nsIObserver { - public: -- static already_AddRefed GetGeolocationService(); -+ static already_AddRefed GetGeolocationService(nsDocShell* docShell = nullptr); - static mozilla::StaticRefPtr sService; - - NS_DECL_THREADSAFE_ISUPPORTS -@@ -189,6 +191,8 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache { - BrowsingContext* aBrowsingContext, - geolocation::ParentRequestResolver&& aResolver); - -+ nsGeolocationService* GetGeolocationService() { return mService; }; -+ - private: - ~Geolocation(); - diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp -index c3e7de8f41e06e11155620b75c4d8a830d908b37..39eb9d31258693dce3a26c3227f28d92bccdb019 100644 +index 9382c73dc527f3fb676e8ea3457a30eee2d9415f..f1785121db1de071ce5aa1a0ad38d3d603c486f7 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp -@@ -64,6 +64,7 @@ +@@ -63,6 +63,7 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLDataListElement.h" #include "mozilla/dom/HTMLOptionElement.h" @@ -1528,7 +1429,7 @@ index c3e7de8f41e06e11155620b75c4d8a830d908b37..39eb9d31258693dce3a26c3227f28d92 #include "nsIFrame.h" #include "nsRangeFrame.h" #include "nsError.h" -@@ -790,6 +791,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { +@@ -816,6 +817,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) { return NS_ERROR_FAILURE; } @@ -1543,7 +1444,7 @@ index c3e7de8f41e06e11155620b75c4d8a830d908b37..39eb9d31258693dce3a26c3227f28d92 return NS_OK; } diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl -index 5e417145c4f21d8f2aa65088611477b681c9c327..bc84c509659c7556077e69c652e5b19639eb88bb 100644 +index 4cdfcaafa779ed402d02411f69c02ab0eb5b4e09..e69c837f7ec340a11e8ae9485cd5b714a3ea9a88 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -374,6 +374,26 @@ interface nsIDOMWindowUtils : nsISupports { @@ -1574,10 +1475,10 @@ index 5e417145c4f21d8f2aa65088611477b681c9c327..bc84c509659c7556077e69c652e5b196 * touchstart, touchend, touchmove, and touchcancel * diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp -index 93f20a36acef74947d5377df5ff916546218d8b8..22b706b985d22a8c0c278a12ab4944e26ded51a4 100644 +index 310e22a130612c055f7642d813b2bc06538c8797..8528cd75252c6cf0bebe42ffbf83c0220551ba28 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp -@@ -1676,6 +1676,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, +@@ -1760,6 +1760,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, if (postLayerization) { postLayerization->Register(); } @@ -1600,24 +1501,24 @@ index 93f20a36acef74947d5377df5ff916546218d8b8..22b706b985d22a8c0c278a12ab4944e2 mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent( diff --git a/dom/ipc/CoalescedMouseData.cpp b/dom/ipc/CoalescedMouseData.cpp -index 5aa445d2e0a6169e57c44569974d557b3baf7064..671f71979b407f0ca17c66f13805e851ba30479e 100644 +index 31d56e5d4ea1f21b4b827c763d6e4b34432eeb7d..8537ae198faeec5fd5cced71995bbce9dccf90c4 100644 --- a/dom/ipc/CoalescedMouseData.cpp +++ b/dom/ipc/CoalescedMouseData.cpp -@@ -67,6 +67,9 @@ bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent, - mCoalescedInputEvent->pointerId == aEvent.pointerId && - mCoalescedInputEvent->mButton == aEvent.mButton && - mCoalescedInputEvent->mButtons == aEvent.mButtons && mGuid == aGuid && -+ // `mJugglerEventId` is 0 for non-juggler events and a unique number for -+ // juggler-emitted events. -+ mCoalescedInputEvent->mJugglerEventId == aEvent.mJugglerEventId && - mInputBlockId == aInputBlockId); - } - +@@ -71,6 +71,9 @@ bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent, + mCoalescedInputEvent->pointerId != aEvent.pointerId || + mCoalescedInputEvent->mButton != aEvent.mButton || + mCoalescedInputEvent->mButtons != aEvent.mButtons || mGuid != aGuid || ++ // `mJugglerEventId` is 0 for non-juggler events and a unique number for ++ // juggler-emitted events. ++ mCoalescedInputEvent->mJugglerEventId != aEvent.mJugglerEventId || + mInputBlockId != aInputBlockId) { + return false; + } diff --git a/dom/media/systemservices/video_engine/desktop_capture_impl.cc b/dom/media/systemservices/video_engine/desktop_capture_impl.cc -index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb242b9a4e 100644 +index db777994b583efc2a3533e7d4534fff894c733bb..eb317c19b2422bd983d5e132c9ee4dac31687428 100644 --- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc +++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc -@@ -52,9 +52,10 @@ namespace webrtc { +@@ -53,9 +53,10 @@ namespace webrtc { DesktopCaptureImpl* DesktopCaptureImpl::Create(const int32_t aModuleId, const char* aUniqueId, @@ -1630,7 +1531,7 @@ index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb } static DesktopCaptureOptions CreateDesktopCaptureOptions() { -@@ -155,8 +156,10 @@ static std::unique_ptr CreateTabCapturer( +@@ -156,8 +157,10 @@ static std::unique_ptr CreateTabCapturer( static std::unique_ptr CreateDesktopCapturerAndThread( CaptureDeviceType aDeviceType, DesktopCapturer::SourceId aSourceId, @@ -1642,7 +1543,7 @@ index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb auto ensureThread = [&]() { if (*aOutThread) { return *aOutThread; -@@ -253,7 +256,8 @@ static std::unique_ptr CreateDesktopCapturerAndThread( +@@ -254,7 +257,8 @@ static std::unique_ptr CreateDesktopCapturerAndThread( } DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, @@ -1652,7 +1553,7 @@ index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb : mModuleId(aId), mTrackingId(mozilla::TrackingId(CaptureEngineToTrackingSourceStr([&] { switch (aType) { -@@ -270,6 +274,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, +@@ -271,6 +275,7 @@ DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId, aId)), mDeviceUniqueId(aUniqueId), mDeviceType(aType), @@ -1660,7 +1561,7 @@ index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb mControlThread(mozilla::GetCurrentSerialEventTarget()), mNextFrameMinimumTime(Timestamp::Zero()), mCallbacks("DesktopCaptureImpl::mCallbacks") {} -@@ -294,6 +299,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback( +@@ -295,6 +300,19 @@ void DesktopCaptureImpl::DeRegisterCaptureDataCallback( } } @@ -1680,16 +1581,16 @@ index c43a1b3b245101c974742c5e50f54857e538bbfb..c07a568da3342cbf2af07471fa6959cb int32_t DesktopCaptureImpl::StopCaptureIfAllClientsClose() { { auto callbacks = mCallbacks.Lock(); -@@ -333,7 +351,7 @@ int32_t DesktopCaptureImpl::StartCapture( - - DesktopCapturer::SourceId sourceId = std::stoi(mDeviceUniqueId); +@@ -350,7 +368,7 @@ int32_t DesktopCaptureImpl::StartCapture( + return -1; + } std::unique_ptr capturer = CreateDesktopCapturerAndThread( - mDeviceType, sourceId, getter_AddRefs(mCaptureThread)); + mDeviceType, sourceId, getter_AddRefs(mCaptureThread), capture_cursor_); MOZ_ASSERT(!capturer == !mCaptureThread); if (!capturer) { -@@ -441,6 +459,15 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult, +@@ -458,6 +476,15 @@ void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult, frameInfo.height = aFrame->size().height(); frameInfo.videoType = VideoType::kARGB; @@ -1803,7 +1704,7 @@ index a76b7de569db1cb42728a5514fb80e5c46e0344e..3d61ad8d3aa4a7eaf96957dcd680e1e1 const nsCOMPtr mControlThread; // Set in StartCapture. diff --git a/dom/script/ScriptSettings.cpp b/dom/script/ScriptSettings.cpp -index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc9b5e445f 100644 +index d5964b27e9c99af76fe3f4076322592a9370c2d0..da217e2c1fbd9ca02e50ff74dde028189a216a6d 100644 --- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -150,6 +150,30 @@ ScriptSettingsStackEntry::~ScriptSettingsStackEntry() { @@ -1847,10 +1748,10 @@ index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc return aGlobalOrNull; diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp -index 5ec21c1c7f975a372399748e8bab2b21ce347f20..ed16831e549afa3d6623398d35eb61e26ab5f2b0 100644 +index 40844c900fc64415e39f4d2dead4c490d5ec301b..f63d0fd6a6df7ea932ca4d4bb70dfbd7562b2fa0 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp -@@ -23,6 +23,7 @@ +@@ -24,6 +24,7 @@ #include "nsSandboxFlags.h" #include "nsServiceManagerUtils.h" #include "nsWhitespaceTokenizer.h" @@ -1894,10 +1795,10 @@ index aee376e971ae01ac1e512c3920b115bfaf06afa8..1701311534bf77e6cd9bafc0e3a28361 * returned quads are further translated relative to the window * origin -- which is not the layout origin. Further translation diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp -index a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49d94325f8 100644 +index 76035deec8f20933800284211a07ff23e124c1b9..d46c22728c82c15ed6b5c9d3c57f8b8e2229212b 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp -@@ -1026,7 +1026,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { +@@ -1079,7 +1079,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) { AssertIsOnMainThread(); nsTArray languages; @@ -1906,7 +1807,7 @@ index a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49 RuntimeService* runtime = RuntimeService::GetService(); if (runtime) { -@@ -1214,8 +1214,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { +@@ -1269,8 +1269,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) { } // The navigator overridden properties should have already been read. @@ -1916,7 +1817,7 @@ index a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49 mNavigatorPropertiesLoaded = true; } -@@ -1836,6 +1835,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( +@@ -1898,6 +1897,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted( } } @@ -1930,7 +1831,7 @@ index a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49 template void RuntimeService::BroadcastAllWorkers(const Func& aFunc) { AssertIsOnMainThread(); -@@ -2361,6 +2367,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( +@@ -2438,6 +2444,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers( } } @@ -1946,7 +1847,7 @@ index a23637c4a887b66a1b4c709a648762b84151bf01..d8da9063261482f1da3257e3f95a8a49 MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aCx); diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h -index 534bbe9ec4f0261189eb3322c1229c1eb5d8802e..6aa99b64fdbbff3704602e944b129879fbdf8c15 100644 +index b17efeafac1df3653ed9659e506c3d582044e2d0..3dc739c3b72980daf8a99190123920d0096da00c 100644 --- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h @@ -112,6 +112,8 @@ class RuntimeService final : public nsIObserver { @@ -1959,7 +1860,7 @@ index 534bbe9ec4f0261189eb3322c1229c1eb5d8802e..6aa99b64fdbbff3704602e944b129879 return mNavigatorProperties; } diff --git a/dom/workers/WorkerCommon.h b/dom/workers/WorkerCommon.h -index 58894a8361c7ef1dddd481ca5877a209a8b8ff5c..c481d40d79b6397b7f1d571bd9f6ae5c0a946217 100644 +index c928f8292a29ce7b53dd45aec4abc107263b1c8f..e11fdf6159623b2232e51160efbcbb6df5b6ed89 100644 --- a/dom/workers/WorkerCommon.h +++ b/dom/workers/WorkerCommon.h @@ -47,6 +47,8 @@ void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow); @@ -1972,10 +1873,10 @@ index 58894a8361c7ef1dddd481ca5877a209a8b8ff5c..c481d40d79b6397b7f1d571bd9f6ae5c bool IsWorkerGlobal(JSObject* global); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp -index 5d918a82708a26125f7322e43f6436d7eafaa812..b230baead02e05d87a211c276066ec7939ea8251 100644 +index e19f398e2672a6ffe07f53c820dd53c3210dd8b1..8dba789845e910986d3240f317a8749a37cd50b6 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp -@@ -736,6 +736,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { +@@ -755,6 +755,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable { } }; @@ -1994,7 +1895,7 @@ index 5d918a82708a26125f7322e43f6436d7eafaa812..b230baead02e05d87a211c276066ec79 class UpdateLanguagesRunnable final : public WorkerThreadRunnable { nsTArray mLanguages; -@@ -2159,6 +2171,16 @@ void WorkerPrivate::UpdateContextOptions( +@@ -2401,6 +2413,16 @@ void WorkerPrivate::UpdateContextOptions( } } @@ -2011,7 +1912,7 @@ index 5d918a82708a26125f7322e43f6436d7eafaa812..b230baead02e05d87a211c276066ec79 void WorkerPrivate::UpdateLanguages(const nsTArray& aLanguages) { AssertIsOnParentThread(); -@@ -5946,6 +5968,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( +@@ -6374,6 +6396,15 @@ void WorkerPrivate::UpdateContextOptionsInternal( } } @@ -2028,10 +1929,10 @@ index 5d918a82708a26125f7322e43f6436d7eafaa812..b230baead02e05d87a211c276066ec79 const nsTArray& aLanguages) { WorkerGlobalScope* globalScope = GlobalScope(); diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h -index 7bccdc8c0c2cd53f7aa7a6d9a74435344dd27980..86bba2128a7c0f4e5efa4bfbc939937aec146695 100644 +index d887a9ca510730aef61d71c6f8b2e58c3762e5df..7ffe86c8ec792558e3d83e5b7ca8fccd39147385 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h -@@ -443,6 +443,8 @@ class WorkerPrivate final +@@ -453,6 +453,8 @@ class WorkerPrivate final void UpdateContextOptionsInternal(JSContext* aCx, const JS::ContextOptions& aContextOptions); @@ -2040,7 +1941,7 @@ index 7bccdc8c0c2cd53f7aa7a6d9a74435344dd27980..86bba2128a7c0f4e5efa4bfbc939937a void UpdateLanguagesInternal(const nsTArray& aLanguages); void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, -@@ -1091,6 +1093,8 @@ class WorkerPrivate final +@@ -1099,6 +1101,8 @@ class WorkerPrivate final void UpdateContextOptions(const JS::ContextOptions& aContextOptions); @@ -2102,10 +2003,10 @@ index 523e84c8c93f4221701f90f2e8ee146ec8e1adbd..98d5b1176e5378431b859a2dbd4d4e77 inline ClippedTime TimeClip(double time); diff --git a/js/src/debugger/Object.cpp b/js/src/debugger/Object.cpp -index b7ea4b6f66d14db0324397cdc1b0ed8c5ea167e2..1c59e328079e7e43b65f7cb7bc31636a48a93263 100644 +index 2d177b5aade6d158e9ae62d317d5155b72cdcf84..28af5d41eb06d4b33cd5f86484dc29c923308835 100644 --- a/js/src/debugger/Object.cpp +++ b/js/src/debugger/Object.cpp -@@ -2484,7 +2484,11 @@ Maybe DebuggerObject::call(JSContext* cx, +@@ -2516,7 +2516,11 @@ Maybe DebuggerObject::call(JSContext* cx, invokeArgs[i].set(args2[i]); } @@ -2273,10 +2174,10 @@ index 4bfd336ddcbee8004ac538ca7b7d8216d04a61c3..cd22351c4aeacea8afc9828972222aca // No boxes to return return; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp -index e8fb3a8304b27814e6e84355f24410c820667f9d..211c86fe55b8b650e40275a427b30a1ee8a9a3d7 100644 +index 1922382f924e74dde81501540d38699dcf3d9e57..c05b9ed071a9a4c99e2afb28eef0dba376777976 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp -@@ -11512,7 +11512,9 @@ bool PresShell::ComputeActiveness() const { +@@ -11881,7 +11881,9 @@ bool PresShell::ComputeActiveness() const { if (!browserChild->IsVisible()) { MOZ_LOG(gLog, LogLevel::Debug, (" > BrowserChild %p is not visible", browserChild)); @@ -2288,7 +2189,7 @@ index e8fb3a8304b27814e6e84355f24410c820667f9d..211c86fe55b8b650e40275a427b30a1e // If the browser is visible but just due to be preserving layers diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp -index 315d532eab56dab13b6c8bc2380a5cda2a17ffc1..7c4552137149e8c7fc9ac08a09bd4242952b53b6 100644 +index bc5b7ab177070178d1a3c49b563df0f04860e7d3..393f694c766bec5e5443d91922a8bc3524ade676 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -708,6 +708,10 @@ bool nsLayoutUtils::AllowZoomingForDocument( @@ -2302,7 +2203,7 @@ index 315d532eab56dab13b6c8bc2380a5cda2a17ffc1..7c4552137149e8c7fc9ac08a09bd4242 // True if we allow zooming for all documents on this platform, or if we are // in RDM. BrowsingContext* bc = aDocument->GetBrowsingContext(); -@@ -9770,6 +9774,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, +@@ -9773,6 +9777,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, /* static */ bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) { @@ -2313,10 +2214,10 @@ index 315d532eab56dab13b6c8bc2380a5cda2a17ffc1..7c4552137149e8c7fc9ac08a09bd4242 return StaticPrefs::dom_meta_viewport_enabled() || (bc && bc->InRDMPane()); } diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h -index c7cf59c2661c7e203384c9b82789879f756b44b7..21e32dab4e60112c073bdd5070a308da2b4e0373 100644 +index 4cdceba041ddc5af98982d27b6e622a0dd5d0e34..50f644a91923e529ff06a164256f5704e5c4d8ae 100644 --- a/layout/style/GeckoBindings.h +++ b/layout/style/GeckoBindings.h -@@ -596,6 +596,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*); +@@ -594,6 +594,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*); bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*); bool Gecko_MediaFeatures_PrefersReducedTransparency( const mozilla::dom::Document*); @@ -2325,10 +2226,10 @@ index c7cf59c2661c7e203384c9b82789879f756b44b7..21e32dab4e60112c073bdd5070a308da const mozilla::dom::Document*); mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp -index ca382a3cfba8ce5839890d6e4cb3cf9789287e3b..5800fc23dc77ee5764beddd6fa48a7fd701d2939 100644 +index d843c5952e273c64703af6e9f2528798e3ada307..50dcfd6839777dd0ed9f806dfac3e1a4b0102efa 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp -@@ -264,11 +264,7 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) { +@@ -268,11 +268,7 @@ bool Gecko_MediaFeatures_MatchesPlatform(StylePlatform aPlatform) { } bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) { @@ -2341,7 +2242,7 @@ index ca382a3cfba8ce5839890d6e4cb3cf9789287e3b..5800fc23dc77ee5764beddd6fa48a7fd } bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) { -@@ -293,6 +289,20 @@ StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( +@@ -297,6 +293,20 @@ StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( // as a signal. StylePrefersContrast Gecko_MediaFeatures_PrefersContrast( const Document* aDocument) { @@ -2363,10 +2264,10 @@ index ca382a3cfba8ce5839890d6e4cb3cf9789287e3b..5800fc23dc77ee5764beddd6fa48a7fd return StylePrefersContrast::NoPreference; } diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp -index 1ec2c64193206d31702e22e5c4783f084b1cff31..fb463eb12ee39cd1e448369f3b47fbcfbb2473b9 100644 +index c9d6cace8de545779ae9c4630973c5a920cd52c6..032a42b732179a951181f390199c2520fecd742b 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp -@@ -696,7 +696,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) +@@ -792,7 +792,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) rhs.mHasInjectedCookieForCookieBannerHandling), mSchemelessInput(rhs.mSchemelessInput), mHttpsUpgradeTelemetry(rhs.mHttpsUpgradeTelemetry), @@ -2376,8 +2277,8 @@ index 1ec2c64193206d31702e22e5c4783f084b1cff31..fb463eb12ee39cd1e448369f3b47fbcf } LoadInfo::LoadInfo( -@@ -2534,4 +2535,16 @@ LoadInfo::SetSkipHTTPSUpgrade(bool aSkipHTTPSUpgrade) { - return NS_OK; +@@ -2705,4 +2706,16 @@ void LoadInfo::UpdateParentAddressSpaceInfo() { + } } +NS_IMETHODIMP @@ -2394,23 +2295,23 @@ index 1ec2c64193206d31702e22e5c4783f084b1cff31..fb463eb12ee39cd1e448369f3b47fbcf + } // namespace mozilla::net diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h -index 93cc8d3630f7029303240555ae72d41b68047375..8a09863af399e25ba3f01caff2f6b3af1260e8b8 100644 +index 7946dacf862aa13da58c65aba3c72deef575dbba..eb307b58b5fda79fa44e5df94f1bcb4dfaa0a8b6 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h -@@ -426,6 +426,8 @@ class LoadInfo final : public nsILoadInfo { +@@ -448,6 +448,8 @@ class LoadInfo final : public nsILoadInfo { bool mIsNewWindowTarget = false; bool mSkipHTTPSUpgrade = false; + + uint64_t mJugglerLoadIdentifier = 0; }; - // This is exposed solely for testing purposes and should not be used outside of + // LoadInfo diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp -index d1650595f8cf28a704f94a99c1f6bfe1deb9cc77..2072a3990ff6f4496626dcebb277291ad845cac3 100644 +index dd8cc849fb3dd56d6e979cc3cbff96727f702d90..dd2d543977cd9543494c52e862552831d955fdb2 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp -@@ -950,5 +950,15 @@ TRRLoadInfo::GetFetchDestination(nsACString& aDestination) { +@@ -973,5 +973,15 @@ TRRLoadInfo::GetFetchDestination(nsACString& aDestination) { return NS_ERROR_NOT_IMPLEMENTED; } @@ -2427,10 +2328,10 @@ index d1650595f8cf28a704f94a99c1f6bfe1deb9cc77..2072a3990ff6f4496626dcebb277291a } // namespace net } // namespace mozilla diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl -index 774ec045c0b18310e8cb86e8a9d6b1788d028435..cbf303a0ed872c27d580b4b6615f3dd9c76a8a19 100644 +index 1cfda408c33c16c75a74ea839ae3bde6142ac92b..b3951bc2df4d448e6c3b7e51bef2c0050e97a35d 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl -@@ -1626,4 +1626,6 @@ interface nsILoadInfo : nsISupports +@@ -1650,4 +1650,6 @@ interface nsILoadInfo : nsISupports return static_cast(userNavigationInvolvement); } %} @@ -2459,10 +2360,10 @@ index 7f91d2df6f8bb4020c75c132dc8f6bf26625fa1e..aaa5541a17039d6b13ad83ab176fdaaf * Set the status and reason for the forthcoming synthesized response. * Multiple calls overwrite existing values. diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp -index 771ae1fbe3d54aa25443eea675cf3abd26a21ce9..a916cb49c16dc6c7809ccbb7c8d4172446a5ac07 100644 +index c486793f79e2c8c248e25f7963ba4e2c08f553d2..f1e625c59ec79c1104fe9594dbf86d39f3293438 100644 --- a/netwerk/ipc/DocumentLoadListener.cpp +++ b/netwerk/ipc/DocumentLoadListener.cpp -@@ -177,6 +177,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, +@@ -178,6 +178,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext, loadInfo->SetTextDirectiveUserActivation( aLoadState->GetTextDirectiveUserActivation()); loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh()); @@ -2516,7 +2417,7 @@ index fbf4bdf1e24d1102df113984be6c8dc3a7d0d810..787bf014d3bf0b8537f99bf5eb4074e1 + // As they are intercepted by Playwright, they don't have + // serviceWorkerTainting as well. + // Thus these asserts are wrong for Playwright world. -+ // Note: these checks were added in https://github.com/mozilla/gecko-dev/commit/92e2cdde79c11510c3e4192e1b6264d00398ed95 ++ // Note: these checks were added in https://github.com/mozilla-firefox/firefox/commit/bb16ca6496682c3b0ddd452d0dd4c1dd46ff71f8 + /* MOZ_ASSERT_IF(!mLoadInfo->GetServiceWorkerTaintingSynthesized(), mLoadInfo->GetLoadingPrincipal()); @@ -2544,7 +2445,7 @@ index 704404c9f094640ad63b685d64bd5a396e733e4b..92bdc21b4d6a015cc2f2bb22781ec675 * InterceptionTimeStamps is used to record the time stamps of the * interception. diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp -index e4479400a4c574f652befbee7d83bd664aa2b840..2761dce9477f7f1622a82fe1da6472597ae82534 100644 +index af4c6482ad41ad67f41b93183c94d6f341fb4989..0a18827e53760b4132381fd0aff286ee69c460e7 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -688,11 +688,9 @@ nsresult nsHttpChannel::OnBeforeConnect() { @@ -2612,7 +2513,7 @@ index e4479400a4c574f652befbee7d83bd664aa2b840..2761dce9477f7f1622a82fe1da647259 return; } -@@ -4214,9 +4206,6 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) { +@@ -4198,9 +4190,6 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) { uint32_t cacheEntryOpenFlags; bool offline = gIOService->IsOffline(); @@ -2622,7 +2523,7 @@ index e4479400a4c574f652befbee7d83bd664aa2b840..2761dce9477f7f1622a82fe1da647259 bool maybeRCWN = false; nsAutoCString cacheControlRequestHeader; -@@ -4227,7 +4216,7 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) { +@@ -4211,7 +4200,7 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) { return NS_OK; } @@ -2631,7 +2532,7 @@ index e4479400a4c574f652befbee7d83bd664aa2b840..2761dce9477f7f1622a82fe1da647259 if (offline || (mLoadFlags & INHIBIT_CACHING) || forceOffline) { if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()) && !offline && !forceOffline) { -@@ -7300,6 +7289,20 @@ void nsHttpChannel::MaybeStartDNSPrefetch() { +@@ -7315,6 +7304,20 @@ void nsHttpChannel::MaybeStartDNSPrefetch() { } } @@ -2653,10 +2554,10 @@ index e4479400a4c574f652befbee7d83bd664aa2b840..2761dce9477f7f1622a82fe1da647259 nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) { if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) { diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h -index cb8b8b7406411edceb30aa53c9b9007a38058f84..ebdc5384ca20feda399b70532a3036174f1a7431 100644 +index fb16e050efea8d49cd315203bfc6a0779f34bb02..c7b5a9894cb993bd420a5407c1aefb4aacd35595 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h -@@ -307,6 +307,10 @@ class nsHttpChannel final : public HttpBaseChannel, +@@ -303,6 +303,10 @@ class nsHttpChannel final : public HttpBaseChannel, void MaybeResolveProxyAndBeginConnect(); void MaybeStartDNSPrefetch(); @@ -2668,10 +2569,10 @@ index cb8b8b7406411edceb30aa53c9b9007a38058f84..ebdc5384ca20feda399b70532a303617 // end server host name. ProxyDNSStrategy GetProxyDNSStrategy(); diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp -index d3b44cc62d3df49bbf842356cbdb153c82c3163c..23cf9bc83fb1faaf1c7406331b78e522b307cbf0 100644 +index 324d71a1567ef2acbd5e9a008961e39a11445419..e76d57612f318b7ad325196684e2e57660008a94 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp -@@ -1349,6 +1349,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta( +@@ -1359,6 +1359,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta( void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -2683,80 +2584,20 @@ index d3b44cc62d3df49bbf842356cbdb153c82c3163c..23cf9bc83fb1faaf1c7406331b78e522 nsCOMPtr preloadCsp = mDocument->GetPreloadCsp(); if (!preloadCsp) { diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp -index 8413eb5916f1f857e18972a14292d14f32684aee..66a3c7b01fdc56c29d789ff786aa91d8b0f02cd6 100644 +index 0ecf2c0df3aad52e004a69b4c7a22fea7532b4d8..f5982c2cf1d6ea0522ac7f7e1fb5b4ab8fa07cb4 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp -@@ -433,7 +433,12 @@ nsCertOverrideService::HasMatchingOverride( - bool disableAllSecurityCheck = false; - { - MutexAutoLock lock(mMutex); -- disableAllSecurityCheck = mDisableAllSecurityCheck; -+ if (aOriginAttributes.mUserContextId) { -+ disableAllSecurityCheck = mUserContextIdsWithDisabledSecurityChecks.has( -+ aOriginAttributes.mUserContextId); -+ } else { -+ disableAllSecurityCheck = mDisableAllSecurityCheck; -+ } - } - if (disableAllSecurityCheck) { - *aIsTemporary = false; -@@ -645,14 +650,24 @@ static bool IsDebugger() { - - NS_IMETHODIMP - nsCertOverrideService:: -- SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable) { -- if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) { -+ SetDisableAllSecurityChecksAndLetAttackersInterceptMyData( -+ bool aDisable, uint32_t aUserContextId) { -+ if (false /* juggler hacks */ && !(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) { - return NS_ERROR_NOT_AVAILABLE; - } - - { - MutexAutoLock lock(mMutex); -- mDisableAllSecurityCheck = aDisable; -+ if (aUserContextId) { -+ if (aDisable) { -+ mozilla::Unused << mUserContextIdsWithDisabledSecurityChecks.put(aUserContextId); -+ } else { -+ mUserContextIdsWithDisabledSecurityChecks.remove(aUserContextId); -+ } -+ return NS_OK; -+ } else { -+ mDisableAllSecurityCheck = aDisable; -+ } - } - - nsCOMPtr nss(do_GetService(PSM_COMPONENT_CONTRACTID)); -diff --git a/security/manager/ssl/nsCertOverrideService.h b/security/manager/ssl/nsCertOverrideService.h -index 21cff56300db6490cf9649aa62099cb5525749b3..ce9a7fc16c2d5980be166e0f4ab9a25df300ca2f 100644 ---- a/security/manager/ssl/nsCertOverrideService.h -+++ b/security/manager/ssl/nsCertOverrideService.h -@@ -118,6 +118,7 @@ class nsCertOverrideService final : public nsICertOverrideService, - - mozilla::Mutex mMutex; - bool mDisableAllSecurityCheck MOZ_GUARDED_BY(mMutex); -+ mozilla::HashSet mUserContextIdsWithDisabledSecurityChecks MOZ_GUARDED_BY(mMutex); - nsCOMPtr mSettingsFile MOZ_GUARDED_BY(mMutex); - nsTHashtable mSettingsTable MOZ_GUARDED_BY(mMutex); - -diff --git a/security/manager/ssl/nsICertOverrideService.idl b/security/manager/ssl/nsICertOverrideService.idl -index 6dfd07d6b676a99993408921de8dea9d561f201d..e3c6794363cd6336effbeac83a179f3796dd71b0 100644 ---- a/security/manager/ssl/nsICertOverrideService.idl -+++ b/security/manager/ssl/nsICertOverrideService.idl -@@ -137,7 +137,9 @@ interface nsICertOverrideService : nsISupports { - * @param aDisable If true, disable all security check and make - * hasMatchingOverride always return true. - */ -- void setDisableAllSecurityChecksAndLetAttackersInterceptMyData(in boolean aDisable); -+ void setDisableAllSecurityChecksAndLetAttackersInterceptMyData( -+ in boolean aDisable, -+ [optional] in uint32_t aUserContextId); +@@ -627,6 +627,8 @@ void nsCertOverrideService::CountPermanentOverrideTelemetry( + } - readonly attribute boolean securityCheckDisabled; - }; + static bool IsDebugger() { ++ // In playwright world, this is always enabled. ++ if (1 == 1) return true; + #ifdef ENABLE_WEBDRIVER + nsCOMPtr marionette = do_GetService(NS_MARIONETTE_CONTRACTID); + if (marionette) { diff --git a/services/settings/Utils.sys.mjs b/services/settings/Utils.sys.mjs -index d3643aedf21a26594268a47bc0f6ac53e3977f75..795c6f3b28278b9f65a596799d4e424880fcffa7 100644 +index cbdf07bcdae11de6f59e6c6c279833aca430dc70..6840c78350ccb52e99f23446b010abc2a1d92a55 100644 --- a/services/settings/Utils.sys.mjs +++ b/services/settings/Utils.sys.mjs @@ -97,7 +97,7 @@ const _cdnURLs = {}; @@ -2793,10 +2634,10 @@ index 75555352b8a15a50e4a21e34fc8ede4e9246c7cc..72855a404effa42b6c55cd0c2fcb8bdd // ignored for Linux. const unsigned long CHROME_SUPPRESS_ANIMATION = 1 << 24; diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs -index 76fb919603e8d2b7864d351eb47be2a38e40e31e..9f1e880fe9027d1a2540ffeaa11fc0c4e1a36133 100644 +index 3d8837cdb38fb5b25abfb9e3e369ad6fe688706e..867ce2efaa10eaea87ffeeb2669cc5a7be0045b3 100644 --- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs +++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs -@@ -108,7 +108,9 @@ EnterprisePoliciesManager.prototype = { +@@ -106,7 +106,9 @@ EnterprisePoliciesManager.prototype = { Services.prefs.clearUserPref(PREF_POLICIES_APPLIED); } @@ -2807,7 +2648,7 @@ index 76fb919603e8d2b7864d351eb47be2a38e40e31e..9f1e880fe9027d1a2540ffeaa11fc0c4 if (provider.failed) { this.status = Ci.nsIEnterprisePolicies.FAILED; -@@ -631,6 +633,19 @@ class JSONPoliciesProvider { +@@ -606,6 +608,19 @@ class JSONPoliciesProvider { } } @@ -2828,10 +2669,10 @@ index 76fb919603e8d2b7864d351eb47be2a38e40e31e..9f1e880fe9027d1a2540ffeaa11fc0c4 constructor() { this._policies = null; diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp -index 253171bed4dea54fc28bb4ddc9920823dbd9351c..6dc0e620b399ed9ee6b53f97bc080ec17ee4e1b5 100644 +index 7dc1cee243017e1647521b021424781532552d8c..47908d73a7a39a7f95ae8ea28362b4f29505508f 100644 --- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp +++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp -@@ -490,7 +490,7 @@ void PopulateLanguages() { +@@ -587,7 +587,7 @@ void PopulateLanguages() { // sufficient to only collect this information as the other properties are // just reformats of Navigator::GetAcceptLanguages. nsTArray languages; @@ -2841,7 +2682,7 @@ index 253171bed4dea54fc28bb4ddc9920823dbd9351c..6dc0e620b399ed9ee6b53f97bc080ec1 for (const auto& language : languages) { diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp -index dc0826f72134b91482e30d183ddf52e95146e12f..119a324e162b6965ddd3d6b2d53bd2856a174452 100644 +index 316a86d5fcadba99918254ba132f363fb462cc3f..9dcd1b208ffd2d42db6c8ff52476a22791149eb4 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -361,7 +361,7 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) { @@ -2854,7 +2695,7 @@ index dc0826f72134b91482e30d183ddf52e95146e12f..119a324e162b6965ddd3d6b2d53bd285 if (windowEnumerator) { bool more; diff --git a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp -index 654903fadb709be976b72f36f155e23bc0622152..815b3dc24c9fda6b1db6c4666ac68904c87ac0ab 100644 +index 89f7233835b4d03e7a140ca2c75ed8db097d482d..ed7c92d30b3cd77c102467eb0f12c9f482a08fde 100644 --- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp @@ -174,8 +174,8 @@ nsBrowserStatusFilter::OnStateChange(nsIWebProgress* aWebProgress, @@ -2886,10 +2727,10 @@ index 811fb16410e8cf900ad873797269e5fe715579a5..821f5b0c2af8e1dc8754cd023571d1d0 /** diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs -index 40f04aeace0efd701e9454bb8dc6260dec90807e..5b70f65f3e78fc0889b15651ff203bb82e79d202 100644 +index 26d633f4f4fe9125df557ee7367809c8591d7211..6c558e8297114ac3f94ae3836c25e9edd51cc4d4 100644 --- a/toolkit/mozapps/update/UpdateService.sys.mjs +++ b/toolkit/mozapps/update/UpdateService.sys.mjs -@@ -3814,6 +3814,8 @@ export class UpdateService { +@@ -3873,6 +3873,8 @@ export class UpdateService { } get disabledForTesting() { @@ -2899,10 +2740,10 @@ index 40f04aeace0efd701e9454bb8dc6260dec90807e..5b70f65f3e78fc0889b15651ff203bb8 } diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild -index c50b7f3932e18da9fad4b673e353974a001e78c4..708e0d75594ddcd62276d4e08c4bd5c64d7f0698 100644 +index 304c39a11bd88e0b8cf681a3c3bc5dc11ed929ec..71e57ce2164e5436fac68d696b632d120a85d6f2 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild -@@ -152,6 +152,7 @@ if CONFIG["ENABLE_WEBDRIVER"]: +@@ -151,6 +151,7 @@ if CONFIG["ENABLE_WEBDRIVER"]: "/remote", "/testing/firefox-ui", "/testing/marionette", @@ -2946,10 +2787,10 @@ index 7eb9e1104682d4eb47060654f43a1efa8b2a6bb2..a8315d6decf654b5302bea5beeea3414 // Only run this code if LauncherProcessWin.h was included beforehand, thus // signalling that the hosting process should support launcher mode. diff --git a/uriloader/base/nsDocLoader.cpp b/uriloader/base/nsDocLoader.cpp -index e5cc386651e192710b61858ab5625c97a02b92da..e560ad4fef232a26ce1e1b244f4ccea05f4aea71 100644 +index 444116840b68443c31d8df66699d47a582ce4622..9ae13a8462301a8a3024bffb10d9a20f9accff1d 100644 --- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp -@@ -812,6 +812,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, +@@ -861,6 +861,12 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout, ("DocLoader:%p: Firing load event for document.open\n", this)); @@ -2963,7 +2804,7 @@ index e5cc386651e192710b61858ab5625c97a02b92da..e560ad4fef232a26ce1e1b244f4ccea0 // nsDocumentViewer::LoadComplete that doesn't do various things // that are not relevant here because this wasn't an actual diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp -index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef228ea0e0cd 100644 +index 7cd8c24d871465bc96fe7249eb9a41c999057eb5..02b3ce1fbaa8056219a139a3bc3f107343f9bb89 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -112,6 +112,7 @@ @@ -3000,7 +2841,7 @@ index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef22 mSaver = do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); -@@ -1672,7 +1684,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { +@@ -1671,7 +1683,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { return NS_OK; } @@ -3038,7 +2879,7 @@ index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef22 if (NS_FAILED(rv)) { nsresult transferError = rv; -@@ -1733,6 +1774,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { +@@ -1732,6 +1773,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { bool alwaysAsk = true; mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk); @@ -3048,7 +2889,7 @@ index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef22 if (alwaysAsk) { // But we *don't* ask if this mimeInfo didn't come from // our user configuration datastore and the user has said -@@ -2249,6 +2293,16 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver, +@@ -2248,6 +2292,16 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver, NotifyTransfer(aStatus); } @@ -3065,7 +2906,7 @@ index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef22 return NS_OK; } -@@ -2732,6 +2786,15 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) { +@@ -2731,6 +2785,15 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) { } } @@ -3082,10 +2923,10 @@ index e23df8e6f982ea71eb1f07dd677ed13109d2831b..d98f49d34a346113fd0ed5c242d5ef22 // OnStartRequest) mDialog = nullptr; diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h -index 2dd4ff87bda3e0ba395cca168c42b37db1713ddf..83e8a3d328e325b3f50f593c9ea71692f9c7d401 100644 +index 68f240c9f9280c498900561e6cdb3bc4c4d45a38..395f54e8c47d56b0892c75a513e5828de7e4fceb 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h -@@ -258,6 +258,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, +@@ -254,6 +254,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService, mozilla::dom::BrowsingContext* aContentContext, bool aForceSave, nsIInterfaceRequestor* aWindowContext, nsIStreamListener** aStreamListener); @@ -3094,7 +2935,7 @@ index 2dd4ff87bda3e0ba395cca168c42b37db1713ddf..83e8a3d328e325b3f50f593c9ea71692 }; /** -@@ -455,6 +457,9 @@ class nsExternalAppHandler final : public nsIStreamListener, +@@ -451,6 +453,9 @@ class nsExternalAppHandler final : public nsIStreamListener, * Upon successful return, both mTempFile and mSaver will be valid. */ nsresult SetUpTempFile(nsIChannel* aChannel); @@ -3148,7 +2989,7 @@ index 53ea934dd4876e4b491b724385c8fbf7d00ee6cd..0b7b88c853b21ce778d8e87fea0a2bfe /** diff --git a/widget/InProcessCompositorWidget.cpp b/widget/InProcessCompositorWidget.cpp -index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9896abd1c 100644 +index 7098db301770ecb5b9a506d7caec89d5cf63384b..aff8d7562f8a3e595a077ce8e591008762479587 100644 --- a/widget/InProcessCompositorWidget.cpp +++ b/widget/InProcessCompositorWidget.cpp @@ -4,7 +4,10 @@ @@ -3176,10 +3017,10 @@ index 1c25e9d9a101233f71e92288a0f93125b81ac1c5..22cf67b0f6e3ddd2b3ed725a314ba6a9 } #endif diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h -index 5ca1a6fa13233b1bd00ee0467732c5875c51d343..0d3b8ebe127e59516802e8819f4bbed961f0992b 100644 +index 8004d6fe2130246252e57198f2ea731f84d1968a..7a0774b8320741108dc209c3b7069f524b1360ca 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h -@@ -368,6 +368,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -375,6 +375,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase, // Otherwise, this must be 0. uint32_t mClickCount = 0; @@ -3189,7 +3030,7 @@ index 5ca1a6fa13233b1bd00ee0467732c5875c51d343..0d3b8ebe127e59516802e8819f4bbed9 // Whether the event should ignore scroll frame bounds during dispatch. bool mIgnoreRootScrollFrame = false; -@@ -391,6 +394,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, +@@ -398,6 +401,7 @@ class WidgetMouseEvent : public WidgetMouseEventBase, mContextMenuTrigger = aEvent.mContextMenuTrigger; mExitFrom = aEvent.mExitFrom; mClickCount = aEvent.mClickCount; @@ -3261,7 +3102,7 @@ index 24b70173c2e8bb9be9fd6255984a70efe3b14099..75ac367a1c4bb44d4b68b5f4ecc6adf5 } if (aEvent.IsMeta()) { diff --git a/widget/gtk/nsFilePicker.cpp b/widget/gtk/nsFilePicker.cpp -index f4bded345e95674c66e4d4ad56b50844fce0871b..321e22d334a8bbc6057ee78e77e139a2804b2403 100644 +index 02b7b185caf4a2352522c0ed4185d89b514c1738..912a270b6d87e1154d844bde2ffe6a3c2b8f3061 100644 --- a/widget/gtk/nsFilePicker.cpp +++ b/widget/gtk/nsFilePicker.cpp @@ -21,6 +21,7 @@ @@ -3273,7 +3114,7 @@ index f4bded345e95674c66e4d4ad56b50844fce0871b..321e22d334a8bbc6057ee78e77e139a2 #include "nsArrayEnumerator.h" #include "nsEnumeratorUtils.h" diff --git a/widget/headless/HeadlessCompositorWidget.cpp b/widget/headless/HeadlessCompositorWidget.cpp -index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db5151dd90c1a 100644 +index bb4ee9175e66dc40de1871a7f91368fe309494a3..6814ae85038d31f1a3b1f87ba341219ffbc1e6b3 100644 --- a/widget/headless/HeadlessCompositorWidget.cpp +++ b/widget/headless/HeadlessCompositorWidget.cpp @@ -3,6 +3,7 @@ @@ -3284,7 +3125,7 @@ index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db515 #include "mozilla/widget/PlatformWidgetTypes.h" #include "HeadlessCompositorWidget.h" #include "VsyncDispatcher.h" -@@ -15,9 +16,32 @@ HeadlessCompositorWidget::HeadlessCompositorWidget( +@@ -15,9 +16,30 @@ HeadlessCompositorWidget::HeadlessCompositorWidget( const layers::CompositorOptions& aOptions, HeadlessWidget* aWindow) : CompositorWidget(aOptions), mWidget(aWindow), @@ -3304,12 +3145,10 @@ index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db515 +} + +already_AddRefed HeadlessCompositorWidget::StartRemoteDrawingInRegion( -+ const LayoutDeviceIntRegion& aInvalidRegion, -+ layers::BufferMode* aBufferMode) { ++ const LayoutDeviceIntRegion& aInvalidRegion) { + if (!mDrawTarget) + return nullptr; + -+ *aBufferMode = layers::BufferMode::BUFFER_NONE; + RefPtr result = mDrawTarget; + return result.forget(); +} @@ -3317,7 +3156,7 @@ index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db515 void HeadlessCompositorWidget::ObserveVsync(VsyncObserver* aObserver) { if (RefPtr cvd = mWidget->GetCompositorVsyncDispatcher()) { -@@ -31,6 +55,59 @@ void HeadlessCompositorWidget::NotifyClientSizeChanged( +@@ -31,6 +53,59 @@ void HeadlessCompositorWidget::NotifyClientSizeChanged( const LayoutDeviceIntSize& aClientSize) { auto size = mClientSize.Lock(); *size = aClientSize; @@ -3378,7 +3217,7 @@ index bb4ee9175e66dc40de1871a7f91368fe309494a3..747625e3869882300bfbc18b184db515 LayoutDeviceIntSize HeadlessCompositorWidget::GetClientSize() { diff --git a/widget/headless/HeadlessCompositorWidget.h b/widget/headless/HeadlessCompositorWidget.h -index facd2bc65afab8ec1aa322faa20a67464964dfb9..d6dea95472bec6006411753c3dfdab2e3659171f 100644 +index facd2bc65afab8ec1aa322faa20a67464964dfb9..3c5751ad1b7f042bc7cd9a63895cebcd2942ccd3 100644 --- a/widget/headless/HeadlessCompositorWidget.h +++ b/widget/headless/HeadlessCompositorWidget.h @@ -6,6 +6,7 @@ @@ -3389,7 +3228,7 @@ index facd2bc65afab8ec1aa322faa20a67464964dfb9..d6dea95472bec6006411753c3dfdab2e #include "mozilla/widget/CompositorWidget.h" #include "HeadlessWidget.h" -@@ -23,8 +24,12 @@ class HeadlessCompositorWidget final : public CompositorWidget, +@@ -23,8 +24,11 @@ class HeadlessCompositorWidget final : public CompositorWidget, HeadlessWidget* aWindow); void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize); @@ -3397,12 +3236,11 @@ index facd2bc65afab8ec1aa322faa20a67464964dfb9..d6dea95472bec6006411753c3dfdab2e // CompositorWidget Overrides + already_AddRefed StartRemoteDrawingInRegion( -+ const LayoutDeviceIntRegion& aInvalidRegion, -+ layers::BufferMode* aBufferMode) override; ++ const LayoutDeviceIntRegion& aInvalidRegion) override; uintptr_t GetWidgetKey() override; -@@ -42,10 +47,18 @@ class HeadlessCompositorWidget final : public CompositorWidget, +@@ -42,10 +46,18 @@ class HeadlessCompositorWidget final : public CompositorWidget, } private: diff --git a/browser_patches/webkit/UPSTREAM_CONFIG.sh b/browser_patches/webkit/UPSTREAM_CONFIG.sh index 574e1f310aa7f..d6e587bc4a52b 100644 --- a/browser_patches/webkit/UPSTREAM_CONFIG.sh +++ b/browser_patches/webkit/UPSTREAM_CONFIG.sh @@ -1,3 +1,3 @@ REMOTE_URL="https://github.com/WebKit/WebKit.git" BASE_BRANCH="main" -BASE_REVISION="153da00a252619799ba4b32cd0ac6c5b8faf6a35" +BASE_REVISION="db897909f7c31d9b38793374c66427b6a4cb3dd3" diff --git a/browser_patches/webkit/embedder/Playwright/mac/AppDelegate.m b/browser_patches/webkit/embedder/Playwright/mac/AppDelegate.m index b4d08b700ac91..0d100edad05e4 100644 --- a/browser_patches/webkit/embedder/Playwright/mac/AppDelegate.m +++ b/browser_patches/webkit/embedder/Playwright/mac/AppDelegate.m @@ -236,16 +236,18 @@ - (WKWebViewConfiguration *)defaultConfiguration configuration.preferences._mediaDevicesEnabled = YES; configuration.preferences._mockCaptureDevicesEnabled = YES; // Enable WebM support. - configuration.preferences._alternateWebMPlayerEnabled = YES; configuration.preferences._hiddenPageDOMTimerThrottlingEnabled = NO; configuration.preferences._hiddenPageDOMTimerThrottlingAutoIncreases = NO; configuration.preferences._pageVisibilityBasedProcessSuppressionEnabled = NO; configuration.preferences._domTimersThrottlingEnabled = NO; // Do not auto play audio and video with sound. configuration.defaultWebpagePreferences._autoplayPolicy = _WKWebsiteAutoplayPolicyAllowWithoutSound; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" _WKProcessPoolConfiguration *processConfiguration = [[[_WKProcessPoolConfiguration alloc] init] autorelease]; processConfiguration.forceOverlayScrollbars = YES; configuration.processPool = [[[WKProcessPool alloc] _initWithConfiguration:processConfiguration] autorelease]; + #pragma clang diagnostic pop } return configuration; } @@ -288,7 +290,10 @@ - (WKWebViewConfiguration *) sessionConfiguration:(uint64_t)sessionID continue; WKWebViewConfiguration *configuration = [[[self defaultConfiguration] copy] autorelease]; configuration.websiteDataStore = [browserContext dataStore]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" configuration.processPool = [browserContext processPool]; + #pragma clang diagnostic pop return configuration; } return [self defaultConfiguration]; @@ -347,8 +352,11 @@ - (WKWebView *)createHeadlessPage:(WKWebViewConfiguration *)configuration withUR - (_WKBrowserContext *)createBrowserContext:(NSString *)proxyServer WithBypassList:(NSString *) proxyBypassList { _WKBrowserContext *browserContext = [[_WKBrowserContext alloc] init]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" _WKProcessPoolConfiguration *processConfiguration = [[[_WKProcessPoolConfiguration alloc] init] autorelease]; processConfiguration.forceOverlayScrollbars = YES; + #pragma clang diagnostic pop _WKWebsiteDataStoreConfiguration *dataStoreConfiguration = [[[_WKWebsiteDataStoreConfiguration alloc] initNonPersistentConfiguration] autorelease]; if (!proxyServer || ![proxyServer length]) proxyServer = _proxyServer; @@ -356,7 +364,10 @@ - (_WKBrowserContext *)createBrowserContext:(NSString *)proxyServer WithBypassLi proxyBypassList = _proxyBypassList; [dataStoreConfiguration setProxyConfiguration:[self proxyConfiguration:proxyServer WithBypassList:proxyBypassList]]; browserContext.dataStore = [[[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration] autorelease]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" browserContext.processPool = [[[WKProcessPool alloc] _initWithConfiguration:processConfiguration] autorelease]; + #pragma clang diagnostic pop [_browserContexts addObject:browserContext]; return browserContext; } diff --git a/browser_patches/webkit/patches/bootstrap.diff b/browser_patches/webkit/patches/bootstrap.diff index 9d644885f063b..f713c6fa093a1 100644 --- a/browser_patches/webkit/patches/bootstrap.diff +++ b/browser_patches/webkit/patches/bootstrap.diff @@ -1,8 +1,8 @@ diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt -index 458e1d4118805169ee6f9139af942cc5c3f88f8e..238f2a33ba150fae735915244910eaa66f5fd267 100644 +index 1b7690be3268f54cc649a97206fc38fb27453e08..d14a541e585d2b43d6738c9bb5eb26642522b46b 100644 --- a/Source/JavaScriptCore/CMakeLists.txt +++ b/Source/JavaScriptCore/CMakeLists.txt -@@ -1411,21 +1411,26 @@ set(JavaScriptCore_INSPECTOR_DOMAINS +@@ -1418,21 +1418,26 @@ set(JavaScriptCore_INSPECTOR_DOMAINS ${JAVASCRIPTCORE_DIR}/inspector/protocol/CSS.json ${JAVASCRIPTCORE_DIR}/inspector/protocol/Canvas.json ${JAVASCRIPTCORE_DIR}/inspector/protocol/Console.json @@ -30,10 +30,10 @@ index 458e1d4118805169ee6f9139af942cc5c3f88f8e..238f2a33ba150fae735915244910eaa6 ${JAVASCRIPTCORE_DIR}/inspector/protocol/ServiceWorker.json ${JAVASCRIPTCORE_DIR}/inspector/protocol/Target.json diff --git a/Source/JavaScriptCore/DerivedSources-input.xcfilelist b/Source/JavaScriptCore/DerivedSources-input.xcfilelist -index 9fd3a4e6a53eb3339ced1e87f0b4cacd16f73167..a77d72ab052625c7b5a4d7c56a71befbad884871 100644 +index 53809cbc20c8fb942304a25899a2e5ecccc79ee3..63d93e08bcf8089e4e315e48d5d2093750588cb1 100644 --- a/Source/JavaScriptCore/DerivedSources-input.xcfilelist +++ b/Source/JavaScriptCore/DerivedSources-input.xcfilelist -@@ -98,20 +98,25 @@ $(PROJECT_DIR)/inspector/protocol/CPUProfiler.json +@@ -100,20 +100,25 @@ $(PROJECT_DIR)/inspector/protocol/CPUProfiler.json $(PROJECT_DIR)/inspector/protocol/CSS.json $(PROJECT_DIR)/inspector/protocol/Canvas.json $(PROJECT_DIR)/inspector/protocol/Console.json @@ -60,10 +60,10 @@ index 9fd3a4e6a53eb3339ced1e87f0b4cacd16f73167..a77d72ab052625c7b5a4d7c56a71befb $(PROJECT_DIR)/inspector/protocol/Security.json $(PROJECT_DIR)/inspector/protocol/ServiceWorker.json diff --git a/Source/JavaScriptCore/DerivedSources.make b/Source/JavaScriptCore/DerivedSources.make -index 3db6c2e250371dedc6a9f88bf1688112d467434a..a3832759ea36fb50bdbea6809b9fd13e6f66e46b 100644 +index cb42b0e750a8b6a64ebc58f1bfe65c50e7b4c99c..ed548bf0ac70da0273a20178a2786e232907ce68 100644 --- a/Source/JavaScriptCore/DerivedSources.make +++ b/Source/JavaScriptCore/DerivedSources.make -@@ -301,21 +301,26 @@ INSPECTOR_DOMAINS := \ +@@ -303,21 +303,26 @@ INSPECTOR_DOMAINS := \ $(JavaScriptCore)/inspector/protocol/CSS.json \ $(JavaScriptCore)/inspector/protocol/Canvas.json \ $(JavaScriptCore)/inspector/protocol/Console.json \ @@ -118,7 +118,7 @@ index 9bc5d1fd8e2a7e576be046b3c6ae1266696cf552..610f810db1dd6865c500c0796386a828 { return addPrefixToIdentifier(++s_lastUsedIdentifier); diff --git a/Source/JavaScriptCore/inspector/IdentifiersFactory.h b/Source/JavaScriptCore/inspector/IdentifiersFactory.h -index 4113ddb45d4a8d08379d4dc56c44cde56162accf..e00cb9cb01a7a241f3f25b1e4cdc2fcaee92b3ac 100644 +index b967783b83cc6da337ab0840ce0a0a8d480cbc4f..06a09811709e5e6cb1db24ad6fc4895481d39f65 100644 --- a/Source/JavaScriptCore/inspector/IdentifiersFactory.h +++ b/Source/JavaScriptCore/inspector/IdentifiersFactory.h @@ -31,6 +31,7 @@ namespace Inspector { @@ -146,7 +146,7 @@ index 8b39848154ecab9f7daa2d21c85562a319cd06d7..c8a1f44cb4516993899ffe1404b6c386 return nullptr; inspectorObject->setValue(name.string(), inspectorValue.releaseNonNull()); diff --git a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp -index 1f5d0adbf624bd24ef1e525967e6e82e8c37b4e5..4fe0f364b4ccd11774bf29f772e0a568549a4322 100644 +index 6f8cae4d995d3ae020cd09aa0ba0c9ccc1e78409..fc3d0d1e0803ea38a70fee82ea94624797c6e407 100644 --- a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp +++ b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.cpp @@ -102,7 +102,7 @@ void BackendDispatcher::registerDispatcherForDomain(const String& domain, Supple @@ -169,7 +169,7 @@ index 1f5d0adbf624bd24ef1e525967e6e82e8c37b4e5..4fe0f364b4ccd11774bf29f772e0a568 // We could be called re-entrantly from a nested run loop, so restore the previous id. SetForScope scopedRequestId(m_currentRequestId, requestId); diff --git a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.h b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.h -index 28f4cdacf6ebd7037a42a75872618436332d90ec..463f014be2bd29a75bee7b2113b6f929da13aca5 100644 +index c988668bd732434ec4eaa485c7bbc0a93e2e64cf..75da5ed8a0575d8fe444f3f251c1af99fed78c97 100644 --- a/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.h +++ b/Source/JavaScriptCore/inspector/InspectorBackendDispatcher.h @@ -95,8 +95,11 @@ public: @@ -237,7 +237,7 @@ index b555c2e5a071d0a6a016061cc60755449557556d..d019346f0932296d15212c76a4a9b56b bool m_isPaused { false }; }; diff --git a/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp b/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp -index 13eb84b3cba851e14eae8894f8f7aeb242b6f9d4..30bfdf65a7b891420a25bbc54bfe7f03db694273 100644 +index 2b36937d913f237ca777f455ada0fbb9bf5f29df..1dd30c0c017dcce8b9019d5c2bf28005e6ea971e 100644 --- a/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp +++ b/Source/JavaScriptCore/inspector/JSGlobalObjectConsoleClient.cpp @@ -222,6 +222,14 @@ void JSGlobalObjectConsoleClient::screenshot(JSGlobalObject*, Ref> getPreview(const Protocol::Runtime::RemoteObjectId&) final; Protocol::ErrorStringOr>, RefPtr>>> getProperties(const Protocol::Runtime::RemoteObjectId&, std::optional&& ownProperties, std::optional&& fetchStart, std::optional&& fetchCount, std::optional&& generatePreview) final; diff --git a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp -index e47c6ca59f37fbf18ca8a393df72e0472363fabd..b393465540595220561ae00afb85408279710864 100644 +index db75bf0f9edf28d3e23ab40740dfb86a6fd3b104..045caf300e3f9085e5d0ae7feb424f4bf3cf964f 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.cpp @@ -90,6 +90,34 @@ Protocol::ErrorStringOr InspectorTargetAgent::sendMessageToTarget(const St @@ -365,7 +365,7 @@ index e47c6ca59f37fbf18ca8a393df72e0472363fabd..b393465540595220561ae00afb854082 void InspectorTargetAgent::didCommitProvisionalTarget(const String& oldTargetID, const String& committedTargetID) diff --git a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h -index 04377b714a6ccb5294c65d592e74350621d470ba..b6de937bfa3e6185ce29f4e432d327a3cedee6df 100644 +index d1ebed23d1fbacd95e1543606b2591438826b0d2..8536f8325de2afa21e814c2d2ce4bef8189bda70 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorTargetAgent.h @@ -53,8 +53,11 @@ public: @@ -1679,33 +1679,39 @@ index 24891ad836086fd23024fcb4d08ca63f6974c812..29f4b6b1923383fec7a99d28a4e815dc private: enum ArgumentRequirement { ArgumentRequired, ArgumentNotRequired }; diff --git a/Source/ThirdParty/libwebrtc/CMakeLists.txt b/Source/ThirdParty/libwebrtc/CMakeLists.txt -index ca4f3508a44e3c6677a72fbe3d7c853714b4f2c6..ae117f5f402a7eb259e376ca9440e00062e22d9f 100644 +index a04e69b350a29f6b569a04d094a15872f0745870..6f2aa0dcb9fe06cdbc8184b80b13bf735729ad1f 100644 --- a/Source/ThirdParty/libwebrtc/CMakeLists.txt +++ b/Source/ThirdParty/libwebrtc/CMakeLists.txt -@@ -532,6 +532,11 @@ set(webrtc_SOURCES - Source/third_party/crc32c/src/src/crc32c.cc - Source/third_party/crc32c/src/src/crc32c_portable.cc - Source/third_party/crc32c/src/src/crc32c_sse42.cc +@@ -1807,6 +1807,14 @@ list(APPEND webrtc_SOURCES + Source/third_party/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc + ) + +# Playwright begin ++list(APPEND webrtc_SOURCES + Source/third_party/libwebm/mkvmuxer/mkvmuxer.cc + Source/third_party/libwebm/mkvmuxer/mkvmuxerutil.cc + Source/third_party/libwebm/mkvmuxer/mkvwriter.cc ++) +# Playwright end - Source/third_party/libyuv/source/compare.cc - Source/third_party/libyuv/source/compare_common.cc - Source/third_party/libyuv/source/compare_gcc.cc -@@ -2348,6 +2353,11 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE - Source/third_party/libsrtp/config - Source/third_party/libsrtp/crypto/include - Source/third_party/libsrtp/include ++ + if (WTF_CPU_X86_64 OR WTF_CPU_X86) + list(APPEND webrtc_SOURCES + Source/webrtc/common_audio/fir_filter_sse.cc +@@ -2399,6 +2407,14 @@ set(webrtc_INCLUDE_DIRECTORIES PRIVATE + Source/webrtc/modules/video_coding/include + ) + +# Playwright begin ++list(APPEND webrtc_INCLUDE_DIRECTORIES + Source/third_party/libwebm + Source/third_party/libvpx/source/libvpx + Source/third_party/libvpx/source/libvpx/third_party/googletest/src/include ++) +# Playwright end - Source/third_party/libyuv/include - Source/third_party/opus/src/celt - Source/third_party/opus/src/include ++ + if (APPLE) + list(APPEND webrtc_INCLUDE_DIRECTORIES PRIVATE + Source/third_party/libvpx/source/libvpx diff --git a/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig b/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig index 0c5c8e689bdddec766f9de5bffd4444a5e068d77..330dd1f585e530722178c65c883641a2b8c0f1bd 100644 --- a/Source/ThirdParty/libwebrtc/Configurations/Base-libwebrtc.xcconfig @@ -1720,13 +1726,13 @@ index 0c5c8e689bdddec766f9de5bffd4444a5e068d77..330dd1f585e530722178c65c883641a2 // FIXME: Set WEBRTC_USE_BUILTIN_ISAC_FIX and WEBRTC_USE_BUILTIN_ISAC_FLOAT for iOS and Mac diff --git a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -index 13c5b5ea562fe808a3251c3ae789f8106632cd25..36b77e7d6bc78ba2e982cad5a2b4792775d3280d 100644 +index 30585bd097ec1e57d0c5f87180bf59a71f0192df..95b7a60b3cc66704c51fd6a6ce4e32582a089f9f 100644 --- a/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp +++ b/Source/ThirdParty/libwebrtc/Configurations/libwebrtc.exp -@@ -434,3 +434,16 @@ __ZN6webrtc18VideoFrameMetadata7SetSsrcEj - __ZN6webrtc18VideoFrameMetadata8SetCsrcsENSt3__16vectorIjNS1_9allocatorIjEEEE - __ZN6webrtc18VideoFrameMetadata8SetWidthEt - __ZN6webrtc18VideoFrameMetadata9SetHeightEt +@@ -404,3 +404,16 @@ __ZTVN6webrtc14NetworkManagerE + __ZTVN6webrtc17AsyncPacketSocketE + __ZTVN6webrtc18NetworkManagerBaseE + __ZNK6webrtc9IPAddressneERKS0_ +__ZN8mkvmuxer11SegmentInfo4InitEv +__ZN8mkvmuxer9MkvWriterC1EP7__sFILE +_ARGBToI420 @@ -1741,7 +1747,7 @@ index 13c5b5ea562fe808a3251c3ae789f8106632cd25..36b77e7d6bc78ba2e982cad5a2b47927 +_vpx_codec_version_str +_vpx_codec_vp8_cx diff --git a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj -index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18bc221e30b 100644 +index df544586f202dfdcb53b0c16ecf7a51b60ca3767..81f39ca4766d0368109ae90b94beb3cbe458a06f 100644 --- a/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj +++ b/Source/ThirdParty/libwebrtc/libwebrtc.xcodeproj/project.pbxproj @@ -56,6 +56,20 @@ @@ -1765,7 +1771,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b /* Begin PBXBuildFile section */ 2D6BFF60280A93DF00A1A74F /* video_coding.h in Headers */ = {isa = PBXBuildFile; fileRef = 4131C45B234C81710028A615 /* video_coding.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2D6BFF61280A93EC00A1A74F /* video_codec_initializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4131C45E234C81720028A615 /* video_codec_initializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; -@@ -5786,6 +5800,13 @@ +@@ -5852,6 +5866,13 @@ remoteGlobalIDString = DDF30D0527C5C003006A526F; remoteInfo = absl; }; @@ -1779,7 +1785,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ -@@ -24271,6 +24292,7 @@ +@@ -24570,6 +24591,7 @@ ); dependencies = ( 410B3827292B73E90003E515 /* PBXTargetDependency */, @@ -1787,7 +1793,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b DD2E76E827C6B69A00F2A74C /* PBXTargetDependency */, CDEBB4CC24C01AB400ADBD44 /* PBXTargetDependency */, 411ED040212E0811004320BA /* PBXTargetDependency */, -@@ -24364,6 +24386,7 @@ +@@ -24663,6 +24685,7 @@ 4460B8B92B155B6A00392062 /* vp9_qp_parser_fuzzer */, 444A6EF02AEADFC9005FE121 /* vp9_replay_fuzzer */, 44945C512B9BA1C300447FFD /* webm_fuzzer */, @@ -1795,7 +1801,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b ); }; /* End PBXProject section */ -@@ -24467,6 +24490,23 @@ +@@ -24766,6 +24789,23 @@ shellPath = /bin/sh; shellScript = "[ -z \"${WK_DERIVED_SDK_HEADERS_DIR}\" -o -d \"${WK_DERIVED_SDK_HEADERS_DIR}\" ] && touch \"${SCRIPT_OUTPUT_FILE_0}\"\n"; }; @@ -1819,7 +1825,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ -@@ -27437,6 +27477,11 @@ +@@ -27789,6 +27829,11 @@ target = DDF30D0527C5C003006A526F /* absl */; targetProxy = DD2E76E727C6B69A00F2A74C /* PBXContainerItemProxy */; }; @@ -1831,7 +1837,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ -@@ -28204,6 +28249,27 @@ +@@ -28556,6 +28601,27 @@ }; name = Production; }; @@ -1859,7 +1865,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b FB39D0711200ED9200088E69 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5D7C59C71208C68B001C873E /* DebugRelease.xcconfig */; -@@ -28586,6 +28652,16 @@ +@@ -28938,6 +29004,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Production; }; @@ -1877,7 +1883,7 @@ index 7585b2e5e9bffdc8cabd888dd822313d53b30141..5909d33b9726cdc7d2d53b538f7da18b isa = XCConfigurationList; buildConfigurations = ( diff --git a/Source/ThirdParty/skia/CMakeLists.txt b/Source/ThirdParty/skia/CMakeLists.txt -index 6bfc5cba986488f3d808ebd0583c476cd93da70e..f4c4222d17bce640355ba52e00152069d5b14432 100644 +index 8675218a87262162d91bf992d00a1eecaf83f289..7dfe83ab3f9dd97bb13721f7034b4963efce3a30 100644 --- a/Source/ThirdParty/skia/CMakeLists.txt +++ b/Source/ThirdParty/skia/CMakeLists.txt @@ -10,6 +10,8 @@ if (USE_SKIA_ENCODERS) @@ -1889,7 +1895,7 @@ index 6bfc5cba986488f3d808ebd0583c476cd93da70e..f4c4222d17bce640355ba52e00152069 if (ANDROID) find_package(EXPAT REQUIRED) endif () -@@ -948,6 +950,7 @@ endif () +@@ -958,6 +960,7 @@ endif () target_link_libraries(Skia PRIVATE JPEG::JPEG PNG::PNG @@ -1897,23 +1903,11 @@ index 6bfc5cba986488f3d808ebd0583c476cd93da70e..f4c4222d17bce640355ba52e00152069 ) WEBKIT_ADD_TARGET_CXX_FLAGS(Skia -diff --git a/Source/ThirdParty/skia/src/opts/SkOpts_SetTarget.h b/Source/ThirdParty/skia/src/opts/SkOpts_SetTarget.h -index 525cfcb862ae96bf8573d00b67dc9e5e23c10d22..f2debc0444cb8f5b80a0e99a2214bceaab3960c1 100644 ---- a/Source/ThirdParty/skia/src/opts/SkOpts_SetTarget.h -+++ b/Source/ThirdParty/skia/src/opts/SkOpts_SetTarget.h -@@ -65,6 +65,7 @@ - // Each of the specific intrinsic headers also checks to ensure that immintrin.h has been - // included, so do that here, first. - #if defined(__clang__) && defined(_MSC_VER) -+ #define __RTMINTRIN_H // Workaround for https://github.com/llvm/llvm-project/issues/95133 - #include - #endif - diff --git a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd7bf5a80f 100644 +index fde8f264b4df392fc8c6a06309f9a676f9c04c9c..955a01995909ccda6d486479bb81574982ee7fd1 100644 --- a/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml +++ b/Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml -@@ -588,6 +588,7 @@ ApplePayEnabled: +@@ -562,6 +562,7 @@ ApplePayEnabled: richJavaScript: true # FIXME: This is on by default in WebKit2 PLATFORM(COCOA). Perhaps we should consider turning it on for WebKitLegacy as well. @@ -1921,7 +1915,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd AsyncClipboardAPIEnabled: type: bool status: mature -@@ -598,7 +599,7 @@ AsyncClipboardAPIEnabled: +@@ -572,7 +573,7 @@ AsyncClipboardAPIEnabled: default: false WebKit: "PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(WPE)" : true @@ -1930,7 +1924,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd WebCore: default: false -@@ -857,13 +858,10 @@ BlobFileAccessEnforcementEnabled: +@@ -832,13 +833,10 @@ BlobFileAccessEnforcementEnabled: sharedPreferenceForWebProcess: true defaultValue: WebKitLegacy: @@ -1944,7 +1938,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false BlockFontServiceInWebContentSandbox: -@@ -2143,6 +2141,7 @@ CrossOriginEmbedderPolicyEnabled: +@@ -2089,6 +2087,7 @@ CrossOriginEmbedderPolicyEnabled: WebCore: default: false @@ -1952,7 +1946,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd CrossOriginOpenerPolicyEnabled: type: bool status: stable -@@ -2216,6 +2215,7 @@ DOMAudioSessionFullEnabled: +@@ -2162,6 +2161,7 @@ DOMAudioSessionFullEnabled: WebCore: default: false @@ -1960,7 +1954,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd DOMPasteAccessRequestsEnabled: type: bool status: internal -@@ -2227,7 +2227,7 @@ DOMPasteAccessRequestsEnabled: +@@ -2173,7 +2173,7 @@ DOMPasteAccessRequestsEnabled: default: false WebKit: "PLATFORM(IOS) || PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(WPE) || PLATFORM(VISION)": true @@ -1969,7 +1963,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd WebCore: default: false -@@ -2293,10 +2293,10 @@ DataListElementEnabled: +@@ -2239,10 +2239,10 @@ DataListElementEnabled: WebKitLegacy: default: false WebKit: @@ -1982,7 +1976,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false sharedPreferenceForWebProcess: true -@@ -2309,7 +2309,7 @@ DataTransferItemsEnabled: +@@ -2255,7 +2255,7 @@ DataTransferItemsEnabled: WebKitLegacy: default: true WebKit: @@ -1991,7 +1985,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -2537,7 +2537,7 @@ DirectoryUploadEnabled: +@@ -2497,7 +2497,7 @@ DirectoryUploadEnabled: WebKitLegacy: default: false WebKit: @@ -2000,7 +1994,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -3020,10 +3020,10 @@ FullScreenEnabled: +@@ -3022,10 +3022,10 @@ FullScreenEnabled: WebKitLegacy: default: false WebKit: @@ -2013,7 +2007,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false sharedPreferenceForWebProcess: true -@@ -3614,7 +3614,7 @@ InputTypeColorEnabled: +@@ -3632,7 +3632,7 @@ InputTypeColorEnabled: WebKitLegacy: default: false WebKit: @@ -2022,7 +2016,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -3647,7 +3647,7 @@ InputTypeDateEnabled: +@@ -3665,7 +3665,7 @@ InputTypeDateEnabled: "PLATFORM(IOS_FAMILY)": true default: false WebKit: @@ -2031,7 +2025,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -3663,7 +3663,7 @@ InputTypeDateTimeLocalEnabled: +@@ -3681,7 +3681,7 @@ InputTypeDateTimeLocalEnabled: "PLATFORM(IOS_FAMILY)": true default: false WebKit: @@ -2040,7 +2034,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -3695,7 +3695,7 @@ InputTypeTimeEnabled: +@@ -3713,7 +3713,7 @@ InputTypeTimeEnabled: "PLATFORM(IOS_FAMILY)": true default: false WebKit: @@ -2049,7 +2043,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -3756,6 +3756,7 @@ InspectorMaximumResourcesContentSize: +@@ -3774,6 +3774,7 @@ InspectorMaximumResourcesContentSize: "PLATFORM(WPE)": 50 default: 200 @@ -2057,7 +2051,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd InspectorStartsAttached: type: bool status: embedder -@@ -3763,7 +3764,7 @@ InspectorStartsAttached: +@@ -3781,7 +3782,7 @@ InspectorStartsAttached: exposed: [ WebKit ] defaultValue: WebKit: @@ -2066,7 +2060,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd InspectorSupportsShowingCertificate: type: bool -@@ -5683,7 +5684,7 @@ PermissionsAPIEnabled: +@@ -5701,7 +5702,7 @@ PermissionsAPIEnabled: WebKitLegacy: default: false WebKit: @@ -2075,7 +2069,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false WebCore: default: false -@@ -5762,6 +5763,19 @@ PitchCorrectionAlgorithm: +@@ -5780,6 +5781,19 @@ PitchCorrectionAlgorithm: WebCore: default: MediaPlayerEnums::PitchCorrectionAlgorithm::BestAllAround @@ -2095,7 +2089,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd PointerLockOptionsEnabled: type: bool status: stable -@@ -6344,7 +6358,7 @@ ScreenOrientationAPIEnabled: +@@ -6364,7 +6378,7 @@ ScreenOrientationAPIEnabled: WebKitLegacy: default: false WebKit: @@ -2104,15 +2098,15 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd WebCore: default: false sharedPreferenceForWebProcess: true -@@ -7806,6 +7820,7 @@ UseCGDisplayListsForDOMRendering: - default: true - sharedPreferenceForWebProcess: true +@@ -7847,6 +7861,7 @@ UseDamagingInformationForCompositing: + WebCore: + default: false +# Playwright: force-disable on Windows. UseGPUProcessForCanvasRenderingEnabled: type: bool status: stable -@@ -7818,7 +7833,7 @@ UseGPUProcessForCanvasRenderingEnabled: +@@ -7859,7 +7874,7 @@ UseGPUProcessForCanvasRenderingEnabled: defaultValue: WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT)": true @@ -2121,7 +2115,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd default: false UseGPUProcessForDOMRenderingEnabled: -@@ -7863,6 +7878,7 @@ UseGPUProcessForMediaEnabled: +@@ -7904,6 +7919,7 @@ UseGPUProcessForMediaEnabled: sharedPreferenceForWebProcess: true mediaPlaybackRelated: true @@ -2129,7 +2123,7 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd UseGPUProcessForWebGLEnabled: type: bool status: internal -@@ -7874,7 +7890,7 @@ UseGPUProcessForWebGLEnabled: +@@ -7915,7 +7931,7 @@ UseGPUProcessForWebGLEnabled: default: false WebKit: "ENABLE(GPU_PROCESS_BY_DEFAULT) && ENABLE(GPU_PROCESS_WEBGL_BY_DEFAULT)": true @@ -2139,10 +2133,10 @@ index 3437d1ccf6e9a78bb9a42158e54ba99f4d9d25f9..b67f7d77bdf4090ec9c29abd221562cd WebCore: "ENABLE(GPU_PROCESS_BY_DEFAULT) && ENABLE(GPU_PROCESS_WEBGL_BY_DEFAULT)": true diff --git a/Source/WTF/wtf/PlatformEnable.h b/Source/WTF/wtf/PlatformEnable.h -index a36395e83c53617c2f894695846f8798fc277624..f0b8dd0fd12b4cde24975c20015cee30292abf9d 100644 +index c7bb871d6471f8ad9144db5063f39e37db20a396..0c09abedbc001572341f30deabefe588e4938e71 100644 --- a/Source/WTF/wtf/PlatformEnable.h +++ b/Source/WTF/wtf/PlatformEnable.h -@@ -385,7 +385,7 @@ +@@ -381,7 +381,7 @@ // ORIENTATION_EVENTS should never get enabled on Desktop, only Mobile. #if !defined(ENABLE_ORIENTATION_EVENTS) @@ -2151,7 +2145,7 @@ index a36395e83c53617c2f894695846f8798fc277624..f0b8dd0fd12b4cde24975c20015cee30 #endif #if !defined(ENABLE_OVERFLOW_SCROLLING_TOUCH) -@@ -502,7 +502,7 @@ +@@ -498,7 +498,7 @@ #endif #if !defined(ENABLE_TOUCH_EVENTS) @@ -2161,10 +2155,10 @@ index a36395e83c53617c2f894695846f8798fc277624..f0b8dd0fd12b4cde24975c20015cee30 #if !defined(ENABLE_TOUCH_ACTION_REGIONS) diff --git a/Source/WTF/wtf/PlatformEnableCocoa.h b/Source/WTF/wtf/PlatformEnableCocoa.h -index 8304147ff102789180b2682eb64d599791528c93..af8cb85981bda7b91edfa21b6cc321849d93b909 100644 +index ab4579dd256f98d3984882e2a2f26a527993416e..3a529e1864e88ccf3ce2df47176b0215427eb4bc 100644 --- a/Source/WTF/wtf/PlatformEnableCocoa.h +++ b/Source/WTF/wtf/PlatformEnableCocoa.h -@@ -808,7 +808,7 @@ +@@ -795,7 +795,7 @@ #endif #if !defined(ENABLE_SEC_ITEM_SHIM) @@ -2174,10 +2168,10 @@ index 8304147ff102789180b2682eb64d599791528c93..af8cb85981bda7b91edfa21b6cc32184 #if !defined(ENABLE_SERVER_PRECONNECT) diff --git a/Source/WTF/wtf/PlatformHave.h b/Source/WTF/wtf/PlatformHave.h -index 9e91a26aac61faea9634328f9a46421a4b4b7c38..b70776d97be4025435dc3c0364105cb17c429f6a 100644 +index bd819cb4b1bef3c4758353827ee0de710708848e..40c923882da567889b48730d3ec3b809aa040b1a 100644 --- a/Source/WTF/wtf/PlatformHave.h +++ b/Source/WTF/wtf/PlatformHave.h -@@ -1189,7 +1189,8 @@ +@@ -1059,7 +1059,8 @@ #endif #if PLATFORM(MAC) @@ -2204,10 +2198,10 @@ index 007b8fe3292f326504013be8198ae020f7aacf35..1c722c473732ffe05fdb61010fa4417e namespace Unicode { diff --git a/Source/WebCore/DerivedSources.make b/Source/WebCore/DerivedSources.make -index 0f0341624503ae9744b71d3675dc96545371456a..cc73c79374f07fbf1f83e7075e53a3d99da0705c 100644 +index 6d76e72ddae832186e6f3609146ac29b91e77a85..18a0976d7c065968d688fb1f585b0022541bac04 100644 --- a/Source/WebCore/DerivedSources.make +++ b/Source/WebCore/DerivedSources.make -@@ -1229,6 +1229,10 @@ JS_BINDING_IDLS := \ +@@ -1230,6 +1230,10 @@ JS_BINDING_IDLS := \ $(WebCore)/dom/SubscriberCallback.idl \ $(WebCore)/dom/SubscriptionObserver.idl \ $(WebCore)/dom/SubscriptionObserverCallback.idl \ @@ -2218,7 +2212,7 @@ index 0f0341624503ae9744b71d3675dc96545371456a..cc73c79374f07fbf1f83e7075e53a3d9 $(WebCore)/dom/Text.idl \ $(WebCore)/dom/TextDecoder.idl \ $(WebCore)/dom/TextDecoderStream.idl \ -@@ -1829,9 +1833,6 @@ JS_BINDING_IDLS := \ +@@ -1830,9 +1834,6 @@ JS_BINDING_IDLS := \ ADDITIONAL_BINDING_IDLS = \ DocumentTouch.idl \ GestureEvent.idl \ @@ -2229,10 +2223,10 @@ index 0f0341624503ae9744b71d3675dc96545371456a..cc73c79374f07fbf1f83e7075e53a3d9 vpath %.in $(WEBKITADDITIONS_HEADER_SEARCH_PATHS) diff --git a/Source/WebCore/Modules/geolocation/Geolocation.cpp b/Source/WebCore/Modules/geolocation/Geolocation.cpp -index 7d0ca9a308e7aeaf132dccfddeae129fc8c9e093..0eeac0eec23fbc8c3df6c56d63603acc46e8590c 100644 +index 51d4e0b4cbc69d2c5c5f76b7063b54865b6fc2d0..d23693e52b96d579f0d828dd825c5dab8572e7c7 100644 --- a/Source/WebCore/Modules/geolocation/Geolocation.cpp +++ b/Source/WebCore/Modules/geolocation/Geolocation.cpp -@@ -362,8 +362,9 @@ bool Geolocation::shouldBlockGeolocationRequests() +@@ -374,8 +374,9 @@ bool Geolocation::shouldBlockGeolocationRequests() bool isSecure = SecurityOrigin::isSecure(document->url()) || document->isSecureContext(); bool hasMixedContent = !document->foundMixedContent().isEmpty(); bool isLocalOrigin = securityOrigin()->isLocal(); @@ -2244,10 +2238,10 @@ index 7d0ca9a308e7aeaf132dccfddeae129fc8c9e093..0eeac0eec23fbc8c3df6c56d63603acc } diff --git a/Source/WebCore/Modules/speech/cocoa/WebSpeechRecognizerTask.mm b/Source/WebCore/Modules/speech/cocoa/WebSpeechRecognizerTask.mm -index b2b0391c120d527a9ab4bc6daf8bff7ea5d03cf7..d490a95f89f21536fce4f403b86399160abefc23 100644 +index ba3ef589f1930904ffbdae7158fd59db13961cde..e227e731c8a1b6e4c2f2d5501ec1cfe0a5174710 100644 --- a/Source/WebCore/Modules/speech/cocoa/WebSpeechRecognizerTask.mm +++ b/Source/WebCore/Modules/speech/cocoa/WebSpeechRecognizerTask.mm -@@ -195,6 +195,7 @@ - (void)sendEndIfNeeded +@@ -197,6 +197,7 @@ - (void)sendEndIfNeeded - (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidChange:(BOOL)available { @@ -2255,7 +2249,7 @@ index b2b0391c120d527a9ab4bc6daf8bff7ea5d03cf7..d490a95f89f21536fce4f403b8639916 ASSERT(isMainThread()); if (available || !_task) -@@ -208,6 +209,7 @@ - (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidC +@@ -210,6 +211,7 @@ - (void)speechRecognizer:(SFSpeechRecognizer *)speechRecognizer availabilityDidC - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didHypothesizeTranscription:(SFTranscription *)transcription { @@ -2263,7 +2257,7 @@ index b2b0391c120d527a9ab4bc6daf8bff7ea5d03cf7..d490a95f89f21536fce4f403b8639916 ASSERT(isMainThread()); [self sendSpeechStartIfNeeded]; -@@ -216,6 +218,7 @@ - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didHypothesizeTran +@@ -218,6 +220,7 @@ - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didHypothesizeTran - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didFinishRecognition:(SFSpeechRecognitionResult *)recognitionResult { @@ -2271,7 +2265,7 @@ index b2b0391c120d527a9ab4bc6daf8bff7ea5d03cf7..d490a95f89f21536fce4f403b8639916 ASSERT(isMainThread()); if (task.state == SFSpeechRecognitionTaskStateCanceling || (!_doMultipleRecognitions && task.state == SFSpeechRecognitionTaskStateCompleted)) -@@ -229,6 +232,7 @@ - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didFinishRecogniti +@@ -231,6 +234,7 @@ - (void)speechRecognitionTask:(SFSpeechRecognitionTask *)task didFinishRecogniti - (void)speechRecognitionTaskWasCancelled:(SFSpeechRecognitionTask *)task { @@ -2279,11 +2273,23 @@ index b2b0391c120d527a9ab4bc6daf8bff7ea5d03cf7..d490a95f89f21536fce4f403b8639916 ASSERT(isMainThread()); [self sendSpeechEndIfNeeded]; +diff --git a/Source/WebCore/PlatformWin.cmake b/Source/WebCore/PlatformWin.cmake +index 72b2846f2c82818fc9a64fd90b7cba0c0601e15f..22277ab6c3233f040852d9daf9becf7ba81d12ca 100644 +--- a/Source/WebCore/PlatformWin.cmake ++++ b/Source/WebCore/PlatformWin.cmake +@@ -217,6 +217,7 @@ if (USE_CAIRO) + platform/graphics/win/cairo/MediaPlayerPrivateMediaFoundationCairo.cpp + + platform/win/cairo/DragImageWinCairo.cpp ++ platform/win/DragImageWin.cpp + ) + elseif (USE_SKIA) + list(APPEND WebCore_SOURCES diff --git a/Source/WebCore/SourcesCocoa.txt b/Source/WebCore/SourcesCocoa.txt -index 06a9accfc8e6c46493733663b5d76b07fc80db22..4946d012d166c84b25d4d954266c4dc528f7d8ad 100644 +index f09f2e709ee13060d25d4419c05affd987e6328f..16a6491a03dc6aeaf5bfdb4c5b75c3bd6dcd6944 100644 --- a/Source/WebCore/SourcesCocoa.txt +++ b/Source/WebCore/SourcesCocoa.txt -@@ -734,3 +734,9 @@ testing/cocoa/WebViewVisualIdentificationOverlay.mm +@@ -732,3 +732,9 @@ testing/cocoa/WebViewVisualIdentificationOverlay.mm platform/graphics/angle/GraphicsContextGLANGLE.cpp @no-unify platform/graphics/cocoa/GraphicsContextGLCocoa.mm @no-unify platform/graphics/cv/GraphicsContextGLCVCocoa.mm @no-unify @@ -2294,13 +2300,13 @@ index 06a9accfc8e6c46493733663b5d76b07fc80db22..4946d012d166c84b25d4d954266c4dc5 +JSTouchList.cpp +// Playwright end diff --git a/Source/WebCore/SourcesGTK.txt b/Source/WebCore/SourcesGTK.txt -index 2b22eb2071e32741cb1383601466e537dca917f2..4da3cb8c1f1247bbc9886b060cd2d53047ca6572 100644 +index 393fc726c204fc3037287d67cc6b7aa9a5a9c4c3..7e45bf78bb7f0688cf3e09449728ad32e2be3870 100644 --- a/Source/WebCore/SourcesGTK.txt +++ b/Source/WebCore/SourcesGTK.txt -@@ -112,3 +112,10 @@ platform/unix/LoggingUnix.cpp - platform/unix/SharedMemoryUnix.cpp - +@@ -113,3 +113,10 @@ platform/unix/SharedMemoryUnix.cpp platform/xdg/MIMETypeRegistryXdg.cpp + + platform/xr/openxr/PlatformXROpenXR.cpp + +// Playwright: begin. +JSSpeechSynthesisErrorCode.cpp @@ -2309,7 +2315,7 @@ index 2b22eb2071e32741cb1383601466e537dca917f2..4da3cb8c1f1247bbc9886b060cd2d530 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/SourcesWPE.txt b/Source/WebCore/SourcesWPE.txt -index ce3cf51287e5891289bd23580084b8137ee4276b..c46e4e0c6faaca888e3ea62afd0e16d6f4cfda35 100644 +index f5fe901c57ba84d42c8ee3be47c404069ac6fed8..599a40b75fdb63306f706dbb7ff845b9e29a473a 100644 --- a/Source/WebCore/SourcesWPE.txt +++ b/Source/WebCore/SourcesWPE.txt @@ -48,6 +48,8 @@ editing/glib/WebContentReaderGLib.cpp @@ -2321,14 +2327,13 @@ index ce3cf51287e5891289bd23580084b8137ee4276b..c46e4e0c6faaca888e3ea62afd0e16d6 page/linux/ResourceUsageOverlayLinux.cpp page/linux/ResourceUsageThreadLinux.cpp -@@ -97,3 +99,13 @@ platform/wpe/PasteboardWPE.cpp - platform/wpe/PlatformScreenWPE.cpp - +@@ -99,3 +101,12 @@ platform/wpe/PlatformScreenWPE.cpp platform/xdg/MIMETypeRegistryXdg.cpp + + platform/xr/openxr/PlatformXROpenXR.cpp + +// Playwright: begin. +platform/wpe/DragDataWPE.cpp -+platform/wpe/DragImageWPE.cpp + +JSSpeechSynthesisErrorCode.cpp +JSSpeechSynthesisErrorEvent.cpp @@ -2336,12 +2341,12 @@ index ce3cf51287e5891289bd23580084b8137ee4276b..c46e4e0c6faaca888e3ea62afd0e16d6 +JSSpeechSynthesisEventInit.cpp +// Playwright: end. diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904d279a0ba 100644 +index 612c2492b160e840f942b1b5bef3faf995f411cf..221ffcfc32818d1d2839aa984eefc43f60d2ba31 100644 --- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj +++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj -@@ -6452,6 +6452,13 @@ - EE0C7E042CE845CB0043DAF8 /* CSSPositionTryRule.h in Headers */ = {isa = PBXBuildFile; fileRef = EE0C7E002CE845CB0043DAF8 /* CSSPositionTryRule.h */; }; - EE0D3C492D2F4B4C00072978 /* StageModeOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = EE0D3C482D2F4AE600072978 /* StageModeOperations.h */; settings = {ATTRIBUTES = (Private, ); }; }; +@@ -6503,6 +6503,13 @@ + EE62BD9D2DE12C1B006C9A05 /* ResolvedScopedName.h in Headers */ = {isa = PBXBuildFile; fileRef = EE62BD9B2DE12BD4006C9A05 /* ResolvedScopedName.h */; settings = {ATTRIBUTES = (Private, ); }; }; + EEE349082DE0061C00A7D4BB /* StyleScopeIdentifier.h in Headers */ = {isa = PBXBuildFile; fileRef = EEE349072DE005FC00A7D4BB /* StyleScopeIdentifier.h */; settings = {ATTRIBUTES = (Private, ); }; }; EFCC6C8F20FE914400A2321B /* CanvasActivityRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F050E16823AC9C080011CE47 /* PlatformTouchEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = F050E16623AC9C070011CE47 /* PlatformTouchEvent.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F050E16A23AD660C0011CE47 /* Touch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F050E16923AD660C0011CE47 /* Touch.cpp */; }; @@ -2353,8 +2358,8 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 F12171F616A8CF0B000053CA /* WebVTTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F12171F416A8BC63000053CA /* WebVTTElement.h */; }; F32BDCD92363AACA0073B6AE /* UserGestureEmulationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */; }; F344C7141125B82C00F26EEE /* InspectorFrontendClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */; settings = {ATTRIBUTES = (Private, ); }; }; -@@ -21141,6 +21148,14 @@ - EE7A169F2C607BFA0057B563 /* StartViewTransitionOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartViewTransitionOptions.h; sourceTree = ""; }; +@@ -21257,6 +21264,14 @@ + EEE349072DE005FC00A7D4BB /* StyleScopeIdentifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StyleScopeIdentifier.h; sourceTree = ""; }; EFB7287B2124C73D005C2558 /* CanvasActivityRecord.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CanvasActivityRecord.cpp; sourceTree = ""; }; EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CanvasActivityRecord.h; sourceTree = ""; }; + F050E16623AC9C070011CE47 /* PlatformTouchEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformTouchEvent.h; sourceTree = ""; }; @@ -2368,7 +2373,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 F12171F316A8BC63000053CA /* WebVTTElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTElement.cpp; sourceTree = ""; }; F12171F416A8BC63000053CA /* WebVTTElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTElement.h; sourceTree = ""; }; F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserGestureEmulationScope.cpp; sourceTree = ""; }; -@@ -28928,6 +28943,11 @@ +@@ -29046,6 +29061,11 @@ BC4A5324256055590028C592 /* TextDirectionSubmenuInclusionBehavior.h */, 2D4F96F11A1ECC240098BF88 /* TextIndicator.cpp */, 2D4F96F21A1ECC240098BF88 /* TextIndicator.h */, @@ -2380,7 +2385,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 F48570A42644C76D00C05F71 /* TranslationContextMenuInfo.h */, F4E1965F21F26E4E00285078 /* UndoItem.cpp */, 2ECDBAD521D8906300F00ECD /* UndoItem.h */, -@@ -35916,6 +35936,8 @@ +@@ -36194,6 +36214,8 @@ 29E4D8DF16B0940F00C84704 /* PlatformSpeechSynthesizer.h */, 1AD8F81A11CAB9E900E93E54 /* PlatformStrategies.cpp */, 1AD8F81911CAB9E900E93E54 /* PlatformStrategies.h */, @@ -2389,7 +2394,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 FE3DC9932D0C063C0021B6FC /* PlatformTZoneImpls.cpp */, 0FD7C21D23CE41E30096D102 /* PlatformWheelEvent.cpp */, 935C476A09AC4D4F00A6AAB4 /* PlatformWheelEvent.h */, -@@ -38777,6 +38799,7 @@ +@@ -39050,6 +39072,7 @@ AD6E71AB1668899D00320C13 /* DocumentSharedObjectPool.h */, 6BDB5DC1227BD3B800919770 /* DocumentStorageAccess.cpp */, 6BDB5DC0227BD3B800919770 /* DocumentStorageAccess.h */, @@ -2397,7 +2402,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 7CE7FA5B1EF882300060C9D6 /* DocumentTouch.cpp */, 7CE7FA591EF882300060C9D6 /* DocumentTouch.h */, A8185F3209765765005826D9 /* DocumentType.cpp */, -@@ -43729,6 +43752,8 @@ +@@ -44014,6 +44037,8 @@ F4E90A3C2B52038E002DA469 /* PlatformTextAlternatives.h in Headers */, 0F7D07331884C56C00B4AF86 /* PlatformTextTrack.h in Headers */, 074E82BB18A69F0E007EF54C /* PlatformTimeRanges.h in Headers */, @@ -2406,7 +2411,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 CDD08ABD277E542600EA3755 /* PlatformTrackConfiguration.h in Headers */, CD1F9B022700323D00617EB6 /* PlatformVideoColorPrimaries.h in Headers */, CD1F9B01270020B700617EB6 /* PlatformVideoColorSpace.h in Headers */, -@@ -45097,6 +45122,7 @@ +@@ -45423,6 +45448,7 @@ 0F54DD081881D5F5003EEDBB /* Touch.h in Headers */, 71B7EE0D21B5C6870031C1EF /* TouchAction.h in Headers */, 0F54DD091881D5F5003EEDBB /* TouchEvent.h in Headers */, @@ -2414,7 +2419,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 0F54DD0A1881D5F5003EEDBB /* TouchList.h in Headers */, 070334D71459FFD5008D8D45 /* TrackBase.h in Headers */, BE88E0C21715CE2600658D98 /* TrackListBase.h in Headers */, -@@ -46300,6 +46326,8 @@ +@@ -46627,6 +46653,8 @@ 2D22830323A8470700364B7E /* CursorMac.mm in Sources */, 5CBD59592280E926002B22AA /* CustomHeaderFields.cpp in Sources */, 07E4BDBF2A3A5FAB000D5509 /* DictationCaretAnimator.cpp in Sources */, @@ -2423,7 +2428,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 7CE6CBFD187F394900D46BF5 /* FormatConverter.cpp in Sources */, 4667EA3E2968D9DA00BAB1E2 /* GameControllerHapticEffect.mm in Sources */, 46FE73D32968E52000B8064C /* GameControllerHapticEngines.mm in Sources */, -@@ -46390,6 +46418,9 @@ +@@ -46718,6 +46746,9 @@ CE88EE262414467B007F29C2 /* TextAlternativeWithRange.mm in Sources */, BE39137129B267F500FA5D4F /* TextTransformCocoa.cpp in Sources */, 51DF6D800B92A18E00C2DC85 /* ThreadCheck.mm in Sources */, @@ -2434,7 +2439,7 @@ index e522afd2d6f038d2a2c9804313d1d8e75c63e914..03f521d898bd7d80a94e7e3c0fc4c904 538EC8021F96AF81004D22A8 /* UnifiedSource1.cpp in Sources */, 538EC8051F96AF81004D22A8 /* UnifiedSource2-mm.mm in Sources */, diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp -index 3f19caf891dfa1cc70aa630f4e82c405825ab40c..62b2978c8d5e8af5e337d8b27c1d2ad938b773fe 100644 +index 9587b90007ab8ee8ab32f7f86219732f11d457bf..81e09081c42db3c905f1adb2f917b0bca32c24c2 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityObject.cpp @@ -72,6 +72,7 @@ @@ -2445,8 +2450,8 @@ index 3f19caf891dfa1cc70aa630f4e82c405825ab40c..62b2978c8d5e8af5e337d8b27c1d2ad9 #include "LocalFrame.h" #include "LocalizedStrings.h" #include "MathMLNames.h" -@@ -3931,7 +3932,12 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const - if (roleValue() == AccessibilityRole::ApplicationDialog) +@@ -3998,7 +3999,12 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const + if (role() == AccessibilityRole::ApplicationDialog) return AccessibilityObjectInclusion::IncludeObject; - return accessibilityPlatformIncludesObject(); @@ -2460,7 +2465,7 @@ index 3f19caf891dfa1cc70aa630f4e82c405825ab40c..62b2978c8d5e8af5e337d8b27c1d2ad9 bool AccessibilityObject::isWithinHiddenWebArea() const diff --git a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h -index 158dc6af1464896ac4c4727c52581729ac132352..b8bd22a7d80d08d6dd56ea1bc10addbe3f839b6d 100644 +index c4b32aaefc75bdef3b52f2469786f9abe11fb8d4..6d84e95ff7f766a2e26c1414be92af89fa7d73cf 100644 --- a/Source/WebCore/bindings/js/WebCoreBuiltinNames.h +++ b/Source/WebCore/bindings/js/WebCoreBuiltinNames.h @@ -190,6 +190,8 @@ namespace WebCore { @@ -2473,10 +2478,10 @@ index 158dc6af1464896ac4c4727c52581729ac132352..b8bd22a7d80d08d6dd56ea1bc10addbe macro(DynamicsCompressorNode) \ macro(ElementInternals) \ diff --git a/Source/WebCore/css/query/MediaQueryFeatures.cpp b/Source/WebCore/css/query/MediaQueryFeatures.cpp -index aec43490053f95341ef979385b5e6c1daf03a090..4e44b7e341c8247f916f8597092248d8ec884518 100644 +index 5354042180f7c7cf5754142ffeeb9908166074bc..2b4d7423a3f4726c5e3ee93078184aa9b1d97649 100644 --- a/Source/WebCore/css/query/MediaQueryFeatures.cpp +++ b/Source/WebCore/css/query/MediaQueryFeatures.cpp -@@ -498,7 +498,11 @@ static const IdentifierSchema& forcedColorsFeatureSchema() +@@ -403,7 +403,11 @@ static const IdentifierSchema& forcedColorsFeatureSchema() "forced-colors"_s, FixedVector { CSSValueNone, CSSValueActive }, OptionSet(), @@ -2489,7 +2494,7 @@ index aec43490053f95341ef979385b5e6c1daf03a090..4e44b7e341c8247f916f8597092248d8 return MatchingIdentifiers { CSSValueNone }; } }; -@@ -686,6 +690,9 @@ static const IdentifierSchema& prefersReducedMotionFeatureSchema() +@@ -591,6 +595,9 @@ static const IdentifierSchema& prefersReducedMotionFeatureSchema() [](auto& context) { bool userPrefersReducedMotion = [&] { Ref frame = *context.document->frame(); @@ -2500,7 +2505,7 @@ index aec43490053f95341ef979385b5e6c1daf03a090..4e44b7e341c8247f916f8597092248d8 case ForcedAccessibilityValue::On: return true; diff --git a/Source/WebCore/dom/DataTransfer.cpp b/Source/WebCore/dom/DataTransfer.cpp -index c6f2dca0d4aede2bea015d1cca45dff434425938..e6f41af39befe69f47d0e0953f7d689c338ca184 100644 +index c0b9d058536120b4c368ab8094c16f19a5d2acba..37e7e016f5862bab19d750d06bd72b100c775353 100644 --- a/Source/WebCore/dom/DataTransfer.cpp +++ b/Source/WebCore/dom/DataTransfer.cpp @@ -523,6 +523,14 @@ Ref DataTransfer::createForDrag(const Document& document) @@ -2519,7 +2524,7 @@ index c6f2dca0d4aede2bea015d1cca45dff434425938..e6f41af39befe69f47d0e0953f7d689c { auto dataTransfer = adoptRef(*new DataTransfer(StoreMode::ReadWrite, makeUnique(), Type::DragAndDropData)); diff --git a/Source/WebCore/dom/DataTransfer.h b/Source/WebCore/dom/DataTransfer.h -index 34ef2f454a732d39acae04987584cab5638b8c60..5e7b788612718dffe3423c89d96141b5b53621fb 100644 +index 315635014cf133628d72942eb230df4e8cad036c..5424ad948028c192d9fc165cde0102e15ad65b56 100644 --- a/Source/WebCore/dom/DataTransfer.h +++ b/Source/WebCore/dom/DataTransfer.h @@ -92,6 +92,9 @@ public: @@ -2729,7 +2734,7 @@ index d0a3d5c048647b07772e1581c76c4eb60ecf41b0..bec324636991079264e620c0dfdaf984 #endif // USE(LIBWPE) diff --git a/Source/WebCore/html/FileInputType.cpp b/Source/WebCore/html/FileInputType.cpp -index d5234a678a77e24ce0a95fe08740416ab8acbadb..c9a42fd354e88c5a8c3ee53974442ec9d4cb1224 100644 +index 64137520fb575aac59fbb4a8fc8509cbdd8cc04e..5bdac5378686cefef1e05f4aa01a49729c3fd398 100644 --- a/Source/WebCore/html/FileInputType.cpp +++ b/Source/WebCore/html/FileInputType.cpp @@ -38,6 +38,7 @@ @@ -2753,7 +2758,7 @@ index d5234a678a77e24ce0a95fe08740416ab8acbadb..c9a42fd354e88c5a8c3ee53974442ec9 return; diff --git a/Source/WebCore/inspector/InspectorController.cpp b/Source/WebCore/inspector/InspectorController.cpp -index 48efc5356601313e907846283f753e44d6a0f983..f153fd7da52da98d033e4070d79c64d4a38ff197 100644 +index af0482d2d06db91a7964b7662dd3379804062fff..49ed24c301cecabeb21ac5f3d06d2777218cd538 100644 --- a/Source/WebCore/inspector/InspectorController.cpp +++ b/Source/WebCore/inspector/InspectorController.cpp @@ -296,6 +296,8 @@ void InspectorController::disconnectFrontend(FrontendChannel& frontendChannel) @@ -2821,7 +2826,7 @@ index 48efc5356601313e907846283f753e44d6a0f983..f153fd7da52da98d033e4070d79c64d4 + } // namespace WebCore diff --git a/Source/WebCore/inspector/InspectorController.h b/Source/WebCore/inspector/InspectorController.h -index 4f5c1e836876710a554455ec53733f72db63de58..774c4a66af84664a7a83ba0790d147f7bbb4236a 100644 +index d56afdb6e48840802c5a4f28b9a954695f09965d..7c3223c9f57bc45f4109325433ac4f5e745eb986 100644 --- a/Source/WebCore/inspector/InspectorController.h +++ b/Source/WebCore/inspector/InspectorController.h @@ -106,6 +106,12 @@ public: @@ -2844,7 +2849,7 @@ index 4f5c1e836876710a554455ec53733f72db63de58..774c4a66af84664a7a83ba0790d147f7 + void runLoopWhilePaused(); WeakRef m_page; - Ref m_instrumentingAgents; + const Ref m_instrumentingAgents; @@ -159,6 +166,7 @@ private: bool m_isAutomaticInspection { false }; bool m_pauseAfterInitialization = { false }; @@ -3374,7 +3379,7 @@ index c028341e84e59a6b1b16107fd74feb21f70b12ab..d385418ac34e8f315f201801a2c65226 + } diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp -index 94492280fc724f299655a5df34492f929d82c57c..af0aafa701a9c697650aad2d86ddcf6b3022dcc5 100644 +index 08bdd6e6cd5295f4be4df37c3e3e830b03916dc7..078ef814e006a45c4b83ff1c2ff341a93705e1a0 100644 --- a/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.cpp @@ -54,6 +54,7 @@ @@ -3743,7 +3748,7 @@ index 94492280fc724f299655a5df34492f929d82c57c..af0aafa701a9c697650aad2d86ddcf6b + } // namespace WebCore diff --git a/Source/WebCore/inspector/agents/InspectorDOMAgent.h b/Source/WebCore/inspector/agents/InspectorDOMAgent.h -index 3496e0037572430b993ea91704501a742dd15627..9eb0ce0094fdb2ab9ca94bb5cddb082f201bf4cf 100644 +index 185ba93a265317bf876cc5fedd5f3d5cf3a8757e..664fc4f47f0540e5b84d96ba4c6a530d44aacf25 100644 --- a/Source/WebCore/inspector/agents/InspectorDOMAgent.h +++ b/Source/WebCore/inspector/agents/InspectorDOMAgent.h @@ -59,6 +59,7 @@ namespace WebCore { @@ -3816,7 +3821,7 @@ index 3496e0037572430b993ea91704501a742dd15627..9eb0ce0094fdb2ab9ca94bb5cddb082f void discardBindings(); diff --git a/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp b/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp -index f3a0bdf189aed455ceb84f4023c2dbfda8e5dc16..25d54fc33be2ccd42565cb7ddf879344ab8aa3f2 100644 +index dfb469de2c5dcaa39b03ef6c188eda34d869401c..4d1fb3b02d63c8b425b50b92c25f334334e8ac86 100644 --- a/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp @@ -58,6 +58,7 @@ @@ -3933,7 +3938,7 @@ index f3a0bdf189aed455ceb84f4023c2dbfda8e5dc16..25d54fc33be2ccd42565cb7ddf879344 { return startsWithLettersIgnoringASCIICase(mimeType, "text/"_s) diff --git a/Source/WebCore/inspector/agents/InspectorNetworkAgent.h b/Source/WebCore/inspector/agents/InspectorNetworkAgent.h -index de6b2dd844943074c5a383c7b9b8ccba1c96419a..7a3404f2380b5e62f1c0523a70f8ef442014759d 100644 +index 66da07b880259dd1388703cb6c8c7b10d63c32d7..ff00bc16dd84f34056fe0b36896c0049d8fa9a49 100644 --- a/Source/WebCore/inspector/agents/InspectorNetworkAgent.h +++ b/Source/WebCore/inspector/agents/InspectorNetworkAgent.h @@ -35,6 +35,8 @@ @@ -3970,7 +3975,7 @@ index de6b2dd844943074c5a383c7b9b8ccba1c96419a..7a3404f2380b5e62f1c0523a70f8ef44 } // namespace WebCore diff --git a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp b/Source/WebCore/inspector/agents/InspectorPageAgent.cpp -index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff89720232 100644 +index 7ec43a15440452915980d0eca565f25b8c6880e9..868a6279fd57ddc3f880eeef18d0559e19a9056d 100644 --- a/Source/WebCore/inspector/agents/InspectorPageAgent.cpp +++ b/Source/WebCore/inspector/agents/InspectorPageAgent.cpp @@ -32,19 +32,27 @@ @@ -4053,7 +4058,7 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff { if (buffer.data()) { @@ -348,6 +375,7 @@ InspectorPageAgent::InspectorPageAgent(PageAgentContext& context, InspectorClien - , m_frontendDispatcher(makeUnique(context.frontendRouter)) + , m_frontendDispatcher(makeUniqueRef(context.frontendRouter)) , m_backendDispatcher(Inspector::PageBackendDispatcher::create(context.backendDispatcher, this)) , m_inspectedPage(context.inspectedPage) + , m_injectedScriptManager(context.injectedScriptManager) @@ -4457,7 +4462,7 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff +Protocol::ErrorStringOr InspectorPageAgent::insertText(const String& text) +{ + UserGestureIndicator indicator { IsProcessingUserGesture::Yes }; -+ RefPtr frame = m_inspectedPage->checkedFocusController()->focusedOrMainFrame(); ++ RefPtr frame = m_inspectedPage->focusController().focusedOrMainFrame(); + if (!frame) + return { }; + @@ -4475,7 +4480,7 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff + String computedRoleString = axObject->computedRoleString(); + if (!computedRoleString.isEmpty()) + return computedRoleString; -+ AccessibilityRole role = axObject->roleValue(); ++ AccessibilityRole role = axObject->role(); + switch(role) { + case AccessibilityRole::Application: + return "Application"_s; @@ -4547,8 +4552,6 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff + return "Feed"_s; + case AccessibilityRole::Figure: + return "Figure"_s; -+ case AccessibilityRole::Footer: -+ return "Footer"_s; + case AccessibilityRole::Footnote: + return "Footnote"_s; + case AccessibilityRole::Form: @@ -4669,6 +4672,10 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff + return "ScrollBar"_s; + case AccessibilityRole::SearchField: + return "SearchField"_s; ++ case AccessibilityRole::SectionFooter: ++ return "SectionFooter"_s; ++ case AccessibilityRole::SectionHeader: ++ return "SectionHeader"_s; + case AccessibilityRole::Slider: + return "Slider"_s; + case AccessibilityRole::SliderThumb: @@ -4743,8 +4750,6 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff + return "WebApplication"_s; + case AccessibilityRole::WebArea: + return "WebArea"_s; -+ case AccessibilityRole::WebCoreLink: -+ return "WebCoreLink"_s; + }; + return "Unknown"_s; +} @@ -4947,7 +4952,7 @@ index cc649f41559f905102d7a8785190f3157d3e6dea..ee7fee8a4ac224d27dd40d04be0803ff } // namespace WebCore diff --git a/Source/WebCore/inspector/agents/InspectorPageAgent.h b/Source/WebCore/inspector/agents/InspectorPageAgent.h -index 7daa8d1d5c96afe1829aa21ccb8ed1b8ebcc3861..da57245795d7cb287daaaaf5d09a523c0f88fcc7 100644 +index 169a3023f68227a6ab6538dc6b1271882154deb9..b300be1ff6f278a79601613f76266c06c5ac92a8 100644 --- a/Source/WebCore/inspector/agents/InspectorPageAgent.h +++ b/Source/WebCore/inspector/agents/InspectorPageAgent.h @@ -32,8 +32,10 @@ @@ -5073,7 +5078,7 @@ index 7daa8d1d5c96afe1829aa21ccb8ed1b8ebcc3861..da57245795d7cb287daaaaf5d09a523c Ref protectedOverlay() const; @@ -173,17 +205,22 @@ private: - RefPtr m_backendDispatcher; + const Ref m_backendDispatcher; WeakRef m_inspectedPage; + Inspector::InjectedScriptManager& m_injectedScriptManager; @@ -5097,7 +5102,7 @@ index 7daa8d1d5c96afe1829aa21ccb8ed1b8ebcc3861..da57245795d7cb287daaaaf5d09a523c } // namespace WebCore diff --git a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp -index 0cc351959aaa96bd427ecbf75dd2ba51e730fc30..fb2b6c842a2e2b819ce746403e33550c0d4e2440 100644 +index 2f591841f3db6618a2936802d5e4e9604cd25963..639bf063a684a361e71b04c7a17c4dfc2541513a 100644 --- a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp +++ b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp @@ -34,6 +34,7 @@ @@ -5229,7 +5234,7 @@ index 0cc351959aaa96bd427ecbf75dd2ba51e730fc30..fb2b6c842a2e2b819ce746403e33550c // Always send the main world first. diff --git a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h -index ab49ddd13fc6e1ed967cf501afb0230c1e795159..5ea038212cedc1a7b250588b616225edad6f643c 100644 +index 6544b61b15956b218e97814b0ceb7250809f8ed3..c6657031bcd27573533a34be62e0ea0d8441a7e4 100644 --- a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h +++ b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h @@ -38,6 +38,7 @@ @@ -5263,7 +5268,7 @@ index ab49ddd13fc6e1ed967cf501afb0230c1e795159..5ea038212cedc1a7b250588b616225ed } // namespace WebCore diff --git a/Source/WebCore/loader/CookieJar.h b/Source/WebCore/loader/CookieJar.h -index 8fb27c1045b8073d1487d5b61ccdec23a395bfd1..5008052f587ca4ba90da973c539188deb9551621 100644 +index 5dbca10d65d0426d90c743b789e18c3786025817..56b1aa222343649be7c28e104e6ae1dde598e59f 100644 --- a/Source/WebCore/loader/CookieJar.h +++ b/Source/WebCore/loader/CookieJar.h @@ -48,6 +48,7 @@ class NetworkStorageSession; @@ -5285,10 +5290,10 @@ index 8fb27c1045b8073d1487d5b61ccdec23a395bfd1..5008052f587ca4ba90da973c539188de protected: static SameSiteInfo sameSiteInfo(const Document&, IsForDOMCookieAccess = IsForDOMCookieAccess::No); diff --git a/Source/WebCore/loader/DocumentLoader.cpp b/Source/WebCore/loader/DocumentLoader.cpp -index 541963eb00a1e5700da683be152543ac57f177a1..bd0debb9cdf2fac02bcf0243571a4a668be9decc 100644 +index c63569ec7dbb402d261f16a5b5febdf428cfe749..b5cff81dcf8fbcc5a0a8cb6f05857f4382b090be 100644 --- a/Source/WebCore/loader/DocumentLoader.cpp +++ b/Source/WebCore/loader/DocumentLoader.cpp -@@ -774,8 +774,10 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc +@@ -775,8 +775,10 @@ void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const Resourc if (!didReceiveRedirectResponse) return completionHandler(WTFMove(newRequest)); @@ -5299,7 +5304,7 @@ index 541963eb00a1e5700da683be152543ac57f177a1..bd0debb9cdf2fac02bcf0243571a4a66 switch (navigationPolicyDecision) { case NavigationPolicyDecision::IgnoreLoad: case NavigationPolicyDecision::LoadWillContinueInAnotherProcess: -@@ -1572,11 +1574,17 @@ void DocumentLoader::detachFromFrame(LoadWillContinueInAnotherProcess loadWillCo +@@ -1592,11 +1594,17 @@ void DocumentLoader::detachFromFrame(LoadWillContinueInAnotherProcess loadWillCo if (auto navigationID = std::exchange(m_navigationID, { })) frame->loader().client().documentLoaderDetached(*navigationID, loadWillContinueInAnotherProcess); @@ -5320,10 +5325,10 @@ index 541963eb00a1e5700da683be152543ac57f177a1..bd0debb9cdf2fac02bcf0243571a4a66 { m_navigationID = navigationID; diff --git a/Source/WebCore/loader/DocumentLoader.h b/Source/WebCore/loader/DocumentLoader.h -index a3e5891cc44903ec74da16164904c12673498d97..9f8566a8c1cc20c1d2a495c9765f1c522890e993 100644 +index 061494ea45af99d0516cf16af3f11c25a180e970..eff67613ea81446d7f53ecf2e66d5b428b4c6bab 100644 --- a/Source/WebCore/loader/DocumentLoader.h +++ b/Source/WebCore/loader/DocumentLoader.h -@@ -218,6 +218,8 @@ public: +@@ -220,6 +220,8 @@ public: WEBCORE_EXPORT virtual void detachFromFrame(LoadWillContinueInAnotherProcess); @@ -5333,10 +5338,10 @@ index a3e5891cc44903ec74da16164904c12673498d97..9f8566a8c1cc20c1d2a495c9765f1c52 WEBCORE_EXPORT RefPtr protectedFrameLoader() const; WEBCORE_EXPORT SubresourceLoader* mainResourceLoader() const; diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp -index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc63ee9326 100644 +index 1263d20c197bc21cb5a4098a07493943fd2830e4..fbcfaa000c4095c9ba9257dfb2229424229ee670 100644 --- a/Source/WebCore/loader/FrameLoader.cpp +++ b/Source/WebCore/loader/FrameLoader.cpp -@@ -1325,6 +1325,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat +@@ -1332,6 +1332,7 @@ void FrameLoader::loadInSameDocument(URL url, RefPtr stat } m_client->dispatchDidNavigateWithinPage(); @@ -5344,7 +5349,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc document->statePopped(stateObject ? stateObject.releaseNonNull() : SerializedScriptValue::nullValue()); m_client->dispatchDidPopStateWithinPage(); -@@ -1867,6 +1868,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t +@@ -1874,6 +1875,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t const String& httpMethod = loader->request().httpMethod(); if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) { @@ -5352,7 +5357,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc RefPtr oldDocumentLoader = m_documentLoader; NavigationAction action { frame->protectedDocument().releaseNonNull(), loader->request(), InitiatedByMainFrame::Unknown, loader->isRequestFromClientOrUserInput(), policyChecker().loadType(), isFormSubmission }; -@@ -1905,7 +1907,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t +@@ -1912,7 +1914,9 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t auto policyDecisionMode = loader->triggeringAction().isFromNavigationAPI() ? PolicyDecisionMode::Synchronous : PolicyDecisionMode::Asynchronous; RELEASE_ASSERT(!isBackForwardLoadType(policyChecker().loadType()) || history().provisionalItem()); @@ -5362,7 +5367,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc continueLoadAfterNavigationPolicy(request, RefPtr { weakFormState.get() }.get(), navigationPolicyDecision, allowNavigationToInvalidURL); completionHandler(); }, policyDecisionMode); -@@ -3219,10 +3223,15 @@ String FrameLoader::userAgent(const URL& url) const +@@ -3238,10 +3242,15 @@ String FrameLoader::userAgent(const URL& url) const String FrameLoader::navigatorPlatform() const { @@ -5380,7 +5385,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc } void FrameLoader::dispatchOnloadEvents() -@@ -3682,6 +3691,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, LoadWill +@@ -3702,6 +3711,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, LoadWill checkCompleted(); if (frame->page()) checkLoadComplete(loadWillContinueInAnotherProcess); @@ -5389,7 +5394,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc } void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, const SecurityOrigin* requesterOrigin, bool shouldContinue, NavigationHistoryBehavior historyHandling) -@@ -4576,9 +4587,6 @@ String FrameLoader::referrer() const +@@ -4591,9 +4602,6 @@ String FrameLoader::referrer() const void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() { @@ -5399,7 +5404,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc Vector> worlds; ScriptController::getAllWorlds(worlds); for (auto& world : worlds) -@@ -4588,13 +4596,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() +@@ -4603,13 +4611,12 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world) { Ref frame = m_frame.get(); @@ -5420,7 +5425,7 @@ index 0974e98f02b59150df7054af753a3c3bb1d3b0e9..559a8532162d4c9e106c1059dfbf50fc InspectorInstrumentation::didClearWindowObjectInWorld(frame, world); } diff --git a/Source/WebCore/loader/LoaderStrategy.h b/Source/WebCore/loader/LoaderStrategy.h -index 1b34dfdd2a8e56beab49591a3517aba02c510ee6..768b895c132b73d935198cbfc2126a227a656b46 100644 +index a33d00c3ce3a861f8746e58b01ea51e5777cb2ea..9cf20e3c47c7a0bb9db72b9385431cc3df48df86 100644 --- a/Source/WebCore/loader/LoaderStrategy.h +++ b/Source/WebCore/loader/LoaderStrategy.h @@ -86,6 +86,7 @@ public: @@ -5432,10 +5437,10 @@ index 1b34dfdd2a8e56beab49591a3517aba02c510ee6..768b895c132b73d935198cbfc2126a22 virtual bool shouldPerformSecurityChecks() const { return false; } virtual bool havePerformedSecurityChecks(const ResourceResponse&) const { return false; } diff --git a/Source/WebCore/loader/NavigationScheduler.cpp b/Source/WebCore/loader/NavigationScheduler.cpp -index d1d57f421c8570468b8c1ad2dc3c081b50cc4f0a..17a75616518d81eb3ce1e8107ee1f3546b477e30 100644 +index 350f729ee5dc95ba3dd7018542db1dbfb00b382e..f8d31dfc86c0cbc10b474e1773ddbf682b3c9b66 100644 --- a/Source/WebCore/loader/NavigationScheduler.cpp +++ b/Source/WebCore/loader/NavigationScheduler.cpp -@@ -806,7 +806,7 @@ void NavigationScheduler::startTimer() +@@ -803,7 +803,7 @@ void NavigationScheduler::startTimer() Seconds delay = 1_s * m_redirect->delay(); m_timer.startOneShot(delay); @@ -5445,7 +5450,7 @@ index d1d57f421c8570468b8c1ad2dc3c081b50cc4f0a..17a75616518d81eb3ce1e8107ee1f354 } diff --git a/Source/WebCore/loader/ProgressTracker.cpp b/Source/WebCore/loader/ProgressTracker.cpp -index a1554c16a0b836bcf87fea0f4e7831d9e0d7baf0..354e4fef6de9c77b7cad96cc428056ae4e2ff067 100644 +index 1832114445abe51041c241f40df9731d6e0a2f8d..3213517c4cd0b235ff12c098e34389987e6ea090 100644 --- a/Source/WebCore/loader/ProgressTracker.cpp +++ b/Source/WebCore/loader/ProgressTracker.cpp @@ -163,6 +163,8 @@ void ProgressTracker::progressCompleted(LocalFrame& frame) @@ -5460,17 +5465,17 @@ index a1554c16a0b836bcf87fea0f4e7831d9e0d7baf0..354e4fef6de9c77b7cad96cc428056ae @@ -189,8 +191,6 @@ void ProgressTracker::finalProgressComplete() m_client->progressFinished(*frame); protectedPage()->progressFinished(*frame); - frame->protectedLoader()->loadProgressingStatusChanged(); + frame->loader().loadProgressingStatusChanged(); - - InspectorInstrumentation::frameStoppedLoading(*frame); } } diff --git a/Source/WebCore/loader/cache/CachedResourceLoader.cpp b/Source/WebCore/loader/cache/CachedResourceLoader.cpp -index c6a8c86e12ba98ad8b9aab74430523c3dd8cb42e..61c73db33d4c629803d14f243862acea15e762cb 100644 +index b78b91913be6fd14efc7621e251bb5adab05c315..b7f3252e4f201c2c44bca48997f20a3205301486 100644 --- a/Source/WebCore/loader/cache/CachedResourceLoader.cpp +++ b/Source/WebCore/loader/cache/CachedResourceLoader.cpp -@@ -1173,8 +1173,11 @@ ResourceErrorOr> CachedResourceLoader::requ +@@ -1180,8 +1180,11 @@ ResourceErrorOr> CachedResourceLoader::requ request.updateReferrerPolicy(document ? document->referrerPolicy() : ReferrerPolicy::Default); @@ -5484,7 +5489,7 @@ index c6a8c86e12ba98ad8b9aab74430523c3dd8cb42e..61c73db33d4c629803d14f243862acea if (RefPtr documentLoader = m_documentLoader.get()) { bool madeHTTPS { request.resourceRequest().wasSchemeOptimisticallyUpgraded() }; -@@ -1810,8 +1813,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const +@@ -1816,8 +1819,9 @@ Vector> CachedResourceLoader::allCachedSVGImages() const ResourceErrorOr> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request) { @@ -5497,7 +5502,7 @@ index c6a8c86e12ba98ad8b9aab74430523c3dd8cb42e..61c73db33d4c629803d14f243862acea RefPtr document = m_document.get(); ASSERT(document); diff --git a/Source/WebCore/page/ChromeClient.h b/Source/WebCore/page/ChromeClient.h -index 60ba4d0aaa365301aa80d144f2d07e842a71a6b8..a962ce78e3ad912886a9d3328d64cf0faec3177d 100644 +index 7f6d70366b7841b4e5215a3b8709554fe995ba64..342de0c124714355bd5beaea78310500a9c184b7 100644 --- a/Source/WebCore/page/ChromeClient.h +++ b/Source/WebCore/page/ChromeClient.h @@ -379,7 +379,7 @@ public: @@ -5510,10 +5515,10 @@ index 60ba4d0aaa365301aa80d144f2d07e842a71a6b8..a962ce78e3ad912886a9d3328d64cf0f virtual RefPtr createColorChooser(ColorChooserClient&, const Color&) = 0; diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp -index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171e8b30b2c 100644 +index d501516df133e721bc2dcf580b4dd1533b3368f3..63fac9ac1a69d522ed733dae33945ca6b685e4e4 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp -@@ -4483,6 +4483,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr +@@ -4515,6 +4515,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr if (!document) return false; @@ -5526,7 +5531,7 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 dragState().dataTransfer = DataTransfer::createForDrag(*document); auto hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No; -@@ -5077,6 +5083,7 @@ static HitTestResult hitTestResultInFrame(LocalFrame* frame, const LayoutPoint& +@@ -5119,6 +5125,7 @@ static HitTestResult hitTestResultInFrame(LocalFrame* frame, const LayoutPoint& return result; } @@ -5534,7 +5539,7 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 Expected EventHandler::handleTouchEvent(const PlatformTouchEvent& event) { Ref frame = m_frame.get(); -@@ -5150,7 +5157,7 @@ Expected EventHandler::handleTouchEvent(co +@@ -5192,7 +5199,7 @@ Expected EventHandler::handleTouchEvent(co // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap. unsigned touchPointTargetKey = point.id() + 1; @@ -5543,7 +5548,7 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 bool pointerCancelled = false; #endif RefPtr touchTarget; -@@ -5197,7 +5204,7 @@ Expected EventHandler::handleTouchEvent(co +@@ -5239,7 +5246,7 @@ Expected EventHandler::handleTouchEvent(co // we also remove it from the map. touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey); @@ -5552,7 +5557,7 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 HitTestResult result = hitTestResultAtPoint(pagePoint, hitType | HitTestRequest::Type::AllowChildFrameContent); pointerTarget = result.targetElement(); pointerCancelled = (pointerTarget != touchTarget); -@@ -5220,7 +5227,7 @@ Expected EventHandler::handleTouchEvent(co +@@ -5262,7 +5269,7 @@ Expected EventHandler::handleTouchEvent(co if (!targetFrame) continue; @@ -5561,7 +5566,7 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 // FIXME: WPE currently does not send touch stationary events, so create a naive TouchReleased PlatformTouchPoint // on release if the hit test result changed since the previous TouchPressed or TouchMoved if (pointState == PlatformTouchPoint::TouchReleased && pointerCancelled) { -@@ -5310,6 +5317,7 @@ Expected EventHandler::handleTouchEvent(co +@@ -5352,6 +5359,7 @@ Expected EventHandler::handleTouchEvent(co return swallowedEvent; } @@ -5570,10 +5575,10 @@ index 04fce4219bdfc1c4a78eb2d849045280416b6f2d..9e44a302b10ba3bc18496f5678262171 #if ENABLE(TOUCH_EVENTS) diff --git a/Source/WebCore/page/FocusController.cpp b/Source/WebCore/page/FocusController.cpp -index 1b183750e1c51e96fcca727bed3c2eb1d30e77e9..e1d00211b5051387bd4495d88cef637704cf1704 100644 +index 79636a0a8528bfd4e86d8373f9ab7e96e9ac8346..f2a2ebeb6a7e41a3900d35dabde88f9fd7c4ae82 100644 --- a/Source/WebCore/page/FocusController.cpp +++ b/Source/WebCore/page/FocusController.cpp -@@ -580,13 +580,14 @@ bool FocusController::relinquishFocusToChrome(FocusDirection direction) +@@ -595,13 +595,14 @@ bool FocusController::relinquishFocusToChrome(FocusDirection direction) return false; Ref page = m_page.get(); @@ -5625,7 +5630,7 @@ index 3474800864049dcbe6c84746c4216e72a68fa48f..8f5d536921eeb41c77fe689c036dcb77 } diff --git a/Source/WebCore/page/FrameSnapshotting.h b/Source/WebCore/page/FrameSnapshotting.h -index 713cb9c3c59bbad23ef0b821ab27b3d669a7dea8..12077ebf0978b6b52e6af4602767c09d9f3cd9de 100644 +index fd9df9d9564fe29c64342fbf77082ad595612e90..af5687c79e2a5be20cde653107e5827c1d8981e5 100644 --- a/Source/WebCore/page/FrameSnapshotting.h +++ b/Source/WebCore/page/FrameSnapshotting.h @@ -58,6 +58,7 @@ enum class SnapshotFlags : uint16_t { @@ -5637,7 +5642,7 @@ index 713cb9c3c59bbad23ef0b821ab27b3d669a7dea8..12077ebf0978b6b52e6af4602767c09d struct SnapshotOptions { diff --git a/Source/WebCore/page/History.cpp b/Source/WebCore/page/History.cpp -index adc6185687b6fcee3a49819d0149cde24a4485bf..a33e5db46933c514abce86e3f264de9b7e7c20d9 100644 +index f2dfb5966f445ee52b4430e65e0accb7e41bb913..1d25db7e91beaaea3c083f315ec3f21b317f271f 100644 --- a/Source/WebCore/page/History.cpp +++ b/Source/WebCore/page/History.cpp @@ -32,6 +32,7 @@ @@ -5658,7 +5663,7 @@ index adc6185687b6fcee3a49819d0149cde24a4485bf..a33e5db46933c514abce86e3f264de9b } diff --git a/Source/WebCore/page/LocalFrame.cpp b/Source/WebCore/page/LocalFrame.cpp -index f6e26519619d560b5edddb30170f166ac76bd57f..51019ccda3f5953c4a154c271690f3923c1a530a 100644 +index 018a1445506e0b3cae8b7d4056f37bcdd21eb1dd..a1cd251e84d2faa8b58b5ebbf5077bacb1e5bf6e 100644 --- a/Source/WebCore/page/LocalFrame.cpp +++ b/Source/WebCore/page/LocalFrame.cpp @@ -42,6 +42,7 @@ @@ -5690,7 +5695,7 @@ index f6e26519619d560b5edddb30170f166ac76bd57f..51019ccda3f5953c4a154c271690f392 void LocalFrame::init() { + InspectorInstrumentation::frameAttached(this); - protectedLoader()->init(); + loader().init(); } @@ -444,7 +448,7 @@ void LocalFrame::orientationChanged() @@ -6068,7 +6073,7 @@ index f6e26519619d560b5edddb30170f166ac76bd57f..51019ccda3f5953c4a154c271690f392 #undef FRAME_RELEASE_LOG_ERROR diff --git a/Source/WebCore/page/LocalFrame.h b/Source/WebCore/page/LocalFrame.h -index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5ce49370d 100644 +index 92a21d8efb29d588a7a530e823079ad299cbd40a..3a925cc017500b37abee9d2d26662f9eccc2eebc 100644 --- a/Source/WebCore/page/LocalFrame.h +++ b/Source/WebCore/page/LocalFrame.h @@ -29,6 +29,7 @@ @@ -6089,7 +6094,7 @@ index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5 class LocalFrame final : public Frame { public: -@@ -233,10 +234,6 @@ public: +@@ -229,10 +230,6 @@ public: WEBCORE_EXPORT DataDetectionResultsStorage& dataDetectionResults(); #endif @@ -6100,7 +6105,7 @@ index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5 WEBCORE_EXPORT Node* deepestNodeAtLocation(const FloatPoint& viewportLocation); WEBCORE_EXPORT Node* nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, SecurityOrigin* = nullptr); WEBCORE_EXPORT Node* nodeRespondingToDoubleClickEvent(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); -@@ -244,6 +241,10 @@ public: +@@ -240,6 +237,10 @@ public: WEBCORE_EXPORT Node* nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation); WEBCORE_EXPORT Node* approximateNodeAtViewportLocationLegacy(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); @@ -6111,7 +6116,7 @@ index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5 WEBCORE_EXPORT NSArray *wordsInCurrentParagraph() const; WEBCORE_EXPORT CGRect renderRectForPoint(CGPoint, bool* isReplaced, float* fontSize) const; -@@ -311,6 +312,7 @@ public: +@@ -307,6 +308,7 @@ public: WEBCORE_EXPORT FloatSize screenSize() const; void setOverrideScreenSize(FloatSize&&); @@ -6119,7 +6124,7 @@ index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5 void selfOnlyRef(); void selfOnlyDeref(); -@@ -382,7 +384,6 @@ private: +@@ -378,7 +380,6 @@ private: #if ENABLE(DATA_DETECTION) std::unique_ptr m_dataDetectionResults; #endif @@ -6127,19 +6132,19 @@ index 7e8505e025af3f52a0adbd5e6b98d6a22f449d9c..667a9578f073c216231ca8834354c1d5 void betterApproximateNode(const IntPoint& testPoint, const NodeQualifier&, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect); bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center); -@@ -390,6 +391,7 @@ private: +@@ -386,6 +387,7 @@ private: enum class ShouldFindRootEditableElement : bool { No, Yes }; Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, const NodeQualifier&, ShouldApproximate, ShouldFindRootEditableElement = ShouldFindRootEditableElement::Yes); +#if PLATFORM(IOS_FAMILY) void setTimersPausedInternal(bool); - UniqueRef m_viewportArguments; + const UniqueRef m_viewportArguments; diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp -index 08315ed1fb551d8ad9f9a7dcdc4fea487cd9a8fb..44cf9866c35e309cb90138b73c3d35e5409aa7eb 100644 +index 6b60173931046a6361e7f0d388c6663166236dfe..fe3175f67a82a3328e1a3c1bc74521daa792dedc 100644 --- a/Source/WebCore/page/Page.cpp +++ b/Source/WebCore/page/Page.cpp -@@ -663,6 +663,45 @@ void Page::setOverrideViewportArguments(const std::optional& +@@ -667,6 +667,45 @@ void Page::setOverrideViewportArguments(const std::optional& localTopDocument->updateViewportArguments(); } @@ -6185,7 +6190,7 @@ index 08315ed1fb551d8ad9f9a7dcdc4fea487cd9a8fb..44cf9866c35e309cb90138b73c3d35e5 ScrollingCoordinator* Page::scrollingCoordinator() { if (!m_scrollingCoordinator && m_settings->scrollingCoordinatorEnabled()) { -@@ -4242,6 +4281,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) +@@ -4292,6 +4331,26 @@ void Page::setUseDarkAppearanceOverride(std::optional valueOverride) appearanceDidChange(); } @@ -6213,10 +6218,10 @@ index 08315ed1fb551d8ad9f9a7dcdc4fea487cd9a8fb..44cf9866c35e309cb90138b73c3d35e5 { if (insets == m_fullscreenInsets) diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h -index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486edb758f6b 100644 +index cd29ee00f3499c50379f02981fe85e7bdd950d93..10b653145543c308afd3ed1497f622d7adf08e9d 100644 --- a/Source/WebCore/page/Page.h +++ b/Source/WebCore/page/Page.h -@@ -389,6 +389,9 @@ public: +@@ -391,6 +391,9 @@ public: const ViewportArguments* overrideViewportArguments() const { return m_overrideViewportArguments.get(); } WEBCORE_EXPORT void setOverrideViewportArguments(const std::optional&); @@ -6226,7 +6231,7 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e static void refreshPlugins(bool reload); WEBCORE_EXPORT PluginData& pluginData(); WEBCORE_EXPORT Ref protectedPluginData(); -@@ -486,6 +489,10 @@ public: +@@ -488,6 +491,10 @@ public: #if ENABLE(DRAG_SUPPORT) DragController& dragController() { return m_dragController.get(); } const DragController& dragController() const { return m_dragController.get(); } @@ -6235,8 +6240,8 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e + const String& overrideDragPasteboardName() { return m_overrideDragPasteboardName; } +#endif #endif - FocusController& focusController() const { return *m_focusController; } - WEBCORE_EXPORT CheckedRef checkedFocusController() const; + FocusController& focusController() const { return m_focusController; } + #if ENABLE(CONTEXT_MENUS) @@ -671,6 +678,10 @@ public: WEBCORE_EXPORT void setUseColorAppearance(bool useDarkAppearance, bool useElevatedUserInterfaceLevel); bool defaultUseDarkAppearance() const { return m_useDarkAppearance; } @@ -6248,7 +6253,7 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e #if ENABLE(TEXT_AUTOSIZING) float textAutosizingWidth() const { return m_textAutosizingWidth; } -@@ -1143,6 +1154,11 @@ public: +@@ -1148,6 +1159,11 @@ public: WEBCORE_EXPORT void setInteractionRegionsEnabled(bool); #endif @@ -6260,7 +6265,7 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e #if ENABLE(DEVICE_ORIENTATION) && PLATFORM(IOS_FAMILY) DeviceOrientationUpdateProvider* deviceOrientationUpdateProvider() const { return m_deviceOrientationUpdateProvider.get(); } #endif -@@ -1424,6 +1440,9 @@ private: +@@ -1437,6 +1453,9 @@ private: #if ENABLE(DRAG_SUPPORT) const UniqueRef m_dragController; @@ -6268,9 +6273,9 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e + String m_overrideDragPasteboardName; +#endif #endif - std::unique_ptr m_focusController; + const UniqueRef m_focusController; #if ENABLE(CONTEXT_MENUS) -@@ -1502,6 +1521,8 @@ private: +@@ -1515,6 +1534,8 @@ private: bool m_useElevatedUserInterfaceLevel { false }; bool m_useDarkAppearance { false }; std::optional m_useDarkAppearanceOverride; @@ -6279,7 +6284,7 @@ index 9f107570fbb2195211ad46358369acb791e0376b..8c753ef756e9fe64ffdca36683ec486e #if ENABLE(TEXT_AUTOSIZING) float m_textAutosizingWidth { 0 }; -@@ -1683,6 +1704,11 @@ private: +@@ -1696,6 +1717,11 @@ private: #endif std::unique_ptr m_overrideViewportArguments; @@ -6318,7 +6323,7 @@ index 153fc36199f26adbfb61cbef6744ffe31a68b951..cc667e06700013fd5e994467e19536d2 Ref protectedPage() const; diff --git a/Source/WebCore/page/PointerCaptureController.cpp b/Source/WebCore/page/PointerCaptureController.cpp -index 7f0fe2766310d43552d12b85d1484cc095a752a4..1ab97c02215826ad9210489b64573145755a1b4f 100644 +index f01c803f6afa4e2949378bc9a758af4a55681dc5..9d35c28b4ff837f4333ac50bc917513bbf2b5773 100644 --- a/Source/WebCore/page/PointerCaptureController.cpp +++ b/Source/WebCore/page/PointerCaptureController.cpp @@ -207,7 +207,7 @@ bool PointerCaptureController::preventsCompatibilityMouseEventsForIdentifier(Poi @@ -6340,7 +6345,7 @@ index 7f0fe2766310d43552d12b85d1484cc095a752a4..1ab97c02215826ad9210489b64573145 #endif diff --git a/Source/WebCore/page/PointerCaptureController.h b/Source/WebCore/page/PointerCaptureController.h -index e50f38c2c45f665b4b88fe1aa4f99d1a51328d63..8144be1724ef71a93b8870f64ec5abcb98eaa27f 100644 +index 2c3ae18820bd2be191dbd8d6bd513c0302a1f580..21538527135e659493ee812aa61c943ae37c0b74 100644 --- a/Source/WebCore/page/PointerCaptureController.h +++ b/Source/WebCore/page/PointerCaptureController.h @@ -60,7 +60,7 @@ public: @@ -6368,7 +6373,7 @@ index e50f38c2c45f665b4b88fe1aa4f99d1a51328d63..8144be1724ef71a93b8870f64ec5abcb #endif ; diff --git a/Source/WebCore/page/Screen.cpp b/Source/WebCore/page/Screen.cpp -index 24ed7c019bea4df52f2883db0e40bdbc2dc74ebd..a788f534d9e0e8124153c7f380b4fdb232c51a1a 100644 +index c45965ee6b7fe6c066871b87d7883e639a849adb..16c3a5b027222d1de5bb540c71c0b6aa107c4657 100644 --- a/Source/WebCore/page/Screen.cpp +++ b/Source/WebCore/page/Screen.cpp @@ -124,6 +124,9 @@ int Screen::availLeft() const @@ -6412,7 +6417,7 @@ index 24ed7c019bea4df52f2883db0e40bdbc2dc74ebd..a788f534d9e0e8124153c7f380b4fdb2 } diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp -index e8cf919fd1d0147fce390aa5750843a6bce40190..fb2d56e3a3b6901f993544e297ad678c469a9cc1 100644 +index 5ba13e25cbe12ea5c7e543a3e9377f662cbfe50e..b5ab59f41db851cb03a56c9dfaf76e47182f8e85 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp @@ -374,6 +374,8 @@ template @@ -6529,7 +6534,7 @@ index 0000000000000000000000000000000000000000..803239911006cfb3b03ea911c003f2d2 + +} diff --git a/Source/WebCore/platform/DragData.h b/Source/WebCore/platform/DragData.h -index 2ebb3221cb3818f20abc7b7c3307ea7644f49d49..e0bffb6f5adee0b0a202340f633c2def1955b384 100644 +index f76eab27bd22db72e23cd53d98fe721f0d7e48b7..9757103efe88c0fed3b68671c3042faa29b31433 100644 --- a/Source/WebCore/platform/DragData.h +++ b/Source/WebCore/platform/DragData.h @@ -47,7 +47,7 @@ typedef void* DragDataRef; @@ -6561,32 +6566,6 @@ index 2ebb3221cb3818f20abc7b7c3307ea7644f49d49..e0bffb6f5adee0b0a202340f633c2def #endif bool m_disallowFileAccess { false }; }; -diff --git a/Source/WebCore/platform/DragImage.cpp b/Source/WebCore/platform/DragImage.cpp -index 41b4b3ca89d0df2ccba562d83c33097539b5ae1c..071bee507b730b89796420222b9787bd411a19ad 100644 ---- a/Source/WebCore/platform/DragImage.cpp -+++ b/Source/WebCore/platform/DragImage.cpp -@@ -280,7 +280,7 @@ DragImage::~DragImage() - deleteDragImage(m_dragImageRef); - } - --#if !PLATFORM(COCOA) && !PLATFORM(GTK) && !PLATFORM(WIN) -+#if !PLATFORM(COCOA) && !PLATFORM(GTK) && !PLATFORM(WIN) && !PLATFORM(WPE) - - IntSize dragImageSize(DragImageRef) - { -diff --git a/Source/WebCore/platform/DragImage.h b/Source/WebCore/platform/DragImage.h -index 77286d8e715825c9c7c0d329480fc0fc497fe561..5ea53eb8b7e53dd5a5950c65a38c1d23bfe46681 100644 ---- a/Source/WebCore/platform/DragImage.h -+++ b/Source/WebCore/platform/DragImage.h -@@ -60,7 +60,7 @@ class Node; - typedef RetainPtr DragImageRef; - #elif PLATFORM(MAC) - typedef RetainPtr DragImageRef; --#elif PLATFORM(WIN) -+#elif PLATFORM(WIN) && USE(CAIRO) - typedef HBITMAP DragImageRef; - #elif USE(CAIRO) - typedef RefPtr DragImageRef; diff --git a/Source/WebCore/platform/Pasteboard.h b/Source/WebCore/platform/Pasteboard.h index f8de8815d483bd3ac684c018159c593798c0495a..e84ce8eec7424fbc3456623289588b1832d9fb7f 100644 --- a/Source/WebCore/platform/Pasteboard.h @@ -6653,7 +6632,7 @@ index f8de8815d483bd3ac684c018159c593798c0495a..e84ce8eec7424fbc3456623289588b18 }; diff --git a/Source/WebCore/platform/PlatformKeyboardEvent.h b/Source/WebCore/platform/PlatformKeyboardEvent.h -index 63ffd6ca32c3baee03db2a9419c4f7e9de45388a..c60c7a8d1f110472117c8c4e969fd05fef71f908 100644 +index 241c4f1cc29a854e8329ba5227b7aaf0cca8138e..0a2a487dd91ddeec37fb996ecc6039fc70c4ba8d 100644 --- a/Source/WebCore/platform/PlatformKeyboardEvent.h +++ b/Source/WebCore/platform/PlatformKeyboardEvent.h @@ -135,6 +135,7 @@ namespace WebCore { @@ -6702,10 +6681,10 @@ index ef0abc9a93e878897ffc9d2497a3da0fca5b37b7..abd96c6d1a6c3ab9e0121c1e78f2f75a +} // namespace WebCore +#endif diff --git a/Source/WebCore/platform/PlatformScreen.h b/Source/WebCore/platform/PlatformScreen.h -index 82a54ac2de2ddf4650e4b48db129fe25f6562264..d144ba15d18d2e892c25988dab27636135353a99 100644 +index 26ca6a098bf39cff7e9a6505e84dbdbbd2aafc24..63d4fa7e39689a47269ae369bcf335642af6e669 100644 --- a/Source/WebCore/platform/PlatformScreen.h +++ b/Source/WebCore/platform/PlatformScreen.h -@@ -160,10 +160,14 @@ WEBCORE_EXPORT float screenScaleFactor(UIScreen * = nullptr); +@@ -165,10 +165,14 @@ WEBCORE_EXPORT float screenScaleFactor(UIScreen * = nullptr); #endif #if ENABLE(TOUCH_EVENTS) @@ -6773,6 +6752,24 @@ index 492c5e76290c2379cda40b9663f5f67ff8f66360..096752985edf39960eb4be6eb733ebe3 static const unsigned scrollbarBorderSize = 1; static const unsigned thumbBorderSize = 1; static const unsigned overlayThumbSize = 3; +diff --git a/Source/WebCore/platform/graphics/ImageAdapter.h b/Source/WebCore/platform/graphics/ImageAdapter.h +index f5d16bcb2d300d6e54b90583a4e9489862a7dfd0..e9c945854a011e9ec1de96cc8dfbc08ef13a91e0 100644 +--- a/Source/WebCore/platform/graphics/ImageAdapter.h ++++ b/Source/WebCore/platform/graphics/ImageAdapter.h +@@ -61,11 +61,12 @@ typedef struct HBITMAP__ *HBITMAP; + #include + #endif + ++#include "NativeImage.h" ++ + namespace WebCore { + + class Image; + class IntSize; +-class NativeImage; + + class ImageAdapter { + WTF_MAKE_TZONE_ALLOCATED(ImageAdapter); diff --git a/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h b/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h index 5b659c763b9754b025a63f89522954cc39915b9a..448b50a2b131361a75d3f816cdcbb6a102551280 100644 --- a/Source/WebCore/platform/graphics/cg/ImageBufferUtilitiesCG.h @@ -6787,7 +6784,7 @@ index 5b659c763b9754b025a63f89522954cc39915b9a..448b50a2b131361a75d3f816cdcbb6a1 Vector encodeData(std::span, const String& mimeType, std::optional quality); diff --git a/Source/WebCore/platform/graphics/filters/software/FEComponentTransferSoftwareApplier.h b/Source/WebCore/platform/graphics/filters/software/FEComponentTransferSoftwareApplier.h -index c7542b821af8c87660e10b0c07b360cfcc8e28a2..a3cc131ff0410ae31df30c115dd9fd26aec5adf8 100644 +index 515ddea3cd42796efa9f41ad74be07a7447c337e..36db42e2a0822d5609b39046191f05a1f8d2b54b 100644 --- a/Source/WebCore/platform/graphics/filters/software/FEComponentTransferSoftwareApplier.h +++ b/Source/WebCore/platform/graphics/filters/software/FEComponentTransferSoftwareApplier.h @@ -23,6 +23,7 @@ @@ -6799,7 +6796,7 @@ index c7542b821af8c87660e10b0c07b360cfcc8e28a2..a3cc131ff0410ae31df30c115dd9fd26 namespace WebCore { diff --git a/Source/WebCore/platform/graphics/win/ComplexTextControllerUniscribe.cpp b/Source/WebCore/platform/graphics/win/ComplexTextControllerUniscribe.cpp -index 78ea08023ebd5f1b41b06cd843b6dce4ee80dd50..55fed94b774f033b4f75e2dee85dc27cf2e2689e 100644 +index 6239b3ab731ffc0826216ddda46d951d8dace5a0..270423347baa7e6fa47ad1da2154c10423037ae2 100644 --- a/Source/WebCore/platform/graphics/win/ComplexTextControllerUniscribe.cpp +++ b/Source/WebCore/platform/graphics/win/ComplexTextControllerUniscribe.cpp @@ -168,6 +168,33 @@ static Vector stringIndicesFromClusters(const Vector& clusters, @@ -6846,7 +6843,7 @@ index 78ea08023ebd5f1b41b06cd843b6dce4ee80dd50..55fed94b774f033b4f75e2dee85dc27c // Determine the string for this item. const UChar* str = cp.data() + items[i].iCharPos; diff --git a/Source/WebCore/platform/gtk/PlatformKeyboardEventGtk.cpp b/Source/WebCore/platform/gtk/PlatformKeyboardEventGtk.cpp -index 2b7d3dc70fdfec767d8caa13966c4051ee73aead..7fa8e155bef817c837042c8d6c4dcb84864232bc 100644 +index b3d305bfa5694f074dba36a0ca11bf3f13e80579..05faaf5417e768c958cf6a9807dfcd1f4c455a8d 100644 --- a/Source/WebCore/platform/gtk/PlatformKeyboardEventGtk.cpp +++ b/Source/WebCore/platform/gtk/PlatformKeyboardEventGtk.cpp @@ -37,8 +37,10 @@ @@ -7108,7 +7105,7 @@ index 2b7d3dc70fdfec767d8caa13966c4051ee73aead..7fa8e155bef817c837042c8d6c4dcb84 { switch (val) { diff --git a/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp b/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp -index 1339241aae69b657ffa40b603eea992c45db0e65..9194ff5f8308e5cf22fb250723ae4161142da45f 100644 +index 98d93889a1b97450fde21cee53e45111b622dca7..9362518e8351eb8fe5b30a2d7875417bb3da770d 100644 --- a/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp +++ b/Source/WebCore/platform/gtk/PlatformScreenGtk.cpp @@ -121,7 +121,7 @@ bool screenSupportsExtendedColor(Widget*) @@ -7502,6 +7499,18 @@ index 80c20938d5ec8282485fb0152b96304c83124354..22975b7c1b99357a7689747251410f30 #include #include #include +diff --git a/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/RealtimeOutgoingAudioSourceLibWebRTC.h b/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/RealtimeOutgoingAudioSourceLibWebRTC.h +index 95d0e3412831763a80c0c2337e84c29cdc849e71..58d5685a9ae1083b96e687a249d96c5c5be827db 100644 +--- a/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/RealtimeOutgoingAudioSourceLibWebRTC.h ++++ b/Source/WebCore/platform/mediastream/libwebrtc/gstreamer/RealtimeOutgoingAudioSourceLibWebRTC.h +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + diff --git a/Source/WebCore/platform/network/HTTPHeaderMap.cpp b/Source/WebCore/platform/network/HTTPHeaderMap.cpp index 1178c8fb001994bc9e6166376a367d9bc148913c..fcc6534568cad6b42a819a435f84ba2b9baae6f8 100644 --- a/Source/WebCore/platform/network/HTTPHeaderMap.cpp @@ -7520,7 +7529,7 @@ index 1178c8fb001994bc9e6166376a367d9bc148913c..fcc6534568cad6b42a819a435f84ba2b m_commonHeaders.append(CommonHeader { name, value }); } diff --git a/Source/WebCore/platform/network/NetworkStorageSession.h b/Source/WebCore/platform/network/NetworkStorageSession.h -index c1e50549e674e1620ad0515a061376ba728e75df..7e6e323436d9b1a87dbf4d6fa275629f531e3ce8 100644 +index 2216400912348ab0b6978a40fe02bfc12b249deb..2a0720873e26158c37d7ede83d9a1fd7a02f4409 100644 --- a/Source/WebCore/platform/network/NetworkStorageSession.h +++ b/Source/WebCore/platform/network/NetworkStorageSession.h @@ -198,6 +198,7 @@ public: @@ -7532,7 +7541,7 @@ index c1e50549e674e1620ad0515a061376ba728e75df..7e6e323436d9b1a87dbf4d6fa275629f WEBCORE_EXPORT HTTPCookieAcceptPolicy cookieAcceptPolicy() const; WEBCORE_EXPORT void setCookie(const Cookie&); diff --git a/Source/WebCore/platform/network/ResourceResponseBase.cpp b/Source/WebCore/platform/network/ResourceResponseBase.cpp -index a4ed6a5f1182b43432f6082ffc10b62473c248e5..05113dbf615171a0b18f8998411e603076bdc15b 100644 +index a3eefa06801c54642ce6ec1c3bc7675b491ccf5c..46a8197a447b4eba385eb6b89b4a949147955bbe 100644 --- a/Source/WebCore/platform/network/ResourceResponseBase.cpp +++ b/Source/WebCore/platform/network/ResourceResponseBase.cpp @@ -78,6 +78,7 @@ ResourceResponseBase::ResourceResponseBase(std::optional&& @@ -7572,7 +7581,7 @@ index a4ed6a5f1182b43432f6082ffc10b62473c248e5..05113dbf615171a0b18f8998411e6030 *source, *type, diff --git a/Source/WebCore/platform/network/ResourceResponseBase.h b/Source/WebCore/platform/network/ResourceResponseBase.h -index 9e2bc7f05c5700c5f86bfaf03f13e2308ed097ed..01bae357d25b8dfef4c0d2cdb87cbc7a3895b416 100644 +index 545befb0776b62d546b7b7a28093e6f1c4478d2c..a12b31f99f380ea5fbd9b82916e397cab8b6298c 100644 --- a/Source/WebCore/platform/network/ResourceResponseBase.h +++ b/Source/WebCore/platform/network/ResourceResponseBase.h @@ -256,6 +256,11 @@ protected: @@ -7613,7 +7622,7 @@ index 9e2bc7f05c5700c5f86bfaf03f13e2308ed097ed..01bae357d25b8dfef4c0d2cdb87cbc7a ResourceResponseBase::Source source; ResourceResponseBase::Type type; diff --git a/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm b/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm -index 4a6533db6aff0620e7122e684f1ef6c723bf7942..395f275722b93a7ab08429588fbc1fdc1ebac4f3 100644 +index 632ec5128ee28f9d5ea33bdfaa311bad56ec6396..837d67d61ba14e509cf9f72320cf56457263501b 100644 --- a/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm +++ b/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm @@ -553,6 +553,22 @@ bool NetworkStorageSession::setCookieFromDOM(const URL& firstParty, const SameSi @@ -7653,7 +7662,7 @@ index 37e129136c69b27d509acc01f10be42a8a1fe35a..9df0babc8f82372925fddf2211a7c8c9 bool m_detectedDatabaseCorruption { false }; diff --git a/Source/WebCore/platform/network/curl/NetworkStorageSessionCurl.cpp b/Source/WebCore/platform/network/curl/NetworkStorageSessionCurl.cpp -index 96289d8ae2e4feb60a91fab3f5cf1fc27b9e7c87..8c0b62c44a18571d1f3ea1ed81d59a0aae28d3f1 100644 +index 78584cd0fb75884e96d489d9da6a24f6ddedbeca..ebbb8c3e9a469d11e7433ac6ef85300e5b6d2ad4 100644 --- a/Source/WebCore/platform/network/curl/NetworkStorageSessionCurl.cpp +++ b/Source/WebCore/platform/network/curl/NetworkStorageSessionCurl.cpp @@ -136,6 +136,12 @@ void NetworkStorageSession::setCookieAcceptPolicy(CookieAcceptPolicy policy) con @@ -7670,7 +7679,7 @@ index 96289d8ae2e4feb60a91fab3f5cf1fc27b9e7c87..8c0b62c44a18571d1f3ea1ed81d59a0a { switch (cookieDatabase().acceptPolicy()) { diff --git a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp -index 274d671614fdca8f425adecb405d5f2402e92f8b..1b19e79e10f0768cfdd66b0fc1b1855388c9f559 100644 +index 5b3deb017487d8362e11479ea814e02e9221fd23..cb829266402ce26b193a62f7fa39d83681302a1e 100644 --- a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp +++ b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp @@ -551,6 +551,26 @@ void NetworkStorageSession::replaceCookies(const Vector& cookies) @@ -7701,7 +7710,7 @@ index 274d671614fdca8f425adecb405d5f2402e92f8b..1b19e79e10f0768cfdd66b0fc1b18553 { GUniquePtr targetCookie(cookie.toSoupCookie()); diff --git a/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp -index bbcc12d58f7b5df3462c93617f6ef19eef403cf2..b255b05da89248a99ff11965ceae840ef45b9fab 100644 +index 72e1784eff239b69db25a598934a671e7aead036..d647b0d8510edeed5aa27d780a06dd7e5cb413dd 100644 --- a/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp +++ b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp @@ -40,6 +40,7 @@ @@ -7725,7 +7734,7 @@ index bbcc12d58f7b5df3462c93617f6ef19eef403cf2..b255b05da89248a99ff11965ceae840e ReleaseStgMedium(&store); } diff --git a/Source/WebCore/platform/win/ClipboardUtilitiesWin.h b/Source/WebCore/platform/win/ClipboardUtilitiesWin.h -index c3ffc7392c0b7fa099a7dd4e4be977cdee1c803c..9570dbb0f2c42ca38598a8898183c9b310f858ab 100644 +index 61624f0c555886e11a82e933aa89a093b5273693..4471b458941d3394d99f85e983cf0d86e1a562d3 100644 --- a/Source/WebCore/platform/win/ClipboardUtilitiesWin.h +++ b/Source/WebCore/platform/win/ClipboardUtilitiesWin.h @@ -34,6 +34,7 @@ namespace WebCore { @@ -7764,69 +7773,6 @@ index 0379437d84807e4a8d3846afac5ec8a70e743e70..5b0461bf12535d4900ffaddc2a878262 { if (!m_dragDataMap.isEmpty() || !m_platformDragData) return m_dragDataMap; -diff --git a/Source/WebCore/platform/win/DragImageWin.cpp b/Source/WebCore/platform/win/DragImageWin.cpp -index dd24e15115aeff41f0f4452a9cac292d75bc0d5d..8df467c008bdb3de59d301b14c1c20b8bb0b6a41 100644 ---- a/Source/WebCore/platform/win/DragImageWin.cpp -+++ b/Source/WebCore/platform/win/DragImageWin.cpp -@@ -62,16 +62,22 @@ IntSize dragImageSize(DragImageRef image) - { - if (!image) - return IntSize(); -- BITMAP b; -- GetObject(image, sizeof(BITMAP), &b); -- return IntSize(b.bmWidth, b.bmHeight); -+ return { image->width(), image->height() }; - } - -+#if USE(CAIRO) - void deleteDragImage(DragImageRef image) - { - if (image) - ::DeleteObject(image); - } -+#else -+void deleteDragImage(DragImageRef) -+{ -+ // Since this is a RefPtr, there's nothing additional we need to do to -+ // delete it. It will be released when it falls out of scope. -+} -+#endif - - DragImageRef dissolveDragImageToFraction(DragImageRef image, float) - { -@@ -79,8 +85,9 @@ DragImageRef dissolveDragImageToFraction(DragImageRef image, float) - return image; - } - --DragImageRef createDragImageIconForCachedImageFilename(const String& filename) -+DragImageRef createDragImageIconForCachedImageFilename(const String&) - { -+#if USE(CAIRO) - SHFILEINFO shfi { }; - auto fname = filename.wideCharacters(); - if (FAILED(SHGetFileInfo(fname.data(), FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES))) -@@ -96,6 +103,9 @@ DragImageRef createDragImageIconForCachedImageFilename(const String& filename) - DeleteObject(iconInfo.hbmMask); - - return iconInfo.hbmColor; -+#else -+ return nullptr; -+#endif - } - - #if USE(CAIRO) -@@ -221,9 +231,9 @@ DragImageRef createDragImageForColor(const Color&, const FloatRect&, float, Path - } - - #if USE(SKIA) --DragImageRef createDragImageFromImage(Image*, ImageOrientation, GraphicsClient*, float) -+DragImageRef createDragImageFromImage(Image* image, ImageOrientation, GraphicsClient*, float) - { -- return nullptr; -+ return image->currentNativeImage()->platformImage(); - } - - DragImageRef scaleDragImage(DragImageRef, FloatSize) diff --git a/Source/WebCore/platform/win/KeyEventWin.cpp b/Source/WebCore/platform/win/KeyEventWin.cpp index d450bf9d0fd1f0bf8f28db483ac9d3d60fa9d114..72a59403a0b5493aea4a8e28eb15eac24b652b09 100644 --- a/Source/WebCore/platform/win/KeyEventWin.cpp @@ -7852,7 +7798,7 @@ index d450bf9d0fd1f0bf8f28db483ac9d3d60fa9d114..72a59403a0b5493aea4a8e28eb15eac2 OptionSet PlatformKeyboardEvent::currentStateOfModifierKeys() diff --git a/Source/WebCore/platform/win/PasteboardWin.cpp b/Source/WebCore/platform/win/PasteboardWin.cpp -index 7987d1cd71b05ba4cf09eaf0a8b0d55da6bcffb0..5edaa06c1a648402dc0c3224e69c4721a234aa06 100644 +index e862b1201f827c58d0f6971bd1071e0c0d34d44d..3166f7a8dcb1f9c2d2d505eee846c17b370e76a7 100644 --- a/Source/WebCore/platform/win/PasteboardWin.cpp +++ b/Source/WebCore/platform/win/PasteboardWin.cpp @@ -1144,7 +1144,21 @@ void Pasteboard::writeCustomData(const Vector& data) @@ -8001,86 +7947,6 @@ index 0000000000000000000000000000000000000000..fbd32d390129129fd5b213f7f9c3e96b +} + +} -diff --git a/Source/WebCore/platform/wpe/DragImageWPE.cpp b/Source/WebCore/platform/wpe/DragImageWPE.cpp -new file mode 100644 -index 0000000000000000000000000000000000000000..4383ede4974fb2b938aa01f2f19eb5e0c47f208c ---- /dev/null -+++ b/Source/WebCore/platform/wpe/DragImageWPE.cpp -@@ -0,0 +1,74 @@ -+/* -+ * Copyright (C) 2010,2017 Igalia S.L. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#include "config.h" -+#include "DragImage.h" -+#include "NativeImage.h" -+#include "NotImplemented.h" -+ -+#include "Image.h" -+ -+namespace WebCore { -+ -+IntSize dragImageSize(DragImageRef) -+{ -+ notImplemented(); -+ return { 0, 0 }; -+} -+ -+void deleteDragImage(DragImageRef) -+{ -+ notImplemented(); -+} -+ -+DragImageRef scaleDragImage(DragImageRef, FloatSize) -+{ -+ notImplemented(); -+ return nullptr; -+} -+ -+DragImageRef dissolveDragImageToFraction(DragImageRef image, float) -+{ -+ notImplemented(); -+ return image; -+} -+ -+DragImageRef createDragImageFromImage(Image* image, ImageOrientation, GraphicsClient*, float) -+{ -+ return image->currentNativeImage()->platformImage(); -+} -+ -+ -+DragImageRef createDragImageIconForCachedImageFilename(const String&) -+{ -+ notImplemented(); -+ return nullptr; -+} -+ -+DragImageRef createDragImageForLink(Element&, URL&, const String&, TextIndicatorData&, float) -+{ -+ notImplemented(); -+ return nullptr; -+} -+ -+DragImageRef createDragImageForColor(const Color&, const FloatRect&, float, Path&) -+{ -+ return nullptr; -+} -+ -+} diff --git a/Source/WebCore/platform/wpe/PasteboardWPE.cpp b/Source/WebCore/platform/wpe/PasteboardWPE.cpp index c0847a84e4aeba3dac78a8ffe9826d906d33a387..c1c60572473dad33e436ab4f52e5cac5bc2d2f76 100644 --- a/Source/WebCore/platform/wpe/PasteboardWPE.cpp @@ -8280,7 +8146,7 @@ index c0847a84e4aeba3dac78a8ffe9826d906d33a387..c1c60572473dad33e436ab4f52e5cac5 #endif // PLATFORM(WPE) diff --git a/Source/WebCore/rendering/RenderTextControl.cpp b/Source/WebCore/rendering/RenderTextControl.cpp -index f8240cb9f855d0c35268d8c0c8fc2a0b985f4be0..3586db4ccf16a4a6bd174c09fc75915730c45ae2 100644 +index 1238b6ac1b2aa43f0b1f7a74e48e9436d67934fa..261ee362bbdc4fb70450a61da3c872ac3d3a8cc7 100644 --- a/Source/WebCore/rendering/RenderTextControl.cpp +++ b/Source/WebCore/rendering/RenderTextControl.cpp @@ -230,13 +230,13 @@ void RenderTextControl::layoutExcludedChildren(RelayoutChildren relayoutChildren @@ -8297,24 +8163,23 @@ index f8240cb9f855d0c35268d8c0c8fc2a0b985f4be0..3586db4ccf16a4a6bd174c09fc759157 +#if PLATFORM(IOS_FAMILY) int RenderTextControl::innerLineHeight() const { - auto innerText = innerTextElement(); + if (auto innerTextElement = this->innerTextElement(); innerTextElement && innerTextElement->renderer()) diff --git a/Source/WebCore/rendering/RenderTextControl.h b/Source/WebCore/rendering/RenderTextControl.h -index faf34133df0bf205072145ffbab8163b93d3c874..fdc4554952e0e33f8827bb3d00c827dec966ad15 100644 +index 82e32c398dfdfa3e1fdb5dda86e98c827a290f84..c9eddb5739b1cd2ab5b744f57ecb8eb01079ad55 100644 --- a/Source/WebCore/rendering/RenderTextControl.h +++ b/Source/WebCore/rendering/RenderTextControl.h -@@ -38,9 +38,9 @@ public: +@@ -38,8 +38,8 @@ public: WEBCORE_EXPORT HTMLTextFormControlElement& textFormControlElement() const; WEBCORE_EXPORT Ref protectedTextFormControlElement() const; -#if PLATFORM(IOS_FAMILY) bool canScroll() const; - +#if PLATFORM(IOS_FAMILY) - // Returns the line height of the inner renderer. - int innerLineHeight() const override; + WEBCORE_EXPORT int innerLineHeight() const; #endif + diff --git a/Source/WebCore/workers/WorkerConsoleClient.cpp b/Source/WebCore/workers/WorkerConsoleClient.cpp -index 7546c0266801803c9f73179e7a370a4f42a4f05e..05bb106972e648f6fe98aaa1f471390e3a98c674 100644 +index e675901b3a15cbdefdb731c949f8a4234af5dede..97d38d8ef0fb379ab689cb2dacba6563bcaf1e85 100644 --- a/Source/WebCore/workers/WorkerConsoleClient.cpp +++ b/Source/WebCore/workers/WorkerConsoleClient.cpp @@ -124,4 +124,6 @@ void WorkerConsoleClient::recordEnd(JSC::JSGlobalObject*, Ref&& @@ -8337,7 +8202,7 @@ index db95c8273bd0deb3f903a45d02fc07bbbd8ab305..bf88228b4c838b90d11d430cc9429d51 WorkerOrWorkletGlobalScope& m_globalScope; }; diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp -index 23a9f5b5afdb7aba0efdc6a1db7898abc641b0fa..220ed1f59582562268f02262ea2f7ed3388aab1f 100644 +index 2d8a4462ea7897aca4455d526fcb5e0fd6b3dcd3..53f0de10cdfb27736855fa837d9e4a650c18c90a 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp @@ -97,6 +97,8 @@ @@ -8349,7 +8214,7 @@ index 23a9f5b5afdb7aba0efdc6a1db7898abc641b0fa..220ed1f59582562268f02262ea2f7ed3 #endif #if ENABLE(APPLE_PAY_REMOTE_UI) -@@ -1237,6 +1239,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) +@@ -1232,6 +1234,14 @@ void NetworkConnectionToWebProcess::clearPageSpecificData(PageIdentifier pageID) storageSession->clearPageSpecificDataForResourceLoadStatistics(pageID); } @@ -8363,9 +8228,9 @@ index 23a9f5b5afdb7aba0efdc6a1db7898abc641b0fa..220ed1f59582562268f02262ea2f7ed3 + void NetworkConnectionToWebProcess::removeStorageAccessForFrame(FrameIdentifier frameID, PageIdentifier pageID) { - if (auto* storageSession = protectedNetworkProcess()->storageSession(m_sessionID)) + if (CheckedPtr storageSession = m_networkProcess->storageSession(m_sessionID)) diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h -index 3f1539237c6c8d1cd832cd3ece2ba20939e01a41..1259c36e38d46c0ebfdf98be20f217433f831ca5 100644 +index 96e3d7d1370b114591191dea8e0e042c9f06c0c8..a4ddbd6483b8bd1ffb8f360cad5552dbcbcfc018 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.h @@ -388,6 +388,8 @@ private: @@ -8378,7 +8243,7 @@ index 3f1539237c6c8d1cd832cd3ece2ba20939e01a41..1259c36e38d46c0ebfdf98be20f21743 void logUserInteraction(RegistrableDomain&&); diff --git a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in -index b10706aafd037a2b92a68b0d2c474ec2e42cd2fe..f80792c02d880dbd61849233fdbc348f1eeffb33 100644 +index 26d43eb49dc956a104782a5e10df084dc2b1e7e8..aa92cfd23b61eae7ee90c9fb7846189f9406e379 100644 --- a/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.messages.in @@ -80,6 +80,8 @@ messages -> NetworkConnectionToWebProcess WantsDispatchMessage { @@ -8391,10 +8256,10 @@ index b10706aafd037a2b92a68b0d2c474ec2e42cd2fe..f80792c02d880dbd61849233fdbc348f LogUserInteraction(WebCore::RegistrableDomain domain) ResourceLoadStatisticsUpdated(Vector statistics) -> () diff --git a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm -index 00a6ce04cae578c8ac9d934e6c4e38041e4a4aee..ab587ef45e27bddf3e31d28882794f7fb11dea0d 100644 +index 23fbdde0494dae83161c9b715f870db48b8caff1..df27897d278c7bb755378ef309376b50d79124cb 100644 --- a/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm +++ b/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm -@@ -1148,6 +1148,14 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data +@@ -1136,6 +1136,14 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data resourceResponse.setDeprecatedNetworkLoadMetrics(WebCore::copyTimingData(taskMetrics.get(), networkDataTask->networkLoadMetrics())); resourceResponse.setProxyName(WTFMove(proxyName)); @@ -8410,7 +8275,7 @@ index 00a6ce04cae578c8ac9d934e6c4e38041e4a4aee..ab587ef45e27bddf3e31d28882794f7f #if !LOG_DISABLED LOG(NetworkSession, "%llu didReceiveResponse completionHandler (%s)", taskIdentifier, toString(policyAction).characters()); diff --git a/Source/WebKit/NetworkProcess/curl/NetworkDataTaskCurl.cpp b/Source/WebKit/NetworkProcess/curl/NetworkDataTaskCurl.cpp -index 975e4097fb5b9cb610ce67c70ea85e6434ec7a9d..6e2676cd751883e2eae26e38ffaefa873857b604 100644 +index 7b1879c44551fae26e338352189adc5b9add0266..dac8034d87d3bcbbb56576c2ef37713ea0ef85ff 100644 --- a/Source/WebKit/NetworkProcess/curl/NetworkDataTaskCurl.cpp +++ b/Source/WebKit/NetworkProcess/curl/NetworkDataTaskCurl.cpp @@ -166,6 +166,7 @@ void NetworkDataTaskCurl::curlDidReceiveResponse(CurlRequest& request, CurlRespo @@ -8441,7 +8306,7 @@ index aebce13abcf8f93c8fa48936120c2065f0a664b1..7b003cf0d65d0179b165fcbce775cfd5 ;; Except deny access to new-style iOS Keychain folders which are UUIDs. (deny file-read* file-write* diff --git a/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp b/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp -index 983947a9ad3abf138c1a6052807c4e86beb5c1d1..c5de2e1f1c74317ee00b9558e0e5c3093496c386 100644 +index 375489f4b3944b7b6f4ac03b1784aef8ca116206..6cce36bd75b872b45b7a6ecc07e2adff00bbc7a4 100644 --- a/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp +++ b/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp @@ -461,6 +461,8 @@ void NetworkDataTaskSoup::didSendRequest(GRefPtr&& inputStream) @@ -8454,7 +8319,7 @@ index 983947a9ad3abf138c1a6052807c4e86beb5c1d1..c5de2e1f1c74317ee00b9558e0e5c309 } diff --git a/Source/WebKit/PlatformGTK.cmake b/Source/WebKit/PlatformGTK.cmake -index 1f0116517d88a3a0fffade7288a4909bd848df88..83b9598353ad6dd9192b0c8827bba25d1cb069ad 100644 +index d39a11b312cc9237973ee095e995afa379da1b0d..e5fc9f02cea359414154756b27958019cbb57374 100644 --- a/Source/WebKit/PlatformGTK.cmake +++ b/Source/WebKit/PlatformGTK.cmake @@ -320,6 +320,9 @@ list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES @@ -8467,7 +8332,7 @@ index 1f0116517d88a3a0fffade7288a4909bd848df88..83b9598353ad6dd9192b0c8827bba25d ) list(APPEND WebKit_INTERFACE_INCLUDE_DIRECTORIES -@@ -359,6 +362,9 @@ if (USE_LIBWEBRTC) +@@ -363,6 +366,9 @@ if (USE_LIBWEBRTC) list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES "${THIRDPARTY_DIR}/libwebrtc/Source/" "${THIRDPARTY_DIR}/libwebrtc/Source/webrtc" @@ -8477,7 +8342,7 @@ index 1f0116517d88a3a0fffade7288a4909bd848df88..83b9598353ad6dd9192b0c8827bba25d ) endif () -@@ -410,6 +416,12 @@ else () +@@ -414,6 +420,12 @@ else () set(WebKitGTK_ENUM_HEADER_TEMPLATE ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitEnumTypesGtk3.h.in) endif () @@ -8491,7 +8356,7 @@ index 1f0116517d88a3a0fffade7288a4909bd848df88..83b9598353ad6dd9192b0c8827bba25d set(WebKitGTK_ENUM_GENERATION_HEADERS ${WebKitGTK_INSTALLED_HEADERS}) list(REMOVE_ITEM WebKitGTK_ENUM_GENERATION_HEADERS ${WebKitGTK_DERIVED_SOURCES_DIR}/webkit/WebKitEnumTypes.h) diff --git a/Source/WebKit/PlatformWPE.cmake b/Source/WebKit/PlatformWPE.cmake -index 56b8ece0181ad1dacd3e33ee1463ac1cc4b2ac20..fb0b4169f860e9064397216b176024760a3503cd 100644 +index 650a918189ad22a6fa76404c80ebca477331c193..74fc793279465c3aa9a586130a98c182e1334568 100644 --- a/Source/WebKit/PlatformWPE.cmake +++ b/Source/WebKit/PlatformWPE.cmake @@ -221,6 +221,7 @@ set(WPE_API_HEADER_TEMPLATES @@ -8600,7 +8465,7 @@ index 35fd0f0397cd92c5bf025d89d7f7c8139c03c69d..b1559f1bb2d2d7a74d0b534f81352e36 } // namespace WebKit diff --git a/Source/WebKit/Shared/NativeWebKeyboardEvent.h b/Source/WebKit/Shared/NativeWebKeyboardEvent.h -index c72c9733800b6f836c4d3ccb0b50d40c3ee83067..e2955ddebe388d886ca43d733dce0eb58256ce8b 100644 +index 60a6308dbbd3f9a09c82214dcd97f86dda9d2078..31036d180a6f319d17c53a363d58d8bad1550a97 100644 --- a/Source/WebKit/Shared/NativeWebKeyboardEvent.h +++ b/Source/WebKit/Shared/NativeWebKeyboardEvent.h @@ -33,6 +33,7 @@ @@ -8682,10 +8547,10 @@ index f8e96218fd2671d1c0aca5e549efe0d8b94ef0f9..6cebd61bceb39c08e916fe991e4c3fc6 NSEvent* nativeEvent() const { return m_nativeEvent.get(); } #elif PLATFORM(GTK) diff --git a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -index e59ca0120c0a684ea1fbdb2262a6a89ccc848828..4fcb529af731becaf446538be42075bfc19bbf90 100644 +index 6bb3cc33a43da97e85efe8afb8e7ea2400dd4787..79e2b2f0196d3edb62b2219d59229bb2822e549f 100644 --- a/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in +++ b/Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in -@@ -2827,6 +2827,9 @@ class WebCore::AuthenticationChallenge { +@@ -2841,6 +2841,9 @@ class WebCore::AuthenticationChallenge { class WebCore::DragData { #if PLATFORM(COCOA) String pasteboardName(); @@ -8695,7 +8560,7 @@ index e59ca0120c0a684ea1fbdb2262a6a89ccc848828..4fcb529af731becaf446538be42075bf #endif WebCore::IntPoint clientPosition(); WebCore::IntPoint globalPosition(); -@@ -3625,6 +3628,7 @@ enum class WebCore::WasPrivateRelayed : bool; +@@ -3649,6 +3652,7 @@ enum class WebCore::WasPrivateRelayed : bool; String httpStatusText; String httpVersion; WebCore::HTTPHeaderMap httpHeaderFields; @@ -8821,10 +8686,10 @@ index 20a6e465457151b02daa22e6bc059cf0e117ece5..ef4b1f737aaa683bc13c447aa4ca77e5 void setPosition(const WebCore::IntPoint& position) { m_position = position; } const WebCore::IntPoint& globalPosition() const { return m_globalPosition; } diff --git a/Source/WebKit/Shared/WebPageCreationParameters.h b/Source/WebKit/Shared/WebPageCreationParameters.h -index 36e44162c6f211876bf86b20e186f3da7e895536..68fe668470fef43c7a3af7a5c45ca4bac1fbbb28 100644 +index 02b701cbe003b0f9e16b9712e98c4157ebedbd69..70269920251e4d31f2c73fddc2f8cecbff453498 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.h +++ b/Source/WebKit/Shared/WebPageCreationParameters.h -@@ -301,6 +301,8 @@ struct WebPageCreationParameters { +@@ -303,6 +303,8 @@ struct WebPageCreationParameters { WebCore::ShouldRelaxThirdPartyCookieBlocking shouldRelaxThirdPartyCookieBlocking { WebCore::ShouldRelaxThirdPartyCookieBlocking::No }; bool httpsUpgradeEnabled { true }; @@ -8834,10 +8699,10 @@ index 36e44162c6f211876bf86b20e186f3da7e895536..68fe668470fef43c7a3af7a5c45ca4ba #if ENABLE(APP_HIGHLIGHTS) WebCore::HighlightVisibility appHighlightsVisible { WebCore::HighlightVisibility::Hidden }; diff --git a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in -index 6df2cea7d9ba7328456822475ed765e33966d4b8..39c08e228a2346b18915bc90e76965e0586d006b 100644 +index 822229ba75d9afe290cc72966cd763bd476f76e1..4487e71729e4c874ffec33374f25aee2ce289c3e 100644 --- a/Source/WebKit/Shared/WebPageCreationParameters.serialization.in +++ b/Source/WebKit/Shared/WebPageCreationParameters.serialization.in -@@ -223,6 +223,8 @@ enum class WebCore::UserInterfaceLayoutDirection : bool; +@@ -225,6 +225,8 @@ enum class WebCore::UserInterfaceLayoutDirection : bool; bool httpsUpgradeEnabled; @@ -8847,7 +8712,7 @@ index 6df2cea7d9ba7328456822475ed765e33966d4b8..39c08e228a2346b18915bc90e76965e0 WebCore::HighlightVisibility appHighlightsVisible; #endif diff --git a/Source/WebKit/Shared/glib/ProcessExecutablePathGLib.cpp b/Source/WebKit/Shared/glib/ProcessExecutablePathGLib.cpp -index 9899d60864664d1abff2b71c1c01e564e5dfb08c..391e0e42ca6a39f82b5a12c6aede069d61095ee2 100644 +index b41816f4ba670a7058014f078ef02441c3ab0abf..983d8679574a2555b348ab0df7699253b7d4a9fe 100644 --- a/Source/WebKit/Shared/glib/ProcessExecutablePathGLib.cpp +++ b/Source/WebKit/Shared/glib/ProcessExecutablePathGLib.cpp @@ -32,7 +32,7 @@ @@ -8869,10 +8734,10 @@ index 9899d60864664d1abff2b71c1c01e564e5dfb08c..391e0e42ca6a39f82b5a12c6aede069d if (execDirectory) { String processPath = FileSystem::pathByAppendingComponent(FileSystem::stringFromFileSystemRepresentation(execDirectory), StringView::fromLatin1(processName)); diff --git a/Source/WebKit/Shared/gtk/NativeWebKeyboardEventGtk.cpp b/Source/WebKit/Shared/gtk/NativeWebKeyboardEventGtk.cpp -index 8d33ceb065fb3e90372b0c696779189d07838da0..6e3194c3e96e46bfa09f8d706324e6515df1e7f4 100644 +index 8aa8b7623aad418b7ddd81f46c803d9c59d6a943..ac3c89fbb0ec41afe841cffebf9250304581d768 100644 --- a/Source/WebKit/Shared/gtk/NativeWebKeyboardEventGtk.cpp +++ b/Source/WebKit/Shared/gtk/NativeWebKeyboardEventGtk.cpp -@@ -51,12 +51,12 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(const String& text, std::optional +@@ -51,7 +51,7 @@ NativeWebKeyboardEvent::NativeWebKeyboardEvent(const String& text, std::optional } NativeWebKeyboardEvent::NativeWebKeyboardEvent(WebEventType type, const String& text, const String& key, const String& code, const String& keyIdentifier, int windowsVirtualKeyCode, int nativeVirtualKeyCode, Vector&& commands, bool isAutoRepeat, bool isKeypad, OptionSet modifiers) @@ -8881,25 +8746,6 @@ index 8d33ceb065fb3e90372b0c696779189d07838da0..6e3194c3e96e46bfa09f8d706324e651 { } - NativeWebKeyboardEvent::NativeWebKeyboardEvent(const NativeWebKeyboardEvent& event) -- : WebKeyboardEvent(WebEvent(event.type(), event.modifiers(), event.timestamp()), event.text(), event.key(), event.code(), event.keyIdentifier(), event.windowsVirtualKeyCode(), event.nativeVirtualKeyCode(), event.handledByInputMethod(), std::optional>(event.preeditUnderlines()), std::optional(event.preeditSelectionRange()), Vector(event.commands()), event.isAutoRepeat(), event.isKeypad()) -+ : WebKeyboardEvent(event) - , m_nativeEvent(event.nativeEvent() ? constructNativeEvent(event.nativeEvent()) : nullptr) - { - } -diff --git a/Source/WebKit/Shared/gtk/NativeWebMouseEventGtk.cpp b/Source/WebKit/Shared/gtk/NativeWebMouseEventGtk.cpp -index 9a1c3f09c756ea368ac2d68e183a13e2eb47ead7..01c738376230f83376d80d6d225543a3914943dd 100644 ---- a/Source/WebKit/Shared/gtk/NativeWebMouseEventGtk.cpp -+++ b/Source/WebKit/Shared/gtk/NativeWebMouseEventGtk.cpp -@@ -61,7 +61,7 @@ NativeWebMouseEvent::NativeWebMouseEvent(WebEventType type, WebMouseEventButton - } - - NativeWebMouseEvent::NativeWebMouseEvent(const NativeWebMouseEvent& event) -- : WebMouseEvent(WebEvent(event.type(), event.modifiers(), event.timestamp()), event.button(), event.buttons(), event.position(), event.globalPosition(), event.deltaX(), event.deltaY(), event.deltaZ(), event.clickCount(), 0, WebMouseEventSyntheticClickType::NoTap, event.isTouchEvent(), event.pointerId(), event.pointerType()) -+ : WebMouseEvent(event) - , m_nativeEvent(event.nativeEvent() ? constructNativeEvent(const_cast(event.nativeEvent())) : nullptr) - { - } diff --git a/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp b/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp index 7fcd22cd2172cd7fa77aee12ad5cfcf7a435abba..bc822b40eea889fb0499dd4e78f89f04d87c64a1 100644 --- a/Source/WebKit/Shared/unix/AuxiliaryProcessMain.cpp @@ -8947,7 +8793,7 @@ index 053e9336017d8818b3cbea79ce7c145fd5c46274..5632498d6ef875df80fc68ec206a9d08 JSC::Config::configureForTesting(); else if (!strcmp(argv[i], "-disable-jit")) diff --git a/Source/WebKit/Sources.txt b/Source/WebKit/Sources.txt -index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b6f44af76 100644 +index a978050575b323345d0de29d1abd1fa0bc993e77..f7ead71d28f77932edb9e7f8958b25e99b50fead 100644 --- a/Source/WebKit/Sources.txt +++ b/Source/WebKit/Sources.txt @@ -392,6 +392,7 @@ UIProcess/AboutSchemeHandler.cpp @@ -8958,7 +8804,7 @@ index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b UIProcess/DeviceIdHashSaltStorage.cpp UIProcess/DisplayLink.cpp UIProcess/DisplayLinkProcessProxyClient.cpp -@@ -401,16 +402,20 @@ UIProcess/FrameLoadState.cpp +@@ -401,6 +402,8 @@ UIProcess/FrameLoadState.cpp UIProcess/FrameProcess.cpp UIProcess/GeolocationPermissionRequestManagerProxy.cpp UIProcess/GeolocationPermissionRequestProxy.cpp @@ -8967,8 +8813,9 @@ index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b UIProcess/LegacyGlobalSettings.cpp UIProcess/MediaKeySystemPermissionRequestManagerProxy.cpp UIProcess/MediaKeySystemPermissionRequestProxy.cpp - UIProcess/ModelElementController.cpp +@@ -408,10 +411,12 @@ UIProcess/ModelElementController.cpp UIProcess/OverrideLanguages.cpp + UIProcess/PageClient.cpp UIProcess/PageLoadState.cpp +UIProcess/PlaywrightFullScreenManagerProxyClient.cpp UIProcess/ProcessAssertion.cpp @@ -8978,8 +8825,8 @@ index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b +UIProcess/RemoteInspectorPipe.cpp UIProcess/RemotePageDrawingAreaProxy.cpp UIProcess/RemotePageFullscreenManagerProxy.cpp - UIProcess/RemotePageProxy.cpp -@@ -453,6 +458,8 @@ UIProcess/WebOpenPanelResultListenerProxy.cpp + UIProcess/RemotePagePlaybackSessionManagerProxy.cpp +@@ -456,6 +461,8 @@ UIProcess/WebOpenPanelResultListenerProxy.cpp UIProcess/WebPageDiagnosticLoggingClient.cpp UIProcess/WebPageGroup.cpp UIProcess/WebPageInjectedBundleClient.cpp @@ -8988,7 +8835,7 @@ index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b UIProcess/WebPageProxy.cpp UIProcess/WebPageProxyMessageReceiverRegistration.cpp UIProcess/WebPageProxyTesting.cpp -@@ -604,6 +611,9 @@ UIProcess/Inspector/WebPageDebuggable.cpp +@@ -609,6 +616,9 @@ UIProcess/Inspector/WebPageDebuggable.cpp UIProcess/Inspector/WebPageInspectorController.cpp UIProcess/Inspector/Agents/InspectorBrowserAgent.cpp @@ -8999,10 +8846,10 @@ index bd7ec1f455c83cb4d24185099b6c7f569e612a33..cf4183ca9b2bb85b00bb58a4bc0c4d6b UIProcess/Media/AudioSessionRoutingArbitratorProxy.cpp UIProcess/Media/MediaUsageManager.cpp diff --git a/Source/WebKit/SourcesCocoa.txt b/Source/WebKit/SourcesCocoa.txt -index c146d8baabb0bc80396dc07d7cde88207ef02efa..b13cb0a6e8e10dcf3a068a4bde97536d490f3b52 100644 +index 4e2c7431cfc5c1b952232c30d6aa7f46815813c7..095c1b78812df547ab8cbe62e4ac801b5eb98968 100644 --- a/Source/WebKit/SourcesCocoa.txt +++ b/Source/WebKit/SourcesCocoa.txt -@@ -272,6 +272,7 @@ UIProcess/API/Cocoa/_WKArchiveExclusionRule.mm +@@ -270,6 +270,7 @@ UIProcess/API/Cocoa/_WKArchiveExclusionRule.mm UIProcess/API/Cocoa/_WKAttachment.mm UIProcess/API/Cocoa/_WKAutomationSession.mm UIProcess/API/Cocoa/_WKAutomationSessionConfiguration.mm @@ -9010,7 +8857,7 @@ index c146d8baabb0bc80396dc07d7cde88207ef02efa..b13cb0a6e8e10dcf3a068a4bde97536d UIProcess/API/Cocoa/_WKContentRuleListAction.mm UIProcess/API/Cocoa/_WKContextMenuElementInfo.mm UIProcess/API/Cocoa/_WKCustomHeaderFields.mm @no-unify -@@ -469,6 +470,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm +@@ -467,6 +468,7 @@ UIProcess/Inspector/ios/WKInspectorHighlightView.mm UIProcess/Inspector/ios/WKInspectorNodeSearchGestureRecognizer.mm UIProcess/Inspector/mac/RemoteWebInspectorUIProxyMac.mm @@ -9019,7 +8866,7 @@ index c146d8baabb0bc80396dc07d7cde88207ef02efa..b13cb0a6e8e10dcf3a068a4bde97536d UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm UIProcess/Inspector/mac/WKInspectorViewController.mm diff --git a/Source/WebKit/SourcesGTK.txt b/Source/WebKit/SourcesGTK.txt -index 5bf7f2a0c8d6e6d32a2845885b943064618fe951..9d8bfa3d2cf0920dcfdae5b95563c5cab5e755f4 100644 +index 68733a26a5cb0cd8f944b5ab48eacb256e147976..b8d320e417303329b9a6fa56ce4c3f6fccc286b8 100644 --- a/Source/WebKit/SourcesGTK.txt +++ b/Source/WebKit/SourcesGTK.txt @@ -122,6 +122,7 @@ UIProcess/API/glib/WebKitAutomationSession.cpp @no-unify @@ -9030,15 +8877,15 @@ index 5bf7f2a0c8d6e6d32a2845885b943064618fe951..9d8bfa3d2cf0920dcfdae5b95563c5ca UIProcess/API/glib/WebKitContextMenuClient.cpp @no-unify UIProcess/API/glib/WebKitCookieManager.cpp @no-unify UIProcess/API/glib/WebKitCredential.cpp @no-unify -@@ -253,6 +254,7 @@ UIProcess/glib/DisplayVBlankMonitor.cpp - UIProcess/glib/DisplayVBlankMonitorDRM.cpp +@@ -254,6 +255,7 @@ UIProcess/glib/DisplayVBlankMonitorDRM.cpp + UIProcess/glib/DisplayVBlankMonitorThreaded.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp UIProcess/glib/FenceMonitor.cpp +UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp UIProcess/glib/ScreenManager.cpp UIProcess/glib/SystemSettingsManagerProxy.cpp UIProcess/glib/WebPageProxyGLib.cpp -@@ -271,6 +273,7 @@ UIProcess/gtk/DisplayX11.cpp @no-unify +@@ -272,6 +274,7 @@ UIProcess/gtk/DisplayX11.cpp @no-unify UIProcess/gtk/DisplayWayland.cpp @no-unify UIProcess/gtk/WebDateTimePickerGtk.cpp UIProcess/gtk/HardwareAccelerationManager.cpp @@ -9046,7 +8893,7 @@ index 5bf7f2a0c8d6e6d32a2845885b943064618fe951..9d8bfa3d2cf0920dcfdae5b95563c5ca UIProcess/gtk/KeyBindingTranslator.cpp UIProcess/gtk/PointerLockManager.cpp @no-unify UIProcess/gtk/PointerLockManagerWayland.cpp @no-unify -@@ -284,6 +287,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp +@@ -285,6 +288,8 @@ UIProcess/gtk/ViewGestureControllerGtk.cpp UIProcess/gtk/WebColorPickerGtk.cpp UIProcess/gtk/WebContextMenuProxyGtk.cpp UIProcess/gtk/WebDataListSuggestionsDropdownGtk.cpp @@ -9056,7 +8903,7 @@ index 5bf7f2a0c8d6e6d32a2845885b943064618fe951..9d8bfa3d2cf0920dcfdae5b95563c5ca UIProcess/gtk/WebPasteboardProxyGtk.cpp UIProcess/gtk/WebPopupMenuProxyGtk.cpp diff --git a/Source/WebKit/SourcesWPE.txt b/Source/WebKit/SourcesWPE.txt -index 8569ab98ebf37a8e25c9e79f99a26c76b175d2c8..48c634968f8dcf42ca2690c07aa1b2c4c83d5884 100644 +index f3647727106bd3786d17adaf0c5f373184ace9fb..419a5a7002ed6567f0110fa63ff723691f027669 100644 --- a/Source/WebKit/SourcesWPE.txt +++ b/Source/WebKit/SourcesWPE.txt @@ -124,6 +124,7 @@ UIProcess/API/glib/WebKitAuthenticationRequest.cpp @no-unify @@ -9075,21 +8922,22 @@ index 8569ab98ebf37a8e25c9e79f99a26c76b175d2c8..48c634968f8dcf42ca2690c07aa1b2c4 UIProcess/API/glib/WebKitPolicyDecision.cpp @no-unify UIProcess/API/glib/WebKitPrivate.cpp @no-unify UIProcess/API/glib/WebKitProtocolHandler.cpp @no-unify -@@ -225,6 +227,7 @@ UIProcess/glib/DisplayVBlankMonitor.cpp - UIProcess/glib/DisplayVBlankMonitorDRM.cpp +@@ -226,6 +228,7 @@ UIProcess/glib/DisplayVBlankMonitorDRM.cpp + UIProcess/glib/DisplayVBlankMonitorThreaded.cpp UIProcess/glib/DisplayVBlankMonitorTimer.cpp UIProcess/glib/FenceMonitor.cpp +UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp UIProcess/glib/ScreenManager.cpp UIProcess/glib/SystemSettingsManagerProxy.cpp UIProcess/glib/WebPageProxyGLib.cpp -@@ -256,8 +259,14 @@ UIProcess/linux/MemoryPressureMonitor.cpp - UIProcess/soup/WebProcessPoolSoup.cpp +@@ -258,9 +261,15 @@ UIProcess/soup/WebProcessPoolSoup.cpp UIProcess/wpe/AcceleratedBackingStoreDMABuf.cpp + UIProcess/wpe/DisplayVBlankMonitorWPE.cpp +UIProcess/wpe/InspectorTargetProxyWPE.cpp UIProcess/wpe/ScreenManagerWPE.cpp UIProcess/wpe/SystemSettingsManagerProxyWPE.cpp + UIProcess/wpe/WPEUtilities.cpp +UIProcess/wpe/WebColorPickerWPE.cpp +UIProcess/wpe/WebDataListSuggestionsDropdownWPE.cpp +UIProcess/wpe/WebDateTimePickerWPE.cpp @@ -9098,7 +8946,7 @@ index 8569ab98ebf37a8e25c9e79f99a26c76b175d2c8..48c634968f8dcf42ca2690c07aa1b2c4 UIProcess/wpe/WebPageProxyWPE.cpp UIProcess/wpe/WebPasteboardProxyWPE.cpp UIProcess/wpe/WebPreferencesWPE.cpp -@@ -285,6 +294,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp +@@ -292,6 +301,8 @@ WebProcess/WebCoreSupport/glib/WebEditorClientGLib.cpp WebProcess/WebCoreSupport/soup/WebFrameNetworkingContext.cpp @@ -9108,11 +8956,11 @@ index 8569ab98ebf37a8e25c9e79f99a26c76b175d2c8..48c634968f8dcf42ca2690c07aa1b2c4 WebProcess/WebPage/AcceleratedSurface.cpp diff --git a/Source/WebKit/UIProcess/API/APIPageConfiguration.cpp b/Source/WebKit/UIProcess/API/APIPageConfiguration.cpp -index 64952ccd02d9db28c2e1388ce7713703430c8212..c616408e2d09f6d5d98dfd609a51ad080ebb990c 100644 +index 1182f2afe2c857598449d3dee6c1be028746c595..4256f65734adf9c68a30d70cc49c0b3a6fec1b1a 100644 --- a/Source/WebKit/UIProcess/API/APIPageConfiguration.cpp +++ b/Source/WebKit/UIProcess/API/APIPageConfiguration.cpp -@@ -268,6 +268,11 @@ WebPageProxy* PageConfiguration::relatedPage() const - return m_data.relatedPage.get(); +@@ -273,6 +273,11 @@ RefPtr PageConfiguration::protectedRelatedPage() const + return relatedPage(); } +WebKit::WebPageProxy* PageConfiguration::openerPageForInspector() const @@ -9124,12 +8972,12 @@ index 64952ccd02d9db28c2e1388ce7713703430c8212..c616408e2d09f6d5d98dfd609a51ad08 { return m_data.pageToCloneSessionStorageFrom.get(); diff --git a/Source/WebKit/UIProcess/API/APIPageConfiguration.h b/Source/WebKit/UIProcess/API/APIPageConfiguration.h -index ade928c03e6e504dc485e75427c56edfb0b1a4a4..1eef15db8aaecea65be8ec3be5d9be903055e840 100644 +index 43a17d77b13b0e51217506425607b8e0698062e4..96c7747d26cfa1d196796c784c046f6c17666969 100644 --- a/Source/WebKit/UIProcess/API/APIPageConfiguration.h +++ b/Source/WebKit/UIProcess/API/APIPageConfiguration.h -@@ -159,6 +159,10 @@ public: - WebKit::WebPageProxy* relatedPage() const; +@@ -160,6 +160,10 @@ public: void setRelatedPage(WeakPtr&& relatedPage) { m_data.relatedPage = WTFMove(relatedPage); } + RefPtr protectedRelatedPage() const; + // This is similar to relatedPage(), but it is also set for noopener links. + WebKit::WebPageProxy* openerPageForInspector() const; @@ -9138,7 +8986,7 @@ index ade928c03e6e504dc485e75427c56edfb0b1a4a4..1eef15db8aaecea65be8ec3be5d9be90 WebKit::WebPageProxy* pageToCloneSessionStorageFrom() const; void setPageToCloneSessionStorageFrom(WeakPtr&&); -@@ -515,6 +519,7 @@ private: +@@ -516,6 +520,7 @@ private: #endif RefPtr pageGroup; WeakPtr relatedPage; @@ -9162,7 +9010,7 @@ index e256b905bf9727aa7c8a48012237a6a6bc9acdbc..4e855c441af6f235f0fd8dfdd57b9bd6 copy->m_shouldTakeUIBackgroundAssertion = this->m_shouldTakeUIBackgroundAssertion; copy->m_shouldCaptureDisplayInUIProcess = this->m_shouldCaptureDisplayInUIProcess; diff --git a/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h b/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h -index af4944e5a5d373bc51995b50d1ea7c70f64ef1b3..403cfdeda26db2b648e32aa0e5f3ef6e076634f6 100644 +index 2fe09a750bf32bd6f124603af08e16adba8a6c7c..5ca91400765fd53559b4f4cefa172b5a1f417196 100644 --- a/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h +++ b/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h @@ -96,6 +96,16 @@ public: @@ -9194,7 +9042,7 @@ index af4944e5a5d373bc51995b50d1ea7c70f64ef1b3..403cfdeda26db2b648e32aa0e5f3ef6e bool m_shouldTakeUIBackgroundAssertion { true }; bool m_shouldCaptureDisplayInUIProcess { DEFAULT_CAPTURE_DISPLAY_IN_UI_PROCESS }; diff --git a/Source/WebKit/UIProcess/API/APIUIClient.h b/Source/WebKit/UIProcess/API/APIUIClient.h -index b0450278b8949985df9f39c7d7a5adbaecb55afe..ac80a31058249d467d21e64f5a053cf0f0012fe1 100644 +index b5c743d02f89afbe0c9a90e5f7e746fbd31fbbb2..29dbedfcf6929788b4810b8e98acaee26d1a686d 100644 --- a/Source/WebKit/UIProcess/API/APIUIClient.h +++ b/Source/WebKit/UIProcess/API/APIUIClient.h @@ -114,6 +114,7 @@ public: @@ -9206,7 +9054,7 @@ index b0450278b8949985df9f39c7d7a5adbaecb55afe..ac80a31058249d467d21e64f5a053cf0 virtual void setStatusText(WebKit::WebPageProxy*, const WTF::String&) { } virtual void mouseDidMoveOverElement(WebKit::WebPageProxy&, const WebKit::WebHitTestResultData&, OptionSet, Object*) { } diff --git a/Source/WebKit/UIProcess/API/C/WKInspector.cpp b/Source/WebKit/UIProcess/API/C/WKInspector.cpp -index bba39f6228ae9cc67c68526c75676648cf3917ef..f1c6f6f14ef0d599bd05410edbe9360e75d6d77a 100644 +index 1aa386d598a35dbda34161ed956d653a2da99bec..9aaeb894594f797931bac01314eb3e2ca8669855 100644 --- a/Source/WebKit/UIProcess/API/C/WKInspector.cpp +++ b/Source/WebKit/UIProcess/API/C/WKInspector.cpp @@ -28,6 +28,11 @@ @@ -9222,7 +9070,7 @@ index bba39f6228ae9cc67c68526c75676648cf3917ef..f1c6f6f14ef0d599bd05410edbe9360e #include "WebFrameProxy.h" #include "WebInspectorUIProxy.h" @@ -131,4 +136,11 @@ void WKInspectorToggleElementSelection(WKInspectorRef inspectorRef) - toImpl(inspectorRef)->toggleElementSelection(); + toProtectedImpl(inspectorRef)->toggleElementSelection(); } +void WKInspectorInitializeRemoteInspectorPipe(ConfigureDataStoreCallback configureDataStore, CreatePageCallback createPage, QuitCallback quit) @@ -9249,10 +9097,10 @@ index 026121d114c5fcad84c1396be8d692625beaa3bd..edd6e5cae033124c589959a42522fde0 } #endif diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp -index 908163888f737cb72569f6ff36dae276a51118eb..636ccf3834aaa47328388a008dbc2bf755ecdf70 100644 +index e15be39b349437382ef59f303986dac5246856dd..08b0290d847a046a5861475b7a5015513b22551f 100644 --- a/Source/WebKit/UIProcess/API/C/WKPage.cpp +++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp -@@ -1935,6 +1935,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient +@@ -1943,6 +1943,13 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient m_client.addMessageToConsole(toAPI(&page), toAPI(message.impl()), m_client.base.clientInfo); } @@ -9266,7 +9114,7 @@ index 908163888f737cb72569f6ff36dae276a51118eb..636ccf3834aaa47328388a008dbc2bf7 void setStatusText(WebPageProxy* page, const String& text) final { if (!m_client.setStatusText) -@@ -1964,6 +1971,8 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient +@@ -1972,6 +1979,8 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient { if (!m_client.didNotHandleKeyEvent) return; @@ -9335,39 +9183,6 @@ index fc43c44a85a0fc6bf5f8c643bd120a16ce762914..ee86fd213d25682f9b6553ec7da99bc8 // Version 15. WKPageDecidePolicyForSpeechRecognitionPermissionRequestCallback decidePolicyForSpeechRecognitionPermissionRequest; -diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm -index f2ee51f3f5dc9c55bcfe96d5bd52268957cc5e1a..472eb2530e812d50ecc38d9bfaa5bbe9948bad68 100644 ---- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm -+++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferences.mm -@@ -712,6 +712,16 @@ - (void)_setMediaCaptureRequiresSecureConnection:(BOOL)requiresSecureConnection - _preferences->setMediaCaptureRequiresSecureConnection(requiresSecureConnection); - } - -+- (BOOL)_alternateWebMPlayerEnabled -+{ -+ return _preferences->alternateWebMPlayerEnabled(); -+} -+ -+- (void)_setAlternateWebMPlayerEnabled:(BOOL)enabled -+{ -+ _preferences->setAlternateWebMPlayerEnabled(enabled); -+} -+ - - (double)_inactiveMediaCaptureStreamRepromptIntervalInMinutes - { - return _preferences->inactiveMediaCaptureStreamRepromptIntervalInMinutes(); -diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h -index 41525c8980f698aa4326e7d4d232311cee2c43d5..1f1745431c6cc7af66b47dd5d015c52389626e6f 100644 ---- a/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h -+++ b/Source/WebKit/UIProcess/API/Cocoa/WKPreferencesPrivate.h -@@ -119,6 +119,7 @@ typedef NS_ENUM(NSInteger, _WKPitchCorrectionAlgorithm) { - @property (nonatomic, setter=_setMockCaptureDevicesEnabled:) BOOL _mockCaptureDevicesEnabled WK_API_AVAILABLE(macos(10.13), ios(11.0)); - @property (nonatomic, setter=_setMockCaptureDevicesPromptEnabled:) BOOL _mockCaptureDevicesPromptEnabled WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); - @property (nonatomic, setter=_setMediaCaptureRequiresSecureConnection:) BOOL _mediaCaptureRequiresSecureConnection WK_API_AVAILABLE(macos(10.13), ios(11.0)); -+@property (nonatomic, setter=_setAlternateWebMPlayerEnabled:) BOOL _alternateWebMPlayerEnabled WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); - @property (nonatomic, setter=_setEnumeratingAllNetworkInterfacesEnabled:) BOOL _enumeratingAllNetworkInterfacesEnabled WK_API_AVAILABLE(macos(10.13), ios(11.0)); - @property (nonatomic, setter=_setICECandidateFilteringEnabled:) BOOL _iceCandidateFilteringEnabled WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); - @property (nonatomic, setter=_setInactiveMediaCaptureStreamRepromptIntervalInMinutes:) double _inactiveMediaCaptureStreamRepromptIntervalInMinutes WK_API_AVAILABLE(macos(10.13.4), ios(11.3)); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h b/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h index 2ce017d213d7875eee965e554af6befb5a3c3908..79b11310d358d3edc49e30420728e1eb905309cb 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegate.h @@ -9399,7 +9214,7 @@ index 930357ac3469195e9f33d5ffce92777018bb0b13..f62555ec562f8416976d31692e8fb175 NS_ASSUME_NONNULL_END diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm -index 7e793fe01f479b9135c54253af2114ce61924fa0..d22dc512f73af0e4152b4503866ba2a4cf5c3e5e 100644 +index e5c1fb49feda418fe4989474a7145016529409c5..ef1ffc4e07e9af85c50bb51d60d0af87c4352eeb 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebsiteDataStore.mm @@ -55,6 +55,7 @@ @@ -9410,7 +9225,7 @@ index 7e793fe01f479b9135c54253af2114ce61924fa0..d22dc512f73af0e4152b4503866ba2a4 #import #import #import -@@ -523,6 +524,11 @@ - (void)removeDataOfTypes:(NSSet *)dataTypes modifiedSince:(NSDate *)date comple +@@ -517,6 +518,11 @@ - (void)removeDataOfTypes:(NSSet *)dataTypes modifiedSince:(NSDate *)date comple }); } @@ -9424,10 +9239,10 @@ index 7e793fe01f479b9135c54253af2114ce61924fa0..d22dc512f73af0e4152b4503866ba2a4 Vector result; diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKBrowserInspector.h b/Source/WebKit/UIProcess/API/Cocoa/_WKBrowserInspector.h new file mode 100644 -index 0000000000000000000000000000000000000000..5fabe06a3289689246c36dfd96eb9900a48b2b0f +index 0000000000000000000000000000000000000000..8938effdcc896cb45f75e3445fbeac6054f69cc6 --- /dev/null +++ b/Source/WebKit/UIProcess/API/Cocoa/_WKBrowserInspector.h -@@ -0,0 +1,55 @@ +@@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 Microsoft Corporation. + * @@ -9465,7 +9280,10 @@ index 0000000000000000000000000000000000000000..5fabe06a3289689246c36dfd96eb9900 +WK_CLASS_AVAILABLE(macos(10.14.0)) +@interface _WKBrowserContext : NSObject +@property (nonatomic, strong) WKWebsiteDataStore *dataStore; ++#pragma clang diagnostic push ++#pragma clang diagnostic ignored "-Wdeprecated-declarations" +@property (nonatomic, strong) WKProcessPool *processPool; ++#pragma clang diagnostic pop +@end + +@protocol _WKBrowserInspectorDelegate @@ -9550,10 +9368,10 @@ index 0000000000000000000000000000000000000000..69eb9c6aa30beb8ea21a0ef647e46304 +} +@end diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h b/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h -index 426c7cbc897e22fd2e962dd3744959975d6ae6a0..f7a52359d7d42f970ef424b6c702b0ec1121a902 100644 +index 7b6c36521ebd5afbd58454192c8dece450b2e90a..a4a0ca2f69b1cc59fccbc261e1dff4e2f15d6e86 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h +++ b/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h -@@ -67,6 +67,7 @@ WK_CLASS_AVAILABLE(macos(10.10), ios(8.0)) +@@ -67,6 +67,7 @@ WK_EXTERN WK_API_DEPRECATED("Creating and using multiple instances of WKProcessP @property (nonatomic) pid_t presentingApplicationPID WK_API_AVAILABLE(macos(10.13), ios(11.0)); @property (nonatomic) audit_token_t presentingApplicationProcessToken WK_API_AVAILABLE(macos(10.13), ios(11.3)); @property (nonatomic) BOOL processSwapsOnNavigation WK_API_AVAILABLE(macos(10.14), ios(12.0)); @@ -9562,10 +9380,10 @@ index 426c7cbc897e22fd2e962dd3744959975d6ae6a0..f7a52359d7d42f970ef424b6c702b0ec @property (nonatomic) BOOL processSwapsOnNavigationWithinSameNonHTTPFamilyProtocol WK_API_AVAILABLE(macos(12.0), ios(15.0)); @property (nonatomic) BOOL prewarmsProcessesAutomatically WK_API_AVAILABLE(macos(10.14.4), ios(12.2)); diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm b/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm -index 7ccfd9a46cd024c8f6644594b9d0cde8ae7e60db..921ec683dce77583f24d27a0b1b6f478242dfb42 100644 +index 7d6be90da095403d61a44afbe8e08eb9569ecffc..c96e67fdd7887c18d150067cd94c99c62a4abfeb 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm -@@ -241,6 +241,16 @@ - (BOOL)processSwapsOnNavigation +@@ -243,6 +243,16 @@ - (BOOL)processSwapsOnNavigation return _processPoolConfiguration->processSwapsOnNavigation(); } @@ -9826,10 +9644,10 @@ index ef24e41f2c62e77e701a6e2b698c60004eaf0789..fd332a5b07f1dea7792eeee3bd9b5efb bool canRunBeforeUnloadConfirmPanel() const final { return true; } diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp -index 5479cf466e1ba77ede37ff980e78bee85bbb094c..224c04a4bd9de6ad5661b15071c1267a57430547 100644 +index dca689a7ace576571ca8ceb0b51921857b68b64c..e321049561de548aa517df0a8d6f91e86889e8e1 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp +++ b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp -@@ -423,10 +423,19 @@ static void webkitWebContextSetProperty(GObject* object, guint propID, const GVa +@@ -430,10 +430,19 @@ static void webkitWebContextSetProperty(GObject* object, guint propID, const GVa } } @@ -9849,7 +9667,7 @@ index 5479cf466e1ba77ede37ff980e78bee85bbb094c..224c04a4bd9de6ad5661b15071c1267a GUniquePtr bundleFilename(g_build_filename(injectedBundleDirectory(), INJECTED_BUNDLE_FILENAME, nullptr)); WebKitWebContext* webContext = WEBKIT_WEB_CONTEXT(object); -@@ -485,6 +494,8 @@ static void webkitWebContextConstructed(GObject* object) +@@ -492,6 +501,8 @@ static void webkitWebContextConstructed(GObject* object) static void webkitWebContextDispose(GObject* object) { @@ -9858,7 +9676,7 @@ index 5479cf466e1ba77ede37ff980e78bee85bbb094c..224c04a4bd9de6ad5661b15071c1267a WebKitWebContextPrivate* priv = WEBKIT_WEB_CONTEXT(object)->priv; if (!priv->clientsDetached) { priv->clientsDetached = true; -@@ -946,6 +957,11 @@ WebKitNetworkSession* webkit_web_context_get_network_session_for_automation(WebK +@@ -953,6 +964,11 @@ WebKitNetworkSession* webkit_web_context_get_network_session_for_automation(WebK return nullptr; #endif } @@ -9895,7 +9713,7 @@ index c1945fbe717a42afc1f51d64a80c7de3fa9009ba..ab63fe19b00ecbd64c9421e6eecad3e2 #endif +int webkitWebContextExistingCount(); diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp -index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b45805e938 100644 +index 449a8b1247c7397cd9709e425f8824a90bc77404..b374e3f5104572d44a6fbbb8384d3031815a2202 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp +++ b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp @@ -39,6 +39,7 @@ @@ -9906,7 +9724,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 #include "WebKitAuthenticationRequestPrivate.h" #include "WebKitBackForwardListPrivate.h" #include "WebKitContextMenuClient.h" -@@ -152,6 +153,7 @@ enum { +@@ -154,6 +155,7 @@ enum { CLOSE, SCRIPT_DIALOG, @@ -9914,7 +9732,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 DECIDE_POLICY, PERMISSION_REQUEST, -@@ -520,6 +522,13 @@ GRefPtr WebKitWebViewClient::showOptionMenu(WebKitPopupMenu& p +@@ -524,6 +526,13 @@ GRefPtr WebKitWebViewClient::showOptionMenu(WebKitPopupMenu& p void WebKitWebViewClient::frameDisplayed(WKWPE::View&) { @@ -9928,7 +9746,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 { SetForScope inFrameDisplayedGuard(m_webView->priv->inFrameDisplayed, true); for (const auto& callback : m_webView->priv->frameDisplayedCallbacks) { -@@ -536,6 +545,13 @@ void WebKitWebViewClient::frameDisplayed(WKWPE::View&) +@@ -540,6 +549,13 @@ void WebKitWebViewClient::frameDisplayed(WKWPE::View&) } } @@ -9942,7 +9760,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 void WebKitWebViewClient::willStartLoad(WKWPE::View&) { webkitWebViewWillStartLoad(m_webView); -@@ -622,7 +638,7 @@ static gboolean webkitWebViewDecidePolicy(WebKitWebView*, WebKitPolicyDecision* +@@ -631,7 +647,7 @@ static gboolean webkitWebViewDecidePolicy(WebKitWebView*, WebKitPolicyDecision* static gboolean webkitWebViewPermissionRequest(WebKitWebView*, WebKitPermissionRequest* request) { @@ -9951,7 +9769,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 if (WEBKIT_IS_POINTER_LOCK_PERMISSION_REQUEST(request)) { webkit_permission_request_allow(request); return TRUE; -@@ -945,6 +961,10 @@ static void webkitWebViewConstructed(GObject* object) +@@ -954,6 +970,10 @@ static void webkitWebViewConstructed(GObject* object) priv->websitePolicies = adoptGRef(webkit_website_policies_new()); Ref configuration = priv->relatedView && priv->relatedView->priv->configurationForNextRelatedView ? priv->relatedView->priv->configurationForNextRelatedView.releaseNonNull() : webkitWebViewCreatePageConfiguration(webView); @@ -9962,7 +9780,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 webkitWebViewCreatePage(webView, WTFMove(configuration)); webkitWebContextWebViewCreated(priv->context.get(), webView); -@@ -1984,6 +2004,15 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass) +@@ -2022,6 +2042,15 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass) G_TYPE_BOOLEAN, 1, WEBKIT_TYPE_SCRIPT_DIALOG); @@ -9978,7 +9796,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 /** * WebKitWebView::decide-policy: * @web_view: the #WebKitWebView on which the signal is emitted -@@ -2769,6 +2798,23 @@ void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const +@@ -2812,6 +2841,23 @@ void webkitWebViewRunJavaScriptBeforeUnloadConfirm(WebKitWebView* webView, const webkit_script_dialog_unref(webView->priv->currentScriptDialog); } @@ -10003,7 +9821,7 @@ index df1381b577be94114401e1faaf1979183d82614f..fb8bb4c7676154bb284a06fc3f05d3b4 { if (!webView->priv->currentScriptDialog) diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h b/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h -index bf5b4c2bcca722e4d008f12194344c29c0db8824..ee6ee6b476ac28dee3a5983d03ba89ad0c9eb9ff 100644 +index c80a5073b50ae7273985c9165cffc1d361a2ff03..7b1ce505325c735a0bf1c23f031ad1ee10639b91 100644 --- a/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h +++ b/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h @@ -64,6 +64,7 @@ void webkitWebViewRunJavaScriptAlert(WebKitWebView*, const CString& message, Fun @@ -10027,7 +9845,7 @@ index 763cd55f7abca011ac8bc4fef7f233bf52854cda..bd43917b274bf19ff9f3d96b7e80e207 #include <@API_INCLUDE_PREFIX@/WebKitClipboardPermissionRequest.h> #include <@API_INCLUDE_PREFIX@/WebKitColorChooserRequest.h> diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp -index 12efcb801d9d63d159a7f7d2e733cda141dc4e55..54f6703d64d5bcfd1d0f42be444492f9cd923bb0 100644 +index 92218ae7cac5f31db31d9ecde99094331a465ebf..e9d4e786b8640d93fb35187cba96496f6bbb1d32 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp @@ -270,6 +270,8 @@ void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool @@ -10052,7 +9870,7 @@ index 12efcb801d9d63d159a7f7d2e733cda141dc4e55..54f6703d64d5bcfd1d0f42be444492f9 void PageClientImpl::didChangeContentSize(const IntSize& size) diff --git a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h -index c20bdf245d4e6d2a215615cf573cbbdefdb574c0..8c9f3f8e2e111bd3d19314e33a394c371944d358 100644 +index 3cafc1ed805da8f947dc8c8c28327116f1d3c8a4..a8ed97eb566e4f966f85b03ea55d8dfccc608e58 100644 --- a/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/gtk/PageClientImpl.h @@ -104,7 +104,7 @@ private: @@ -10165,10 +9983,10 @@ index 496079da90993ac37689b060b69ecd4a67c2b6a8..af30181ca922f16c0f6e245c70e5ce7d G_BEGIN_DECLS diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp -index 3bbacbe1dbe27e9743f608322f06abb1adc75173..be721a26127ffff7904072aa2d9e7d72050ca514 100644 +index b58f8dd27e62f047857ed2f36fcd6dbd80e8038d..897d4f038d6dcbd9a4a37dbdda91fae46f56f521 100644 --- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp +++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBase.cpp -@@ -2875,6 +2875,11 @@ void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase) +@@ -2884,6 +2884,11 @@ void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase) #endif } @@ -10180,7 +9998,7 @@ index 3bbacbe1dbe27e9743f608322f06abb1adc75173..be721a26127ffff7904072aa2d9e7d72 void webkitWebViewBaseEnterAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext) { ASSERT(webkitWebViewBase->priv->acceleratedBackingStore); -@@ -2931,12 +2936,12 @@ void webkitWebViewBasePageClosed(WebKitWebViewBase* webkitWebViewBase) +@@ -2940,12 +2945,12 @@ void webkitWebViewBasePageClosed(WebKitWebViewBase* webkitWebViewBase) webkitWebViewBase->priv->acceleratedBackingStore->update({ }); } @@ -10196,7 +10014,7 @@ index 3bbacbe1dbe27e9743f608322f06abb1adc75173..be721a26127ffff7904072aa2d9e7d72 #if !USE(GTK4) diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h -index 3b8ca9470bab69dc26313111a79f954b10b30bf4..5c056dd6734f42d24bc168b4f5ba436584b3f7a8 100644 +index 8a71b1d7d9b9ef8bfe4a6935e3788e40a5fe6d5d..dad4a47725cc3a5d9d3fd6b1f4ec23c866cfe973 100644 --- a/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h +++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebViewBasePrivate.h @@ -27,6 +27,7 @@ @@ -10223,7 +10041,7 @@ index 3b8ca9470bab69dc26313111a79f954b10b30bf4..5c056dd6734f42d24bc168b4f5ba4365 + +WebKit::AcceleratedBackingStore* webkitWebViewBaseGetAcceleratedBackingStore(WebKitWebViewBase*); diff --git a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h -index 7636ad733e7be66a74f8fede966b0acb905a5842..777d86d6e160a7cfba6dd50d416ed1881f448de4 100644 +index 9091ae5198e765c2cfe0584d121afe4f88df3c0e..b0efedec419673ef2bfd0fd79406774e24aa98e9 100644 --- a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h +++ b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h @@ -26,6 +26,9 @@ @@ -10249,7 +10067,7 @@ index 7636ad733e7be66a74f8fede966b0acb905a5842..777d86d6e160a7cfba6dd50d416ed188 virtual void didChangePageID(WKWPE::View&) { } virtual void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler&& completionHandler) { completionHandler(WebKit::UserMessage()); } diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp -index bbbdaf4b823048adcb1e77756560b3dfd525297d..d4f873d6778bae8150817968b992d91dd2428518 100644 +index ab67db0aab214edea4f3c7ff80e1fa27c7c0a95b..069d41ca9eb33fdc851fc96a7b260ceca9fdfa9a 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp @@ -35,9 +35,12 @@ @@ -10297,7 +10115,7 @@ index bbbdaf4b823048adcb1e77756560b3dfd525297d..d4f873d6778bae8150817968b992d91d } RefPtr PageClientImpl::createDateTimePicker(WebPageProxy& page) -@@ -546,6 +555,37 @@ void PageClientImpl::selectionDidChange() +@@ -551,6 +560,37 @@ void PageClientImpl::selectionDidChange() m_view.selectionDidChange(); } @@ -10335,7 +10153,7 @@ index bbbdaf4b823048adcb1e77756560b3dfd525297d..d4f873d6778bae8150817968b992d91d WebKitWebResourceLoadManager* PageClientImpl::webResourceLoadManager() { return m_view.webResourceLoadManager(); -@@ -556,4 +596,11 @@ void PageClientImpl::callAfterNextPresentationUpdate(CompletionHandler&& +@@ -561,4 +601,11 @@ void PageClientImpl::callAfterNextPresentationUpdate(CompletionHandler&& m_view.callAfterNextPresentationUpdate(WTFMove(callback)); } @@ -10348,10 +10166,10 @@ index bbbdaf4b823048adcb1e77756560b3dfd525297d..d4f873d6778bae8150817968b992d91d + } // namespace WebKit diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h -index 8809d3fbeeae0387bf683f6154e6db741c3ecc4a..4f1671804b06c82ba221ad74b2b30d0f47ee8718 100644 +index 000b7c9950785f55fdfdcaa186026903a506c79e..2f0f821890ab40bcfa68b105710484674600a2db 100644 --- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h +++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h -@@ -183,9 +183,15 @@ private: +@@ -184,9 +184,15 @@ private: void didChangeWebPageID() const override; void selectionDidChange() override; @@ -10527,7 +10345,7 @@ index e4b92ace1531090ae38a7aec3d3d4febf19aee84..b66b573f9148c39c5ce2738add6cd01a + +PlatformImage webkitWebViewBackendTakeScreenshot(WebKitWebViewBackend*); diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h b/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h -index 2f1182cb91a00353eace0b71612df096391c2450..f2d09ba39f7d3c1b76ed705a0d945b869823c03e 100644 +index 0fa1f2e970ed0c0232df9b9c7b8b4bcd0ceac655..d68c6747edc699de7d84bae2a63b927dbdd8d0fe 100644 --- a/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h +++ b/Source/WebKit/UIProcess/API/wpe/WebKitWebViewClient.h @@ -51,6 +51,11 @@ private: @@ -10543,7 +10361,7 @@ index 2f1182cb91a00353eace0b71612df096391c2450..f2d09ba39f7d3c1b76ed705a0d945b86 void didChangePageID(WKWPE::View&) override; void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler&&) override; diff --git a/Source/WebKit/UIProcess/Automation/WebAutomationSession.h b/Source/WebKit/UIProcess/Automation/WebAutomationSession.h -index b351f11524a994ac6136daf9c277419fce44df0f..31679e63462cfaceeac00ebf31985c118ff1e4e4 100644 +index d0a40e3e9f4d59f894c0574926ee13b2d83767ab..3dfd8e059273d2a14494b76892e5abfe66475c2e 100644 --- a/Source/WebKit/UIProcess/Automation/WebAutomationSession.h +++ b/Source/WebKit/UIProcess/Automation/WebAutomationSession.h @@ -287,6 +287,8 @@ public: @@ -10553,8 +10371,8 @@ index b351f11524a994ac6136daf9c277419fce44df0f..31679e63462cfaceeac00ebf31985c11 + static std::optional platformGetBase64EncodedPNGData(const ViewSnapshot&); + RefPtr webPageProxyForHandle(const String&); + String handleForWebFrameID(std::optional); String handleForWebPageProxy(const WebPageProxy&); - @@ -338,7 +340,6 @@ private: // Get base64-encoded PNG data from a bitmap. @@ -10741,7 +10559,7 @@ index 89d125f7742f81ead8c50f218ecb1771b8000636..baa6cf58ad502c6c033ee6293a6cc8d4 namespace WebKit { diff --git a/Source/WebKit/UIProcess/Cocoa/UIDelegate.h b/Source/WebKit/UIProcess/Cocoa/UIDelegate.h -index 554009ae0e083f7721f97a3952f0f17b7d8d7d9d..fbf643cc61caab6389e4bc727adb897ebbb6a109 100644 +index c633008451c5719866d79300c49d60f979ffb581..9cc370de9b38f70970a6ca11a43651cbb2549529 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.h +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.h @@ -103,6 +103,7 @@ private: @@ -10752,7 +10570,7 @@ index 554009ae0e083f7721f97a3952f0f17b7d8d7d9d..fbf643cc61caab6389e4bc727adb897e void presentStorageAccessConfirmDialog(const WTF::String& requestingDomain, const WTF::String& currentDomain, CompletionHandler&&); void requestStorageAccessConfirm(WebPageProxy&, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, std::optional&&, CompletionHandler&&) final; void decidePolicyForGeolocationPermissionRequest(WebPageProxy&, WebFrameProxy&, const FrameInfoData&, Function&) final; -@@ -221,6 +222,7 @@ private: +@@ -226,6 +227,7 @@ private: bool webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; bool webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler : 1; @@ -10761,7 +10579,7 @@ index 554009ae0e083f7721f97a3952f0f17b7d8d7d9d..fbf643cc61caab6389e4bc727adb897e bool webViewRequestStorageAccessPanelForDomainUnderCurrentDomainForQuirkDomainsCompletionHandler : 1; bool webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler : 1; diff --git a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm -index 30de92c390b49a383369fb59397810187f752c8a..0752cc7cefcc4c3579eafca4fa1cc6e4045d5d84 100644 +index 6c934950b89e32e9ff58992497a313062658c235..c3f1b8d1193ae36efe9e5700f85eee4e86e8c548 100644 --- a/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm +++ b/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm @@ -135,6 +135,7 @@ void UIDelegate::setDelegate(id delegate) @@ -10772,7 +10590,7 @@ index 30de92c390b49a383369fb59397810187f752c8a..0752cc7cefcc4c3579eafca4fa1cc6e4 m_delegateMethods.webViewRequestStorageAccessPanelUnderFirstPartyCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:completionHandler:)]; m_delegateMethods.webViewRequestStorageAccessPanelForDomainUnderCurrentDomainForQuirkDomainsCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:forQuirkDomains:completionHandler:)]; m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:)]; -@@ -495,6 +496,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St +@@ -500,6 +501,15 @@ void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::St }).get()]; } @@ -10789,7 +10607,7 @@ index 30de92c390b49a383369fb59397810187f752c8a..0752cc7cefcc4c3579eafca4fa1cc6e4 { RefPtr uiDelegate = m_uiDelegate.get(); diff --git a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm -index 0eaba44d278da255e3035a4f71ddffb276ad2164..23838dad0c6f4a150ec99ecc1105163db1140a0a 100644 +index 70f71e49666eb0bc9a732c5893d27179b051bd72..a6c273ea6793f213aadc64485fe7c1fb23854b72 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm @@ -43,7 +43,9 @@ @@ -10802,7 +10620,7 @@ index 0eaba44d278da255e3035a4f71ddffb276ad2164..23838dad0c6f4a150ec99ecc1105163d #import "PlatformXRSystem.h" #import "PlaybackSessionManagerProxy.h" #import "RemoteLayerTreeTransaction.h" -@@ -347,11 +349,86 @@ bool WebPageProxy::scrollingUpdatesDisabledForTesting() +@@ -351,11 +353,86 @@ bool WebPageProxy::scrollingUpdatesDisabledForTesting() void WebPageProxy::startDrag(const DragItem& dragItem, ShareableBitmap::Handle&& dragImageHandle, const std::optional& elementID) { @@ -10891,10 +10709,10 @@ index 0eaba44d278da255e3035a4f71ddffb276ad2164..23838dad0c6f4a150ec99ecc1105163d #if ENABLE(ATTACHMENT_ELEMENT) diff --git a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm -index 7f9f0ecf631ab772460603adb4d0426e1d468085..a98a1388e589d5f079fe29f5b6091aa022b6ecfc 100644 +index 3b6663cb77c2c20e2e6822bfdbf822cea3022fdc..d0e67aa4714f4f8e01c3c2e1ef3fd1adc6274d20 100644 --- a/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm +++ b/Source/WebKit/UIProcess/Cocoa/WebProcessPoolCocoa.mm -@@ -436,7 +436,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END +@@ -442,7 +442,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END auto screenProperties = WebCore::collectScreenProperties(); parameters.screenProperties = WTFMove(screenProperties); #if PLATFORM(MAC) @@ -10903,7 +10721,7 @@ index 7f9f0ecf631ab772460603adb4d0426e1d468085..a98a1388e589d5f079fe29f5b6091aa0 #endif #if PLATFORM(VISION) -@@ -835,8 +835,8 @@ void WebProcessPool::registerNotificationObservers() +@@ -841,8 +841,8 @@ void WebProcessPool::registerNotificationObservers() }]; m_scrollerStyleNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSPreferredScrollerStyleDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *notification) { @@ -10915,7 +10733,7 @@ index 7f9f0ecf631ab772460603adb4d0426e1d468085..a98a1388e589d5f079fe29f5b6091aa0 m_activationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidBecomeActiveNotification object:NSApp queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *notification) { diff --git a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp -index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6f2a73d87 100644 +index f356f65a1515e9ed17e51d4bde50a8c0758fd6e1..0caab4689c42a89d1d5060a3bdf9a0502e73b8ab 100644 --- a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp +++ b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.cpp @@ -33,6 +33,7 @@ @@ -10954,7 +10772,7 @@ index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6 using namespace WebCore; @@ -182,6 +194,11 @@ void DrawingAreaProxyCoordinatedGraphics::deviceScaleFactorDidChange(CompletionH - sendWithAsyncReply(Messages::DrawingArea::SetDeviceScaleFactor(m_webPageProxy->deviceScaleFactor()), WTFMove(completionHandler)); + sendWithAsyncReply(Messages::DrawingArea::SetDeviceScaleFactor(page()->deviceScaleFactor()), WTFMove(completionHandler)); } +void DrawingAreaProxyCoordinatedGraphics::waitForSizeUpdate(Function&& callback) @@ -10974,7 +10792,7 @@ index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6 +{ + RefPtr surface; + if (isInAcceleratedCompositingMode()) { -+ AcceleratedBackingStore* backingStore = webkitWebViewBaseGetAcceleratedBackingStore(WEBKIT_WEB_VIEW_BASE(protectedWebPageProxy()->viewWidget())); ++ AcceleratedBackingStore* backingStore = webkitWebViewBaseGetAcceleratedBackingStore(WEBKIT_WEB_VIEW_BASE(protectedPage()->viewWidget())); + if (!backingStore) + return; + @@ -11003,7 +10821,7 @@ index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6 + if (!skImage) + return; + -+ protectedWebPageProxy()->inspectorController().didPaint(WTFMove(skImage)); ++ protectedPage()->inspectorController().didPaint(WTFMove(skImage)); +} +#endif // PLATFORM(GTK) + @@ -11018,16 +10836,16 @@ index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6 + auto image = surface->makeImageSnapshot(); + if (!image) + return; -+ protectedWebPageProxy()->inspectorController().didPaint(WTFMove(image)); ++ protectedPage()->inspectorController().didPaint(WTFMove(image)); +} +#endif // PLATFORM(WIN) + bool DrawingAreaProxyCoordinatedGraphics::alwaysUseCompositing() const { - if (!m_webPageProxy) + if (!page()) @@ -310,6 +380,12 @@ void DrawingAreaProxyCoordinatedGraphics::didUpdateGeometry() // we need to resend the new size here. - if (m_lastSentSize != m_size) + if (m_lastSentSize != size()) sendUpdateGeometry(); + else { + Vector> callbacks; @@ -11039,7 +10857,7 @@ index 183a2dc44f4d2921e68a6ff5fd2fb0fb815753af..f9e80d965f5b7cd64049d85e57e668d6 #if !PLATFORM(WPE) diff --git a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h -index 9c2bde0db0e4032a32e6ae02dc45af335df92f7a..3f3a58ee9b0f5c0ddad84f41cf3acd6199d81c37 100644 +index 61ce018548600fb7e4f3cdb3ee82b444d5b2429f..4e93f979cd98c36b24a08c00193b190a8a76af85 100644 --- a/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h +++ b/Source/WebKit/UIProcess/CoordinatedGraphics/DrawingAreaProxyCoordinatedGraphics.h @@ -29,6 +29,7 @@ @@ -11177,7 +10995,7 @@ index 9a92a8cde3b5d1da0fbbf5fe7c549cebb8a7f2f7..9ce201ca2d7aa002c7bd389f1fe03edf } // namespace WebKit diff --git a/Source/WebKit/UIProcess/DrawingAreaProxy.h b/Source/WebKit/UIProcess/DrawingAreaProxy.h -index e1f55b4a7fbc452ca1f2eb025b0c88ec9f4b845f..beb3c7a6619b554bb3186a0de917f497ac0f47f8 100644 +index c30c32f55aff3552bb05112dd53b9b8b62dd4a3f..32417be5f8cf613f58db900ce615518751b1509a 100644 --- a/Source/WebKit/UIProcess/DrawingAreaProxy.h +++ b/Source/WebKit/UIProcess/DrawingAreaProxy.h @@ -94,6 +94,7 @@ public: @@ -11190,7 +11008,7 @@ index e1f55b4a7fbc452ca1f2eb025b0c88ec9f4b845f..beb3c7a6619b554bb3186a0de917f497 virtual void sizeToContentAutoSizeMaximumSizeDidChange() { } diff --git a/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.cpp b/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.cpp new file mode 100644 -index 0000000000000000000000000000000000000000..0615d7de9494901c53131b45018ee48612e7cfca +index 0000000000000000000000000000000000000000..c7a4a2f5fbd8c99d2b53fe0d3b1645c865d46634 --- /dev/null +++ b/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.cpp @@ -0,0 +1,326 @@ @@ -11492,7 +11310,7 @@ index 0000000000000000000000000000000000000000..0615d7de9494901c53131b45018ee486 + + // Do not send the same frame over and over. + auto cryptoDigest = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_1); -+ cryptoDigest->addBytes(std::span(data.data(), data.size())); ++ cryptoDigest->addBytes(std::span(data.mutableSpan().data(), data.size())); + auto digest = cryptoDigest->computeHash(); + if (m_lastFrameDigest != digest) { + String base64Data = base64EncodeToString(data); @@ -11522,10 +11340,10 @@ index 0000000000000000000000000000000000000000..0615d7de9494901c53131b45018ee486 +WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.h b/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.h new file mode 100644 -index 0000000000000000000000000000000000000000..6e031f61b132176587643bc79b23202009c4aca4 +index 0000000000000000000000000000000000000000..852c199da066320f730226653a47c8e5185dc560 --- /dev/null +++ b/Source/WebKit/UIProcess/Inspector/Agents/InspectorScreencastAgent.h -@@ -0,0 +1,102 @@ +@@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 Microsoft Corporation. + * @@ -11561,6 +11379,10 @@ index 0000000000000000000000000000000000000000..6e031f61b132176587643bc79b232020 +#include +#include + ++#if USE(SKIA) ++#include ++#endif ++ +namespace Inspector { +class BackendDispatcher; +class FrontendChannel; @@ -12347,7 +12169,7 @@ index edd6e7f1799279ed3d0eb81b6c2eef9f5b375134..d4231f84f3c52641f4d9e88559e8e1a4 String m_identifier; Inspector::InspectorTargetType m_type; diff --git a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp -index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50915d8a5e 100644 +index 9e1e41b6a796082231a32b04fe8b13270b02eb3e..58b4d514f7a06b7fb7cdae111f99a9009f018187 100644 --- a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp +++ b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp @@ -26,13 +26,23 @@ @@ -12394,7 +12216,7 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 , m_backendDispatcher(BackendDispatcher::create(m_frontendRouter.copyRef())) , m_inspectedPage(inspectedPage) { -- auto targetAgent = makeUnique(m_frontendRouter.get(), m_backendDispatcher.get()); +- auto targetAgent = makeUnique(m_frontendRouter, m_backendDispatcher); - m_targetAgent = targetAgent.get(); - m_agents.append(WTFMove(targetAgent)); } @@ -12525,7 +12347,7 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 + m_pendingNavigations.clear(); + } - auto inspectedPage = protectedInspectedPage(); + Ref inspectedPage = m_inspectedPage.get(); inspectedPage->didChangeInspectorFrontendCount(m_frontendRouter->frontendCount()); @@ -137,6 +241,8 @@ void WebPageInspectorController::disconnectAllFrontends() // Disconnect any remaining remote frontends. @@ -12533,7 +12355,7 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 + m_pendingNavigations.clear(); + - auto inspectedPage = protectedInspectedPage(); + Ref inspectedPage = m_inspectedPage.get(); inspectedPage->didChangeInspectorFrontendCount(m_frontendRouter->frontendCount()); @@ -165,6 +271,66 @@ void WebPageInspectorController::setIndicating(bool indicating) @@ -12604,7 +12426,7 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 { addTarget(InspectorTargetProxy::create(protectedInspectedPage(), targetId, type)); @@ -184,6 +350,52 @@ void WebPageInspectorController::sendMessageToInspectorFrontend(const String& ta - m_targetAgent->sendMessageFromTargetToFrontend(targetId, message); + checkedTargetAgent()->sendMessageFromTargetToFrontend(targetId, message); } +void WebPageInspectorController::setPauseOnStart(bool shouldPause) @@ -12665,8 +12487,8 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 } void WebPageInspectorController::willDestroyProvisionalPage(const ProvisionalPageProxy& provisionalPage) -@@ -287,4 +499,29 @@ void WebPageInspectorController::browserExtensionsDisabled(HashSet&& ext - m_enabledBrowserAgent->extensionsDisabled(WTFMove(extensionIDs)); +@@ -288,4 +500,29 @@ void WebPageInspectorController::browserExtensionsDisabled(HashSet&& ext + enabledBrowserAgent->extensionsDisabled(WTFMove(extensionIDs)); } +void WebPageInspectorController::adjustPageSettings() @@ -12696,7 +12518,7 @@ index 45eb87344ce4249eea90dc0a73a2c717f69f55fa..c070e869819c36e60a5c64bf4c762a50 + } // namespace WebKit diff --git a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h -index c219e0a072057a8d40d8a30a1d404851d6c12d43..aff66705b9acadb9d811f2da439a59dbf5e6ea06 100644 +index 0e6766e155e83f9f5fde89080448b3f08a3a27e5..8ed8b9baf74866fad6498a1a13c6db96f0e2b460 100644 --- a/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h +++ b/Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h @@ -26,19 +26,39 @@ @@ -12813,12 +12635,13 @@ index c219e0a072057a8d40d8a30a1d404851d6c12d43..aff66705b9acadb9d811f2da439a59db bool shouldPauseLoading(const ProvisionalPageProxy&) const; void setContinueLoadingCallback(const ProvisionalPageProxy&, WTF::Function&&); -@@ -86,11 +153,12 @@ public: +@@ -86,12 +153,13 @@ public: void browserExtensionsDisabled(HashSet&&); private: - Ref protectedInspectedPage(); + WeakRef protectedInspectedPage(); + CheckedPtr checkedTargetAgent() { return m_targetAgent; } WebPageAgentContext webPageAgentContext(); void createLazyAgents(); @@ -12827,7 +12650,7 @@ index c219e0a072057a8d40d8a30a1d404851d6c12d43..aff66705b9acadb9d811f2da439a59db const Ref m_frontendRouter; const Ref m_backendDispatcher; -@@ -101,9 +169,16 @@ private: +@@ -102,9 +170,16 @@ private: CheckedPtr m_targetAgent; HashMap> m_targets; @@ -14317,7 +14140,7 @@ index 0000000000000000000000000000000000000000..e7a3dcc533294bb6e12f65d79b5b716b + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp -index 272e0a6edea50acd35032a854d625b5af5a1472e..486c807ba879ecf534db1ffb593709d117e0c35a 100644 +index f5c20099e6f746c3d26035313bf67641927549ec..4d3a40809a303bcb49bcc65cce7b7ce06ac7a96f 100644 --- a/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp +++ b/Source/WebKit/UIProcess/Launcher/glib/ProcessLauncherGLib.cpp @@ -168,6 +168,13 @@ void ProcessLauncher::launchProcess() @@ -14346,7 +14169,7 @@ index 272e0a6edea50acd35032a854d625b5af5a1472e..486c807ba879ecf534db1ffb593709d1 WTF_ALLOW_UNSAFE_BUFFER_USAGE_END diff --git a/Source/WebKit/UIProcess/Launcher/win/ProcessLauncherWin.cpp b/Source/WebKit/UIProcess/Launcher/win/ProcessLauncherWin.cpp -index a108acd8a4503a07309fe8c54afc80b0f4175eae..1421d9a761042c31a6ecf3cc78ce3f0e96109abe 100644 +index 71f80f5414e3e0cec1588fb3b4432665aa6facde..23bbdb9f5eac73ebd0cc5aea33bb08bcc67b60dc 100644 --- a/Source/WebKit/UIProcess/Launcher/win/ProcessLauncherWin.cpp +++ b/Source/WebKit/UIProcess/Launcher/win/ProcessLauncherWin.cpp @@ -91,14 +91,21 @@ void ProcessLauncher::launchProcess() @@ -14370,10 +14193,10 @@ index a108acd8a4503a07309fe8c54afc80b0f4175eae..1421d9a761042c31a6ecf3cc78ce3f0e + startupInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startupInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); PROCESS_INFORMATION processInformation { }; - BOOL result = ::CreateProcess(0, commandLine.data(), 0, 0, true, 0, 0, 0, &startupInfo, &processInformation); + BOOL result = ::CreateProcess(0, commandLine.mutableSpan().data(), 0, 0, true, 0, 0, 0, &startupInfo, &processInformation); diff --git a/Source/WebKit/UIProcess/PageClient.h b/Source/WebKit/UIProcess/PageClient.h -index 1e06e94f26fc73c323605da1afb0474559b35c33..1ac2f393d3dc0a7812c1b9a1bdaa6cea4f150bf4 100644 +index 48b9ebe975bff1b8179ac26a31b4724a028ecb94..f61094aa9f8db7832f3adcb3fd77bb19005abc40 100644 --- a/Source/WebKit/UIProcess/PageClient.h +++ b/Source/WebKit/UIProcess/PageClient.h @@ -74,6 +74,11 @@ @@ -14552,7 +14375,7 @@ index 493bde430bef5c064ff6807296ad088d8dee1a72..9b6dbc259e150fba3ba5fb4b488d91e3 #include "ProvisionalFrameCreationParameters.h" diff --git a/Source/WebKit/UIProcess/RemoteInspectorPipe.cpp b/Source/WebKit/UIProcess/RemoteInspectorPipe.cpp new file mode 100644 -index 0000000000000000000000000000000000000000..b1ddac8c1442cb4da17f50d92599ef9aff6d4066 +index 0000000000000000000000000000000000000000..be84b2a5ce00ec2e242785fe8b00a08c44900215 --- /dev/null +++ b/Source/WebKit/UIProcess/RemoteInspectorPipe.cpp @@ -0,0 +1,230 @@ @@ -14766,7 +14589,7 @@ index 0000000000000000000000000000000000000000..b1ddac8c1442cb4da17f50d92599ef9a + break; + + if (end > start) { -+ String message = String::fromUTF8({ line.data() + start, end - start }); ++ String message = String::fromUTF8({ line.mutableSpan().data() + start, end - start }); + RunLoop::main().dispatch([this, message = WTFMove(message)] { + if (!m_terminated) + m_playwrightAgent.dispatchMessageFromFrontend(message); @@ -14776,7 +14599,7 @@ index 0000000000000000000000000000000000000000..b1ddac8c1442cb4da17f50d92599ef9a + start = end; + } + if (start != 0 && start < line.size()) -+ memmove(line.data(), line.data() + start, line.size() - start); ++ memmove(line.mutableSpan().data(), line.mutableSpan().data() + start, line.size() - start); + line.shrink(line.size() - start); + } +} @@ -14857,6 +14680,50 @@ index 0000000000000000000000000000000000000000..6d04f9290135069359ce6bf872654648 +} // namespace WebKit + +#endif // ENABLE(REMOTE_INSPECTOR) +diff --git a/Source/WebKit/UIProcess/RemotePagePlaybackSessionManagerProxy.h b/Source/WebKit/UIProcess/RemotePagePlaybackSessionManagerProxy.h +index 3f6cd845733e2e8f5e25c318e7ab54f77032d4cf..e4399bfd734a28ef5df5ec855b7c78b46634c91d 100644 +--- a/Source/WebKit/UIProcess/RemotePagePlaybackSessionManagerProxy.h ++++ b/Source/WebKit/UIProcess/RemotePagePlaybackSessionManagerProxy.h +@@ -28,11 +28,11 @@ + #if PLATFORM(IOS_FAMILY) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)) + + #include "MessageReceiver.h" ++#include "PlaybackSessionManagerProxy.h" + #include + + namespace WebKit { + +-class PlaybackSessionManagerProxy; + class WebProcessProxy; + + class RemotePagePlaybackSessionManagerProxy : public IPC::MessageReceiver, public RefCounted { +diff --git a/Source/WebKit/UIProcess/RemotePageProxy.cpp b/Source/WebKit/UIProcess/RemotePageProxy.cpp +index 22c8a561850249008f998405712e88f1c2229336..70e7f76c32af780642d254003ee834204784694f 100644 +--- a/Source/WebKit/UIProcess/RemotePageProxy.cpp ++++ b/Source/WebKit/UIProcess/RemotePageProxy.cpp +@@ -37,6 +37,7 @@ + #include "ProvisionalFrameProxy.h" + #include "RemotePageDrawingAreaProxy.h" + #include "RemotePageFullscreenManagerProxy.h" ++#include "RemotePagePlaybackSessionManagerProxy.h" + #include "RemotePageVisitedLinkStoreRegistration.h" + #include "UserMediaProcessManager.h" + #include "WebFrameProxy.h" +@@ -51,12 +52,14 @@ + #include + #include + ++ + #if ENABLE(FULLSCREEN_API) + #include "WebFullScreenManagerProxy.h" + #endif + + #if ENABLE(VIDEO_PRESENTATION_MODE) + #include "RemotePageVideoPresentationManagerProxy.h" ++#include "VideoPresentationManagerProxy.h" + #endif + + namespace WebKit { diff --git a/Source/WebKit/UIProcess/WebContextMenuProxy.h b/Source/WebKit/UIProcess/WebContextMenuProxy.h index 697a350812e1bf73dd44cc3d723a6a291f9d59d1..a8e1edd710d88f48632d51fd05aa964732d727d3 100644 --- a/Source/WebKit/UIProcess/WebContextMenuProxy.h @@ -15610,18 +15477,18 @@ index 0000000000000000000000000000000000000000..26a2a3c0791c334f811ec99a630314f8 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp -index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744cad3254e10 100644 +index 26edaf188b554c9d0622ee3419d343e0e500e7f9..a884af86f2c56693a1aba8246168fda102142943 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.cpp +++ b/Source/WebKit/UIProcess/WebPageProxy.cpp -@@ -205,6 +205,7 @@ - #include +@@ -206,6 +206,7 @@ #include #include + #include +#include #include #include #include -@@ -217,6 +218,7 @@ +@@ -218,6 +219,7 @@ #include #include #include @@ -15670,7 +15537,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); #if PLATFORM(COCOA) -@@ -1007,6 +1018,10 @@ WebPageProxy::~WebPageProxy() +@@ -1006,6 +1017,10 @@ WebPageProxy::~WebPageProxy() #endif internals().updatePlayingMediaDidChangeTimer.stop(); @@ -15681,24 +15548,24 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } Ref WebPageProxy::Internals::protectedPage() const -@@ -1580,7 +1595,7 @@ void WebPageProxy::didAttachToRunningProcess() +@@ -1590,7 +1605,7 @@ void WebPageProxy::didAttachToRunningProcess() #if ENABLE(FULLSCREEN_API) ASSERT(!m_fullScreenManager); -- m_fullScreenManager = WebFullScreenManagerProxy::create(*this, protectedPageClient()->fullScreenManagerProxyClient()); -+ m_fullScreenManager = WebFullScreenManagerProxy::create(*this, m_fullScreenManagerClientOverride ? *m_fullScreenManagerClientOverride : protectedPageClient()->fullScreenManagerProxyClient()); +- m_fullScreenManager = WebFullScreenManagerProxy::create(*this, protectedPageClient()->checkedFullScreenManagerProxyClient().get()); ++ m_fullScreenManager = WebFullScreenManagerProxy::create(*this, m_fullScreenManagerClientOverride ? *m_fullScreenManagerClientOverride : protectedPageClient()->checkedFullScreenManagerProxyClient().get()); #endif #if ENABLE(VIDEO_PRESENTATION_MODE) ASSERT(!m_playbackSessionManager); -@@ -1739,6 +1754,7 @@ void WebPageProxy::initializeWebPage(const Site& site, WebCore::SandboxFlags eff +@@ -1754,6 +1769,7 @@ void WebPageProxy::initializeWebPage(const Site& site, WebCore::SandboxFlags eff if (preferences->siteIsolationEnabled()) browsingContextGroup->addPage(*this); - process->send(Messages::WebProcess::CreateWebPage(m_webPageID, creationParameters(process, *m_drawingArea, m_mainFrame->frameID(), std::nullopt)), 0); + process->send(Messages::WebProcess::CreateWebPage(m_webPageID, creationParameters(process, *protectedDrawingArea(), m_mainFrame->frameID(), std::nullopt)), 0); + m_inspectorController->didInitializeWebPage(); #if ENABLE(WINDOW_PROXY_PROPERTY_ACCESS_NOTIFICATION) - internals().frameLoadStateObserver = makeUniqueWithoutRefCountedCheck(*this); -@@ -2021,6 +2037,21 @@ Ref WebPageProxy::ensureProtectedRunningProcess() + internals().frameLoadStateObserver = WebPageProxyFrameLoadStateObserver::create(); +@@ -2036,6 +2052,21 @@ Ref WebPageProxy::ensureProtectedRunningProcess() return ensureRunningProcess(); } @@ -15720,7 +15587,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca RefPtr WebPageProxy::loadRequest(WebCore::ResourceRequest&& request, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, IsPerformingHTTPFallback isPerformingHTTPFallback, std::unique_ptr&& lastNavigationAction, API::Object* userData) { if (m_isClosed) -@@ -2136,11 +2167,29 @@ void WebPageProxy::loadRequestWithNavigationShared(Ref&& proces +@@ -2152,11 +2183,29 @@ void WebPageProxy::loadRequestWithNavigationShared(Ref&& proces navigation->setIsLoadedWithNavigationShared(true); protectedProcess->markProcessAsRecentlyUsed(); @@ -15754,7 +15621,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca }); } -@@ -2696,6 +2745,63 @@ void WebPageProxy::setControlledByAutomation(bool controlled) +@@ -2712,6 +2761,63 @@ void WebPageProxy::setControlledByAutomation(bool controlled) protectedWebsiteDataStore()->protectedNetworkProcess()->send(Messages::NetworkProcess::SetSessionIsControlledByAutomation(m_websiteDataStore->sessionID(), m_controlledByAutomation), 0); } @@ -15818,7 +15685,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca void WebPageProxy::createInspectorTarget(IPC::Connection& connection, const String& targetId, Inspector::InspectorTargetType type) { MESSAGE_CHECK_BASE(!targetId.isEmpty(), connection); -@@ -2955,6 +3061,24 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) +@@ -2999,6 +3105,24 @@ void WebPageProxy::updateActivityState(OptionSet flagsToUpdate) bool wasVisible = isViewVisible(); RefPtr pageClient = this->pageClient(); internals().activityState.remove(flagsToUpdate); @@ -15843,7 +15710,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca if (flagsToUpdate & ActivityState::IsFocused && pageClient->isViewFocused()) internals().activityState.add(ActivityState::IsFocused); if (flagsToUpdate & ActivityState::WindowIsActive && pageClient->isViewWindowActive()) -@@ -3718,7 +3842,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt +@@ -3762,7 +3886,7 @@ void WebPageProxy::performDragOperation(DragData& dragData, const String& dragSt if (!hasRunningProcess()) return; @@ -15852,7 +15719,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca URL url { dragData.asURL() }; if (url.protocolIsFile()) protectedLegacyMainFrameProcess()->assumeReadAccessToBaseURL(*this, url.string(), [] { }); -@@ -3746,6 +3870,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3790,6 +3914,8 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag if (!hasRunningProcess()) return; @@ -15861,7 +15728,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca auto completionHandler = [this, protectedThis = Ref { *this }, action, dragData] (std::optional dragOperation, WebCore::DragHandlingMethod dragHandlingMethod, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted, const IntRect& insertionRect, const IntRect& editableElementRect, std::optional remoteUserInputEventData) mutable { if (!m_pageClient) return; -@@ -3757,7 +3883,7 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag +@@ -3801,7 +3927,7 @@ void WebPageProxy::performDragControllerAction(DragControllerAction action, Drag dragData.setClientPosition(remoteUserInputEventData->transformedPoint); performDragControllerAction(action, dragData, remoteUserInputEventData->targetFrameID); }; @@ -15870,7 +15737,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca ASSERT(dragData.platformData()); sendWithAsyncReplyToProcessContainingFrame(frameID, Messages::WebPage::PerformDragControllerAction(action, dragData.clientPosition(), dragData.globalPosition(), dragData.draggingSourceOperationMask(), *dragData.platformData(), dragData.flags()), WTFMove(completionHandler)); #else -@@ -3792,14 +3918,35 @@ void WebPageProxy::didPerformDragControllerAction(std::optionalpageClient()) pageClient->didPerformDragControllerAction(); @@ -15910,7 +15777,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } didStartDrag(); } -@@ -3821,6 +3968,24 @@ void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& glo +@@ -3865,6 +4012,24 @@ void WebPageProxy::dragEnded(const IntPoint& clientPosition, const IntPoint& glo setDragCaretRect({ }); } @@ -15935,7 +15802,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca void WebPageProxy::didStartDrag() { if (!hasRunningProcess()) -@@ -3828,6 +3993,26 @@ void WebPageProxy::didStartDrag() +@@ -3872,6 +4037,26 @@ void WebPageProxy::didStartDrag() discardQueuedMouseEvents(); send(Messages::WebPage::DidStartDrag()); @@ -15962,20 +15829,20 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } void WebPageProxy::dragCancelled() -@@ -3999,26 +4184,47 @@ void WebPageProxy::processNextQueuedMouseEvent() +@@ -4043,26 +4228,47 @@ void WebPageProxy::processNextQueuedMouseEvent() process->startResponsivenessTimer(); } - std::optional> sandboxExtensions; -+ m_lastMousePositionForDrag = event.position(); ++ m_lastMousePositionForDrag = event->position(); + if (!m_dragSelectionData) { + std::optional> sandboxExtensions; #if PLATFORM(MAC) -- bool eventMayStartDrag = !m_currentDragOperation && eventType == WebEventType::MouseMove && event.button() != WebMouseEventButton::None; +- bool eventMayStartDrag = !m_currentDragOperation && eventType == WebEventType::MouseMove && event->button() != WebMouseEventButton::None; - if (eventMayStartDrag) - sandboxExtensions = SandboxExtension::createHandlesForMachLookup({ "com.apple.iconservices"_s, "com.apple.iconservices.store"_s }, process->auditToken(), SandboxExtension::MachBootstrapOptions::EnableMachBootstrap); -+ bool eventMayStartDrag = !m_currentDragOperation && eventType == WebEventType::MouseMove && event.button() != WebMouseEventButton::None; ++ bool eventMayStartDrag = !m_currentDragOperation && eventType == WebEventType::MouseMove && event->button() != WebMouseEventButton::None; + if (eventMayStartDrag) + sandboxExtensions = SandboxExtension::createHandlesForMachLookup({ "com.apple.iconservices"_s, "com.apple.iconservices.store"_s }, process->auditToken(), SandboxExtension::MachBootstrapOptions::EnableMachBootstrap); #endif @@ -15983,13 +15850,13 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca - auto eventWithCoalescedEvents = event; + auto eventWithCoalescedEvents = event; -- if (event.type() == WebEventType::MouseMove) { +- if (event->type() == WebEventType::MouseMove) { - internals().coalescedMouseEvents.append(event); -- eventWithCoalescedEvents.setCoalescedEvents(internals().coalescedMouseEvents); +- eventWithCoalescedEvents->setCoalescedEvents(internals().coalescedMouseEvents); - } -+ if (event.type() == WebEventType::MouseMove) { ++ if (event->type() == WebEventType::MouseMove) { + internals().coalescedMouseEvents.append(event); -+ eventWithCoalescedEvents.setCoalescedEvents(internals().coalescedMouseEvents); ++ eventWithCoalescedEvents->setCoalescedEvents(internals().coalescedMouseEvents); + } - LOG_WITH_STREAM(MouseHandling, stream << "UIProcess: sent mouse event " << eventType << " (queue size " << internals().mouseEventQueue.size() << ", coalesced events size " << internals().coalescedMouseEvents.size() << ")"); @@ -16002,9 +15869,9 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca + internals().coalescedMouseEvents.clear(); + } else { +#if PLATFORM(WIN) || PLATFORM(COCOA) -+ DragData dragData(*m_dragSelectionData, event.position(), event.globalPosition(), m_dragSourceOperationMask); ++ DragData dragData(*m_dragSelectionData, event->position(), event->globalPosition(), m_dragSourceOperationMask); +#else -+ DragData dragData(&*m_dragSelectionData, event.position(), event.globalPosition(), m_dragSourceOperationMask); ++ DragData dragData(&*m_dragSelectionData, event->position(), event->globalPosition(), m_dragSourceOperationMask); +#endif + if (eventType == WebEventType::MouseMove) { + dragUpdated(dragData); @@ -16015,14 +15882,14 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca + performDragOperation(dragData, ""_s, WTFMove(sandboxExtensionHandle), WTFMove(sandboxExtensionsForUpload)); + } + m_dragSelectionData = std::nullopt; -+ dragEnded(event.position(), event.globalPosition(), m_dragSourceOperationMask); ++ dragEnded(event->position(), event->globalPosition(), m_dragSourceOperationMask); + } + didReceiveEventIPC(process->connection(), eventType, true, std::nullopt); + } } void WebPageProxy::doAfterProcessingAllPendingMouseEvents(WTF::Function&& action) -@@ -4215,6 +4421,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) +@@ -4260,6 +4466,8 @@ void WebPageProxy::wheelEventHandlingCompleted(bool wasHandled) if (RefPtr automationSession = m_configuration->processPool().automationSession()) automationSession->wheelEventsFlushedForPage(*this); @@ -16031,7 +15898,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } void WebPageProxy::cacheWheelEventScrollingAccelerationCurve(const NativeWebWheelEvent& nativeWheelEvent) -@@ -4351,7 +4559,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) +@@ -4396,7 +4604,7 @@ static TrackingType mergeTrackingTypes(TrackingType a, TrackingType b) void WebPageProxy::updateTouchEventTracking(const WebTouchEvent& touchStartEvent) { @@ -16040,7 +15907,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca for (auto& touchPoint : touchStartEvent.touchPoints()) { auto location = touchPoint.locationInRootView(); auto update = [this, location](TrackingType& trackingType, EventTrackingRegions::EventType eventType) { -@@ -5012,6 +5220,7 @@ void WebPageProxy::receivedNavigationActionPolicyDecision(WebProcessProxy& proce +@@ -5078,6 +5286,7 @@ Ref WebPageProxy::navigationOriginatingPage(const FrameInfoData& f void WebPageProxy::receivedPolicyDecision(PolicyAction action, API::Navigation* navigation, RefPtr&& websitePolicies, Ref&& navigationAction, WillContinueLoadInNewProcess willContinueLoadInNewProcess, std::optional sandboxExtensionHandle, std::optional&& consoleMessage, CompletionHandler&& completionHandler) { @@ -16048,7 +15915,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca if (!hasRunningProcess()) return completionHandler(PolicyDecision { }); -@@ -6013,6 +6222,7 @@ void WebPageProxy::viewScaleFactorDidChange(IPC::Connection& connection, double +@@ -6080,6 +6289,7 @@ void WebPageProxy::viewScaleFactorDidChange(IPC::Connection& connection, double MESSAGE_CHECK_BASE(scaleFactorIsValid(scaleFactor), connection); if (!legacyMainFrameProcess().hasConnection(connection)) return; @@ -16056,35 +15923,33 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca forEachWebContentProcess([&] (auto& process, auto pageID) { if (&process == &legacyMainFrameProcess()) -@@ -6664,6 +6874,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, We +@@ -6731,6 +6941,7 @@ void WebPageProxy::didDestroyNavigationShared(Ref&& process, We RefPtr protectedPageClient { pageClient() }; - protectedNavigationState()->didDestroyNavigation(process->coreProcessIdentifier(), navigationID); + m_navigationState->didDestroyNavigation(process->coreProcessIdentifier(), navigationID); + m_inspectorController->didDestroyNavigation(navigationID); } void WebPageProxy::didStartProvisionalLoadForFrame(IPC::Connection& connection, FrameIdentifier frameID, FrameInfoData&& frameInfo, ResourceRequest&& request, std::optional navigationID, URL&& url, URL&& unreachableURL, const UserData& userData, WallTime timestamp) -@@ -7008,6 +7219,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p +@@ -7079,6 +7290,8 @@ void WebPageProxy::didFailProvisionalLoadForFrameShared(Ref&& p m_failingProvisionalLoadURL = { }; + m_inspectorController->didFailProvisionalLoadForFrame(*navigationID, error); + // If the provisional page's load fails then we destroy the provisional page. - if (m_provisionalPage && m_provisionalPage->mainFrame() == &frame && willContinueLoading == WillContinueLoading::No) + if (m_provisionalPage && m_provisionalPage->mainFrame() == &frame && (willContinueLoading == WillContinueLoading::No || protectedPreferences()->siteIsolationEnabled())) m_provisionalPage = nullptr; -@@ -8536,8 +8749,9 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w +@@ -8642,6 +8855,8 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w if (RefPtr page = originatingFrameInfo->page()) openerAppInitiatedState = page->lastNavigationWasAppInitiated(); -- auto navigationDataForNewProcess = navigationActionData.hasOpener ? nullptr : makeUnique(navigationActionData); + m_inspectorController->willCreateNewPage(windowFeatures, request.url()); - -+ auto navigationDataForNewProcess = navigationActionData.hasOpener ? nullptr : makeUnique(navigationActionData); ++ auto completionHandler = [ this, protectedThis = Ref { *this }, -@@ -8617,6 +8831,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w +@@ -8721,6 +8936,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w configuration->setOpenedMainFrameName(openedMainFrameName); if (!protectedPreferences()->siteIsolationEnabled()) configuration->setRelatedPage(*this); @@ -16092,7 +15957,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca if (RefPtr openerFrame = WebFrameProxy::webFrame(originatingFrameInfoData.frameID); navigationActionData.hasOpener && openerFrame) { configuration->setOpenerInfo({ { -@@ -8644,6 +8859,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w +@@ -8748,6 +8964,7 @@ void WebPageProxy::createNewPage(IPC::Connection& connection, WindowFeatures&& w void WebPageProxy::showPage() { m_uiClient->showPage(this); @@ -16100,7 +15965,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } bool WebPageProxy::hasOpenedPage() const -@@ -8775,6 +8991,10 @@ void WebPageProxy::closePage() +@@ -8898,6 +9115,10 @@ void WebPageProxy::closePage() if (isClosed()) return; @@ -16111,7 +15976,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca WEBPAGEPROXY_RELEASE_LOG(Process, "closePage:"); if (RefPtr pageClient = this->pageClient()) pageClient->clearAllEditCommands(); -@@ -8813,6 +9033,8 @@ void WebPageProxy::runJavaScriptAlert(IPC::Connection& connection, FrameIdentifi +@@ -8936,6 +9157,8 @@ void WebPageProxy::runJavaScriptAlert(IPC::Connection& connection, FrameIdentifi } runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), WTFMove(message), [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, String&& message, CompletionHandler&& completion) mutable { @@ -16120,7 +15985,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca page.m_uiClient->runJavaScriptAlert(page, WTFMove(message), frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)]() mutable { reply(); completion(); -@@ -8835,6 +9057,8 @@ void WebPageProxy::runJavaScriptConfirm(IPC::Connection& connection, FrameIdenti +@@ -8958,6 +9181,8 @@ void WebPageProxy::runJavaScriptConfirm(IPC::Connection& connection, FrameIdenti if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this, message, std::nullopt); } @@ -16129,7 +15994,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), WTFMove(message), [reply = WTFMove(reply)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, String&& message, CompletionHandler&& completion) mutable { page.m_uiClient->runJavaScriptConfirm(page, WTFMove(message), frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](bool result) mutable { -@@ -8859,6 +9083,8 @@ void WebPageProxy::runJavaScriptPrompt(IPC::Connection& connection, FrameIdentif +@@ -8982,6 +9207,8 @@ void WebPageProxy::runJavaScriptPrompt(IPC::Connection& connection, FrameIdentif if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->willShowJavaScriptDialog(*this, message, defaultValue); } @@ -16138,7 +16003,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca runModalJavaScriptDialog(WTFMove(frame), WTFMove(frameInfo), WTFMove(message), [reply = WTFMove(reply), defaultValue= WTFMove(defaultValue)](WebPageProxy& page, WebFrameProxy* frame, FrameInfoData&& frameInfo, String&& message, CompletionHandler&& completion) mutable { page.m_uiClient->runJavaScriptPrompt(page, WTFMove(message), WTFMove(defaultValue), frame, WTFMove(frameInfo), [reply = WTFMove(reply), completion = WTFMove(completion)](auto& result) mutable { -@@ -9000,6 +9226,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(IPC::Connection& connection, Fram +@@ -9123,6 +9350,8 @@ void WebPageProxy::runBeforeUnloadConfirmPanel(IPC::Connection& connection, Fram return; } } @@ -16147,7 +16012,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca // Since runBeforeUnloadConfirmPanel() can spin a nested run loop we need to turn off the responsiveness timer and the tryClose timer. webProcess->stopResponsivenessTimer(); -@@ -9626,6 +9854,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, +@@ -9758,6 +9987,11 @@ void WebPageProxy::resourceLoadDidCompleteWithError(ResourceLoadInfo&& loadInfo, } #if ENABLE(FULLSCREEN_API) @@ -16159,7 +16024,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca WebFullScreenManagerProxy* WebPageProxy::fullScreenManager() { return m_fullScreenManager.get(); -@@ -9753,6 +9986,17 @@ void WebPageProxy::requestDOMPasteAccess(IPC::Connection& connection, DOMPasteAc +@@ -9890,6 +10124,17 @@ void WebPageProxy::requestDOMPasteAccess(IPC::Connection& connection, DOMPasteAc } } @@ -16177,7 +16042,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca protectedPageClient()->requestDOMPasteAccess(pasteAccessCategory, requiresInteraction, elementRect, originIdentifier, WTFMove(completionHandler)); } -@@ -10795,6 +11039,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event +@@ -10900,6 +11145,8 @@ void WebPageProxy::mouseEventHandlingCompleted(std::optional event if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->mouseEventsFlushedForPage(*this); didFinishProcessingAllPendingMouseEvents(); @@ -16186,7 +16051,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } } -@@ -10830,6 +11076,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy +@@ -10935,6 +11182,7 @@ void WebPageProxy::keyEventHandlingCompleted(std::optional eventTy if (!canProcessMoreKeyEvents) { if (RefPtr automationSession = configuration().processPool().automationSession()) automationSession->keyboardEventsFlushedForPage(*this); @@ -16194,7 +16059,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca } } -@@ -11262,7 +11509,10 @@ void WebPageProxy::dispatchProcessDidTerminate(WebProcessProxy& process, Process +@@ -11369,7 +11617,10 @@ void WebPageProxy::dispatchProcessDidTerminate(WebProcessProxy& process, Process if (protectedPreferences()->siteIsolationEnabled()) protectedBrowsingContextGroup()->processDidTerminate(*this, process); @@ -16206,7 +16071,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca if (m_loaderClient) handledByClient = reason != ProcessTerminationReason::RequestedByClient && m_loaderClient->processDidCrash(*this); else -@@ -11913,6 +12163,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc +@@ -12022,6 +12273,8 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc parameters.canUseCredentialStorage = m_canUseCredentialStorage; parameters.httpsUpgradeEnabled = preferences->upgradeKnownHostsToHTTPSEnabled() ? m_configuration->httpsUpgradeEnabled() : false; @@ -16215,7 +16080,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca #if ENABLE(APP_HIGHLIGHTS) parameters.appHighlightsVisible = appHighlightsVisibility() ? HighlightVisibility::Visible : HighlightVisibility::Hidden; -@@ -12081,8 +12333,47 @@ void WebPageProxy::allowGamepadAccess() +@@ -12190,8 +12443,47 @@ void WebPageProxy::allowGamepadAccess() #endif // ENABLE(GAMEPAD) @@ -16263,7 +16128,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes) { m_navigationClient->shouldAllowLegacyTLS(*this, authenticationChallenge.get(), [this, protectedThis = Ref { *this }, authenticationChallenge] (bool shouldAllowLegacyTLS) { if (shouldAllowLegacyTLS) -@@ -12178,6 +12469,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(IPC::Connection& connect +@@ -12287,6 +12579,12 @@ void WebPageProxy::requestGeolocationPermissionForFrame(IPC::Connection& connect request->deny(); }; @@ -16276,7 +16141,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca // FIXME: Once iOS migrates to the new WKUIDelegate SPI, clean this up // and make it one UIClient call that calls the completionHandler with false // if there is no delegate instead of returning the completionHandler -@@ -12244,6 +12541,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -12353,6 +12651,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi shouldChangeDeniedToPrompt = false; if (sessionID().isEphemeral()) { @@ -16289,7 +16154,7 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; } -@@ -12258,6 +12561,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi +@@ -12367,6 +12671,12 @@ void WebPageProxy::queryPermission(const ClientOrigin& clientOrigin, const Permi return; } @@ -16303,19 +16168,19 @@ index 7c380219c6ff83938d0b7425b62932521cdb1edb..7439ed4314cd66866e0eb570e46744ca completionHandler(shouldChangeDeniedToPrompt ? PermissionState::Prompt : PermissionState::Denied); return; diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h -index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6821de584 100644 +index 9cf73b03b77ddb5e68cfa1866c368b3126405610..ffcd057469132f3be590d06469518172ef8a2def 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.h +++ b/Source/WebKit/UIProcess/WebPageProxy.h -@@ -26,6 +26,7 @@ - #pragma once - +@@ -28,6 +28,7 @@ + // Including more headers here slows down build times a lot. + // Use forward declarations and WebPageProxyInternals.h instead. #include "APIObject.h" +#include "APIWebsitePolicies.h" #include "MessageReceiver.h" - #include - #include -@@ -46,6 +47,20 @@ - #include + #include + #include +@@ -38,6 +39,20 @@ + #include #include #include +#include "InspectorDialogAgent.h" @@ -16333,26 +16198,26 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 +#include +#endif - #if USE(DICTATION_ALTERNATIVES) - #include -@@ -130,6 +145,7 @@ class DragData; + namespace API { + class Attachment; +@@ -115,6 +130,7 @@ class DragData; class Exception; class FloatPoint; class FloatQuad; -+typedef UncheckedKeyHashMap> DragDataMap; ++typedef HashMap> DragDataMap; class FloatRect; class FloatSize; class FontAttributeChanges; -@@ -735,6 +751,8 @@ public: +@@ -745,6 +761,8 @@ public: void setControlledByAutomation(bool); - WebPageInspectorController& inspectorController() { return *m_inspectorController; } + WebPageInspectorController& inspectorController() { return m_inspectorController.get(); } + InspectorDialogAgent* inspectorDialogAgent() { return m_inspectorDialogAgent; } + void setInspectorDialogAgent(InspectorDialogAgent * dialogAgent) { m_inspectorDialogAgent = dialogAgent; } #if PLATFORM(IOS_FAMILY) void showInspectorIndication(); -@@ -768,6 +786,7 @@ public: +@@ -778,6 +796,7 @@ public: bool hasSleepDisabler() const; #if ENABLE(FULLSCREEN_API) @@ -16360,7 +16225,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 WebFullScreenManagerProxy* fullScreenManager(); RefPtr protectedFullScreenManager(); void setFullScreenClientForTesting(std::unique_ptr&&); -@@ -859,6 +878,12 @@ public: +@@ -870,6 +889,12 @@ public: void setPageLoadStateObserver(RefPtr&&); @@ -16373,7 +16238,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 void initializeWebPage(const WebCore::Site&, WebCore::SandboxFlags); void setDrawingArea(RefPtr&&); -@@ -890,6 +915,8 @@ public: +@@ -901,6 +926,8 @@ public: RefPtr loadRequest(WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, WebCore::IsPerformingHTTPFallback); RefPtr loadRequest(WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, WebCore::IsPerformingHTTPFallback, std::unique_ptr&&, API::Object* userData = nullptr); @@ -16382,7 +16247,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 RefPtr loadFile(const String& fileURL, const String& resourceDirectoryURL, bool isAppInitiated = true, API::Object* userData = nullptr); RefPtr loadData(Ref&&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData = nullptr); RefPtr loadData(Ref&&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, WebCore::ShouldOpenExternalURLsPolicy); -@@ -978,6 +1005,7 @@ public: +@@ -990,6 +1017,7 @@ public: PageClient* pageClient() const; RefPtr protectedPageClient() const; @@ -16390,7 +16255,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 void setViewNeedsDisplay(const WebCore::Region&); void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, WebCore::ScrollIsAnimated); -@@ -1620,17 +1648,23 @@ public: +@@ -1635,17 +1663,23 @@ public: void didStartDrag(); void dragCancelled(); void setDragCaretRect(const WebCore::IntRect&); @@ -16415,7 +16280,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 #endif void processDidBecomeUnresponsive(); -@@ -1883,6 +1917,7 @@ public: +@@ -1897,6 +1931,7 @@ public: void setViewportSizeForCSSViewportUnits(const WebCore::FloatSize&); WebCore::FloatSize viewportSizeForCSSViewportUnits() const; @@ -16423,7 +16288,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 void didReceiveAuthenticationChallengeProxy(Ref&&, NegotiatedLegacyTLS); void negotiatedLegacyTLS(); void didNegotiateModernTLS(const URL&); -@@ -1916,6 +1951,8 @@ public: +@@ -1930,6 +1965,8 @@ public: #if PLATFORM(COCOA) || PLATFORM(GTK) RefPtr takeViewSnapshot(std::optional&&); RefPtr takeViewSnapshot(std::optional&&, ForceSoftwareCapturingViewportSnapshot); @@ -16432,7 +16297,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 #endif void serializeAndWrapCryptoKey(IPC::Connection&, WebCore::CryptoKeyData&&, CompletionHandler>&&)>&&); -@@ -2931,6 +2968,7 @@ private: +@@ -2956,6 +2993,7 @@ private: RefPtr launchProcessForReload(); void requestNotificationPermission(const String& originString, CompletionHandler&&); @@ -16440,7 +16305,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 void didChangeContentSize(const WebCore::IntSize&); void didChangeIntrinsicContentSize(const WebCore::IntSize&); -@@ -3450,8 +3488,10 @@ private: +@@ -3482,8 +3520,10 @@ private: String m_openedMainFrameName; RefPtr m_inspector; @@ -16451,7 +16316,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 RefPtr m_fullScreenManager; std::unique_ptr m_fullscreenClient; #endif -@@ -3650,6 +3690,22 @@ private: +@@ -3680,6 +3720,22 @@ private: std::optional m_currentDragOperation; bool m_currentDragIsOverFileInput { false }; unsigned m_currentDragNumberOfFilesToBeAccepted { 0 }; @@ -16474,7 +16339,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 #endif bool m_mainFrameHasHorizontalScrollbar { false }; -@@ -3821,6 +3877,10 @@ private: +@@ -3851,6 +3907,10 @@ private: RefPtr messageBody; }; Vector m_pendingInjectedBundleMessages; @@ -16486,7 +16351,7 @@ index c220a17ba9d98b37e16bea6ef2c37ffd4599c906..d5e57548822361620b9a18c283e5e5e6 #if PLATFORM(IOS_FAMILY) && ENABLE(DEVICE_ORIENTATION) RefPtr m_webDeviceOrientationUpdateProviderProxy; diff --git a/Source/WebKit/UIProcess/WebPageProxy.messages.in b/Source/WebKit/UIProcess/WebPageProxy.messages.in -index acdb79859685a55f3cda48014621340177f30e3d..36aca25956f9b073eefa5a68a013cde6017df39a 100644 +index fcb0649584da102c1446029342c8f31c59628301..5559c5f690a568bb1b39fe4bc0ac181a8e1953b3 100644 --- a/Source/WebKit/UIProcess/WebPageProxy.messages.in +++ b/Source/WebKit/UIProcess/WebPageProxy.messages.in @@ -35,6 +35,7 @@ messages -> WebPageProxy { @@ -16507,14 +16372,14 @@ index acdb79859685a55f3cda48014621340177f30e3d..36aca25956f9b073eefa5a68a013cde6 #endif +#if PLATFORM(WIN) && ENABLE(DRAG_SUPPORT) -+ StartDrag(UncheckedKeyHashMap> dragDataMap) ++ StartDrag(HashMap> dragDataMap) +#endif + #if PLATFORM(IOS_FAMILY) && ENABLE(DRAG_SUPPORT) WillReceiveEditDragSnapshot() DidReceiveEditDragSnapshot(struct std::optional textIndicator) diff --git a/Source/WebKit/UIProcess/WebProcessCache.cpp b/Source/WebKit/UIProcess/WebProcessCache.cpp -index dc6f440403cccc5cd93f75806cffbf05cc56041c..e880beba2034cc2b87dcfb3e1e8bacf1bed78cf3 100644 +index 9619dd597aec77547cccdf0d3ce6c711335f6244..db169d23388fd7b1cb7b79eef4160301aee0e0ef 100644 --- a/Source/WebKit/UIProcess/WebProcessCache.cpp +++ b/Source/WebKit/UIProcess/WebProcessCache.cpp @@ -100,6 +100,10 @@ bool WebProcessCache::canCacheProcess(WebProcessProxy& process) const @@ -16529,10 +16394,10 @@ index dc6f440403cccc5cd93f75806cffbf05cc56041c..e880beba2034cc2b87dcfb3e1e8bacf1 } diff --git a/Source/WebKit/UIProcess/WebProcessPool.cpp b/Source/WebKit/UIProcess/WebProcessPool.cpp -index 794f1e40dea081e818326f1d4123ebdf33569f86..35b0c4cdda0257c4844d4390d5901e71809c23f8 100644 +index a406b5a1e1523c07cb6dc2eebc493fa4f0026575..1f4fc38edd3756d5423e89a41c30a8ce7d5bf71f 100644 --- a/Source/WebKit/UIProcess/WebProcessPool.cpp +++ b/Source/WebKit/UIProcess/WebProcessPool.cpp -@@ -447,10 +447,10 @@ void WebProcessPool::setAutomationClient(std::unique_ptr& +@@ -439,10 +439,10 @@ void WebProcessPool::setAutomationClient(std::unique_ptr& void WebProcessPool::setOverrideLanguages(Vector&& languages) { @@ -16545,7 +16410,7 @@ index 794f1e40dea081e818326f1d4123ebdf33569f86..35b0c4cdda0257c4844d4390d5901e71 #if ENABLE(GPU_PROCESS) if (RefPtr gpuProcess = GPUProcessProxy::singletonIfCreated()) -@@ -458,9 +458,10 @@ void WebProcessPool::setOverrideLanguages(Vector&& languages) +@@ -450,9 +450,10 @@ void WebProcessPool::setOverrideLanguages(Vector&& languages) #endif #if USE(SOUP) for (Ref networkProcess : NetworkProcessProxy::allNetworkProcesses()) @@ -16557,7 +16422,7 @@ index 794f1e40dea081e818326f1d4123ebdf33569f86..35b0c4cdda0257c4844d4390d5901e71 void WebProcessPool::fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnabled) { -@@ -945,7 +946,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa +@@ -999,7 +1000,7 @@ void WebProcessPool::initializeNewWebProcess(WebProcessProxy& process, WebsiteDa #endif parameters.cacheModel = LegacyGlobalSettings::singleton().cacheModel(); @@ -16565,9 +16430,9 @@ index 794f1e40dea081e818326f1d4123ebdf33569f86..35b0c4cdda0257c4844d4390d5901e71 + parameters.overrideLanguages = configuration().overrideLanguages(); /* playwright revert fb205fb */ LOG_WITH_STREAM(Language, stream << "WebProcessPool is initializing a new web process with overrideLanguages: " << parameters.overrideLanguages); - parameters.urlSchemesRegisteredAsEmptyDocument = copyToVector(m_schemesToRegisterAsEmptyDocument); + parameters.urlSchemesRegisteredAsSecure = copyToVector(LegacyGlobalSettings::singleton().schemesToRegisterAsSecure()); diff --git a/Source/WebKit/UIProcess/WebProcessProxy.cpp b/Source/WebKit/UIProcess/WebProcessProxy.cpp -index f1074d1138c9e6f2a76c1ce76807c775b16c657c..b7386197a24a689a5b0cae6772abefb17819dbe9 100644 +index 51546b2db1e5ca415ceb550e69dc2daa982df450..6d10b802e49a3a0b803eb8a630e7c1c83c53f79c 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.cpp +++ b/Source/WebKit/UIProcess/WebProcessProxy.cpp @@ -208,6 +208,11 @@ Vector> WebProcessProxy::allProcesses() @@ -16610,7 +16475,7 @@ index f1074d1138c9e6f2a76c1ce76807c775b16c657c..b7386197a24a689a5b0cae6772abefb1 if (isPrewarmed()) diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h -index 4471fdf4a46180f78cea1cf035671bfe3c83b8e2..0858fe7f56eb3d8648467f5b82c2d77d6c958b77 100644 +index 4512391119154ce5b0ebb892e853499bda9747d9..33404c266e230e0cf62a0d9a597a7b59a047a1f3 100644 --- a/Source/WebKit/UIProcess/WebProcessProxy.h +++ b/Source/WebKit/UIProcess/WebProcessProxy.h @@ -185,6 +185,7 @@ public: @@ -16622,10 +16487,29 @@ index 4471fdf4a46180f78cea1cf035671bfe3c83b8e2..0858fe7f56eb3d8648467f5b82c2d77d void initializeWebProcess(WebProcessCreationParameters&&); diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp -index c13d50e1828843714be1f49d2b1d5ea45e9c85aa..d7c76f184e77f1d828cde263ddf01f262c6c8f3a 100644 +index 606401f719eecd52737fc3f6f15a328f74f4f5d9..be13e62a1461f21543f0a51c57003779bb3c1c79 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp -@@ -2061,6 +2061,15 @@ void WebsiteDataStore::setCacheModelSynchronouslyForTesting(CacheModel cacheMode +@@ -321,15 +321,10 @@ SOAuthorizationCoordinator& WebsiteDataStore::soAuthorizationCoordinator(const W + + static Ref networkProcessForSession(PAL::SessionID sessionID) + { +-#if ((PLATFORM(GTK) || PLATFORM(WPE)) && !ENABLE(2022_GLIB_API)) +- if (sessionID.isEphemeral()) { +- // Reuse a previous persistent session network process for ephemeral sessions. +- for (auto& dataStore : allDataStores().values()) { +- if (dataStore->isPersistent()) +- return dataStore->networkProcess(); +- } +- } ++// Playwright begin ++#if PLATFORM(GTK) || PLATFORM(WPE) + return NetworkProcessProxy::create(); ++// Playwright end + #else + UNUSED_PARAM(sessionID); + return NetworkProcessProxy::ensureDefaultNetworkProcess(); +@@ -2061,6 +2056,15 @@ void WebsiteDataStore::setCacheModelSynchronouslyForTesting(CacheModel cacheMode processPool->setCacheModelSynchronouslyForTesting(cacheModel); } @@ -16641,7 +16525,7 @@ index c13d50e1828843714be1f49d2b1d5ea45e9c85aa..d7c76f184e77f1d828cde263ddf01f26 Vector WebsiteDataStore::parametersFromEachWebsiteDataStore() { return WTF::map(allDataStores(), [](auto& entry) { -@@ -2509,6 +2518,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, +@@ -2504,6 +2508,12 @@ void WebsiteDataStore::originDirectoryForTesting(WebCore::ClientOrigin&& origin, protectedNetworkProcess()->websiteDataOriginDirectoryForTesting(m_sessionID, WTFMove(origin), type, WTFMove(completionHandler)); } @@ -16655,7 +16539,7 @@ index c13d50e1828843714be1f49d2b1d5ea45e9c85aa..d7c76f184e77f1d828cde263ddf01f26 void WebsiteDataStore::hasAppBoundSession(CompletionHandler&& completionHandler) const { diff --git a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h -index f1a113cc08f6543ab22be75a03823f49a3f52017..ef6dc1b450e2f4b0bcd374135a4cbc3d626a89d9 100644 +index 64d8fb4e83836c3dde325e77ded6b014e819cb10..c4b923995301697f8a52d72e74c842dcf4ec8161 100644 --- a/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h +++ b/Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h @@ -98,6 +98,7 @@ class DeviceIdHashSaltStorage; @@ -16700,7 +16584,7 @@ index f1a113cc08f6543ab22be75a03823f49a3f52017..ef6dc1b450e2f4b0bcd374135a4cbc3d void setNetworkProxySettings(WebCore::SoupNetworkProxySettings&&); const WebCore::SoupNetworkProxySettings& networkProxySettings() const { return m_networkProxySettings; } void setCookiePersistentStorage(const String&, SoupCookiePersistentStorageType); -@@ -420,6 +432,12 @@ public: +@@ -419,6 +431,12 @@ public: static const String& defaultBaseDataDirectory(); #endif @@ -16713,7 +16597,7 @@ index f1a113cc08f6543ab22be75a03823f49a3f52017..ef6dc1b450e2f4b0bcd374135a4cbc3d void resetQuota(CompletionHandler&&); void resetStoragePersistedState(CompletionHandler&&); #if PLATFORM(IOS_FAMILY) -@@ -618,7 +636,9 @@ private: +@@ -616,7 +634,9 @@ private: #if USE(SOUP) bool m_persistentCredentialStorageEnabled { true }; @@ -16724,7 +16608,7 @@ index f1a113cc08f6543ab22be75a03823f49a3f52017..ef6dc1b450e2f4b0bcd374135a4cbc3d WebCore::SoupNetworkProxySettings m_networkProxySettings; String m_cookiePersistentStoragePath; SoupCookiePersistentStorageType m_cookiePersistentStorageType { SoupCookiePersistentStorageType::SQLite }; -@@ -645,6 +665,10 @@ private: +@@ -643,6 +663,10 @@ private: RefPtr m_cookieStore; RefPtr m_networkProcess; @@ -16779,7 +16663,7 @@ index 96bf77411e2e1f4c835f56b409dc179977d197ee..512af5ffce511711b502248e34e49e45 RunLoop::Timer m_destroyLaterTimer; diff --git a/Source/WebKit/UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp b/Source/WebKit/UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp new file mode 100644 -index 0000000000000000000000000000000000000000..5185677fc67a418fac7a09c69246f4406a74c706 +index 0000000000000000000000000000000000000000..dfa06245b5411f2bc6ca7cebdb7c6d5fb46cd800 --- /dev/null +++ b/Source/WebKit/UIProcess/glib/InspectorPlaywrightAgentClientGLib.cpp @@ -0,0 +1,192 @@ @@ -16859,7 +16743,7 @@ index 0000000000000000000000000000000000000000..5185677fc67a418fac7a09c69246f440 + } + } + ignoreHosts.append(nullptr); -+ return parseRawProxySettings(proxyServer, ignoreHosts.data()); ++ return parseRawProxySettings(proxyServer, ignoreHosts.mutableSpan().data()); +} + +InspectorPlaywrightAgentClientGlib::InspectorPlaywrightAgentClientGlib(const WTF::String& proxyURI, const char* const* ignoreHosts) @@ -17043,10 +16927,10 @@ index 0000000000000000000000000000000000000000..441442d899e4088f5c24ae9f70c3e4ff + +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebKit/UIProcess/glib/WebProcessPoolGLib.cpp b/Source/WebKit/UIProcess/glib/WebProcessPoolGLib.cpp -index 51ba8b585ca37a2eed54bce5218e1c92c2844cc6..dc04a2ca0b0ac2333036b897dd18d5303c5237c6 100644 +index 4d782728182319b3d6e8e6d05580d96dd7ea19ac..c28dff07b154dba6ba82e94953b7b24e25a89106 100644 --- a/Source/WebKit/UIProcess/glib/WebProcessPoolGLib.cpp +++ b/Source/WebKit/UIProcess/glib/WebProcessPoolGLib.cpp -@@ -124,6 +124,8 @@ static OptionSet availableInputDevices() +@@ -126,6 +126,8 @@ static OptionSet availableInputDevices() return toAvailableInputDevices(gdk_seat_get_capabilities(seat)); } #endif @@ -17188,7 +17072,7 @@ index 0000000000000000000000000000000000000000..bf78de1915940c2d3292514cf0fe4e68 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/gtk/SystemSettingsManagerProxyGtk.cpp b/Source/WebKit/UIProcess/gtk/SystemSettingsManagerProxyGtk.cpp -index 9ef483a6b0ab1558c059304d727311ea7984184d..f532d2d2dbe2d7cb66323b6ea5fe2daace15d7b8 100644 +index 349cd3d9efd7a921560fa2f1a9e1a2238e415938..9df937535595281039660b060c7fba221d110691 100644 --- a/Source/WebKit/UIProcess/gtk/SystemSettingsManagerProxyGtk.cpp +++ b/Source/WebKit/UIProcess/gtk/SystemSettingsManagerProxyGtk.cpp @@ -126,6 +126,8 @@ int SystemSettingsManagerProxy::xftDPI() const @@ -17202,10 +17086,10 @@ index 9ef483a6b0ab1558c059304d727311ea7984184d..f532d2d2dbe2d7cb66323b6ea5fe2daa GtkFontRendering fontRendering; diff --git a/Source/WebKit/UIProcess/gtk/WebPageInspectorEmulationAgentGtk.cpp b/Source/WebKit/UIProcess/gtk/WebPageInspectorEmulationAgentGtk.cpp new file mode 100644 -index 0000000000000000000000000000000000000000..7d9672b0f831b5b7f6acf14ede26e1e8e9a65389 +index 0000000000000000000000000000000000000000..6d763018a12b3d7cd33af84b23f4340cea194bce --- /dev/null +++ b/Source/WebKit/UIProcess/gtk/WebPageInspectorEmulationAgentGtk.cpp -@@ -0,0 +1,117 @@ +@@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 Microsoft Corporation. + * @@ -17235,6 +17119,8 @@ index 0000000000000000000000000000000000000000..7d9672b0f831b5b7f6acf14ede26e1e8 +#include "DrawingAreaProxyCoordinatedGraphics.h" +#include "WebPageInspectorEmulationAgent.h" +#include "WebPageProxy.h" ++#include ++#include +#include +#include + @@ -17406,7 +17292,7 @@ index 0000000000000000000000000000000000000000..36ab6e9aec9f8d79fb13a8a49beadaaf + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/gtk/WebPasteboardProxyGtk.cpp b/Source/WebKit/UIProcess/gtk/WebPasteboardProxyGtk.cpp -index c7db5171b505ea9656f409c95c422dce9f0fa1ae..1992a112468e03840be3696245eecd8452ca51ed 100644 +index e4f31f89b42bc7884da760a18150bd1274d0383d..b2390de63e48a8a48a798199aa99203ef9390451 100644 --- a/Source/WebKit/UIProcess/gtk/WebPasteboardProxyGtk.cpp +++ b/Source/WebKit/UIProcess/gtk/WebPasteboardProxyGtk.cpp @@ -85,8 +85,10 @@ void WebPasteboardProxy::setPrimarySelectionOwner(WebFrameProxy* frame) @@ -17422,6 +17308,23 @@ index c7db5171b505ea9656f409c95c422dce9f0fa1ae..1992a112468e03840be3696245eecd84 m_primarySelectionOwner = frame; } +diff --git a/Source/WebKit/UIProcess/mac/CorrectionPanel.h b/Source/WebKit/UIProcess/mac/CorrectionPanel.h +index 8afb6132fad823816f84328a8b0a1a514f998bf7..54b582e60f4b16b3c7ba038c8c52466cce9875c4 100644 +--- a/Source/WebKit/UIProcess/mac/CorrectionPanel.h ++++ b/Source/WebKit/UIProcess/mac/CorrectionPanel.h +@@ -33,11 +33,10 @@ + #import + #import + #import ++#import "WebViewImpl.h" + + namespace WebKit { + +-class WebViewImpl; +- + class CorrectionPanel { + public: + CorrectionPanel(); diff --git a/Source/WebKit/UIProcess/mac/InspectorPlaywrightAgentClientMac.h b/Source/WebKit/UIProcess/mac/InspectorPlaywrightAgentClientMac.h new file mode 100644 index 0000000000000000000000000000000000000000..2aabc02a4b5432f68a6e85fd9689775608f05a67 @@ -17632,10 +17535,22 @@ index 0000000000000000000000000000000000000000..8adbd51bfecad2a273117588bf50f8f7 + +#endif diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.h b/Source/WebKit/UIProcess/mac/PageClientImplMac.h -index 27627ddb817e90c92ff5e533618ded9d017d36d9..9169b380a79d57201a7d3dceab04a1d16d987865 100644 +index c0de66cc6e82f1a2459ba006b5ac0fd63d8da261..a01639bab522df3ecff9df5aa4b6218633b2f049 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.h +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.h -@@ -61,6 +61,8 @@ class PageClientImpl final : public PageClientImplCocoa +@@ -31,9 +31,11 @@ + #include "PageClientImplCocoa.h" + #include "WebFullScreenManagerProxy.h" + #include ++#include + #include + #include + #include ++#include + + @class WKEditorUndoTarget; + @class WKView; +@@ -61,6 +63,8 @@ class PageClientImpl final : public PageClientImplCocoa WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(PageClientImpl); #endif public: @@ -17644,7 +17559,7 @@ index 27627ddb817e90c92ff5e533618ded9d017d36d9..9169b380a79d57201a7d3dceab04a1d1 PageClientImpl(NSView *, WKWebView *); virtual ~PageClientImpl(); -@@ -176,6 +178,9 @@ private: +@@ -175,6 +179,9 @@ private: void updateAcceleratedCompositingMode(const LayerTreeContext&) override; void didFirstLayerFlush(const LayerTreeContext&) override; @@ -17654,7 +17569,7 @@ index 27627ddb817e90c92ff5e533618ded9d017d36d9..9169b380a79d57201a7d3dceab04a1d1 RefPtr takeViewSnapshot(std::optional&&) override; RefPtr takeViewSnapshot(std::optional&&, ForceSoftwareCapturingViewportSnapshot) override; void wheelEventWasNotHandledByWebCore(const NativeWebWheelEvent&) override; -@@ -227,6 +232,10 @@ private: +@@ -226,6 +233,10 @@ private: void beganExitFullScreen(const WebCore::IntRect& initialFrame, const WebCore::IntRect& finalFrame, CompletionHandler&&) override; #endif @@ -17666,10 +17581,10 @@ index 27627ddb817e90c92ff5e533618ded9d017d36d9..9169b380a79d57201a7d3dceab04a1d1 void navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem&) override; void navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem&) override; diff --git a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm -index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1dcd896d6 100644 +index 0af0f031313b707c18ad675115385c42d5a1284f..4408666f4d7b4e67670f3edfabe9a1407e22afe2 100644 --- a/Source/WebKit/UIProcess/mac/PageClientImplMac.mm +++ b/Source/WebKit/UIProcess/mac/PageClientImplMac.mm -@@ -108,6 +108,13 @@ namespace WebKit { +@@ -107,6 +107,13 @@ namespace WebKit { using namespace WebCore; @@ -17683,7 +17598,7 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 PageClientImpl::PageClientImpl(NSView *view, WKWebView *webView) : PageClientImplCocoa(webView) , m_view(view) -@@ -161,6 +168,9 @@ NSWindow *PageClientImpl::activeWindow() const +@@ -162,6 +169,9 @@ NSWindow *PageClientImpl::activeWindow() const bool PageClientImpl::isViewWindowActive() { @@ -17693,7 +17608,7 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer)); RetainPtr activeViewWindow = activeWindow(); return activeViewWindow.get().isKeyWindow || (activeViewWindow && [NSApp keyWindow] == activeViewWindow.get()); -@@ -168,6 +178,9 @@ bool PageClientImpl::isViewWindowActive() +@@ -169,6 +179,9 @@ bool PageClientImpl::isViewWindowActive() bool PageClientImpl::isViewFocused() { @@ -17702,8 +17617,8 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 + // FIXME: This is called from the WebPageProxy constructor before we have a WebViewImpl. // Once WebViewImpl and PageClient merge, this won't be a problem. - if (!m_impl) -@@ -191,6 +204,9 @@ void PageClientImpl::makeFirstResponder() + if (CheckedPtr impl = m_impl.get()) +@@ -192,6 +205,9 @@ void PageClientImpl::makeFirstResponder() bool PageClientImpl::isViewVisible() { @@ -17713,35 +17628,35 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 RetainPtr activeView = this->activeView(); RetainPtr activeViewWindow = activeWindow(); -@@ -265,7 +281,8 @@ void PageClientImpl::didRelaunchProcess() +@@ -267,7 +283,8 @@ void PageClientImpl::didRelaunchProcess() void PageClientImpl::preferencesDidChange() { -- m_impl->preferencesDidChange(); -+ if (m_impl) -+ m_impl->preferencesDidChange(); +- checkedImpl()->preferencesDidChange(); ++ if (CheckedPtr impl = m_impl.get()) ++ impl->preferencesDidChange(); } void PageClientImpl::toolTipChanged(const String& oldToolTip, const String& newToolTip) -@@ -474,6 +491,8 @@ IntRect PageClientImpl::rootViewToAccessibilityScreen(const IntRect& rect) +@@ -477,6 +494,8 @@ IntRect PageClientImpl::rootViewToAccessibilityScreen(const IntRect& rect) void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent& event, bool eventWasHandled) { + if (!event.nativeEvent()) + return; - m_impl->doneWithKeyEvent(event.nativeEvent(), eventWasHandled); + checkedImpl()->doneWithKeyEvent(event.nativeEvent(), eventWasHandled); } -@@ -493,6 +512,8 @@ void PageClientImpl::computeHasVisualSearchResults(const URL& imageURL, Shareabl +@@ -496,6 +515,8 @@ void PageClientImpl::computeHasVisualSearchResults(const URL& imageURL, Shareabl RefPtr PageClientImpl::createPopupMenuProxy(WebPageProxy& page) { + if (_headless) + return nullptr; - return WebPopupMenuProxyMac::create(m_view.get().get(), page.popupMenuClient()); + return WebPopupMenuProxyMac::create(m_view.get().get(), page.checkedPopupMenuClient().get()); } -@@ -633,6 +654,12 @@ CALayer *PageClientImpl::footerBannerLayer() const +@@ -621,6 +642,12 @@ CALayer *PageClientImpl::footerBannerLayer() const return m_impl->footerBannerLayer(); } @@ -17753,8 +17668,8 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 + RefPtr PageClientImpl::takeViewSnapshot(std::optional&&) { - return m_impl->takeViewSnapshot(); -@@ -844,6 +871,13 @@ void PageClientImpl::beganExitFullScreen(const IntRect& initialFrame, const IntR + return checkedImpl()->takeViewSnapshot(); +@@ -832,6 +859,13 @@ void PageClientImpl::beganExitFullScreen(const IntRect& initialFrame, const IntR #endif // ENABLE(FULLSCREEN_API) @@ -17767,15 +17682,15 @@ index 22f6f6f08ba1601647898ca0f350672c3cac3816..6fad8bc05d1b458e25f8ddb9515ec8e1 + void PageClientImpl::navigationGestureDidBegin() { - m_impl->dismissContentRelativeChildWindowsWithAnimation(true); -@@ -1024,6 +1058,9 @@ void PageClientImpl::requestScrollToRect(const WebCore::FloatRect& targetRect, c + checkedImpl()->dismissContentRelativeChildWindowsWithAnimation(true); +@@ -1012,6 +1046,9 @@ void PageClientImpl::requestScrollToRect(const WebCore::FloatRect& targetRect, c bool PageClientImpl::windowIsFrontWindowUnderMouse(const NativeWebMouseEvent& event) { + // Simulated event. + if (!event.nativeEvent()) + return false; - return m_impl->windowIsFrontWindowUnderMouse(event.nativeEvent()); + return checkedImpl()->windowIsFrontWindowUnderMouse(event.nativeEvent()); } diff --git a/Source/WebKit/UIProcess/mac/SecItemShimProxy.messages.in b/Source/WebKit/UIProcess/mac/SecItemShimProxy.messages.in @@ -17801,6 +17716,18 @@ index f46895285dbc84c624537a194814c18f771a0c08..29ef9e5afa13b8d2b47b7f2dd4ce3784 } +#endif +diff --git a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm +index b9ffbadc03edbe9d260d546b59933c1c50d4148d..d0559dbd0bdfe0aa7c83ad97f5d01d8213c0caf2 100644 +--- a/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm ++++ b/Source/WebKit/UIProcess/mac/WKFullScreenWindowController.mm +@@ -29,6 +29,7 @@ + #if ENABLE(FULLSCREEN_API) && !PLATFORM(IOS_FAMILY) + + #import "AppKitSPI.h" ++#import "GPUProcessProxy.h" + #import "LayerTreeContext.h" + #import "NativeWebMouseEvent.h" + #import "VideoPresentationManagerProxy.h" diff --git a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h index a3c53c0bf913385d4d2d92900360d5f7d75927f8..e5570ef599ff1b59224648c353f8ab16f8fe7f88 100644 --- a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.h @@ -17814,10 +17741,10 @@ index a3c53c0bf913385d4d2d92900360d5f7d75927f8..e5570ef599ff1b59224648c353f8ab16 bool showAfterPostProcessingContextData(); diff --git a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm -index c8d9a4ae3fb5ef85afa837a1743c119ae34e1f7b..486a774cd1c98d9985c9d3c92418f835a9be2de1 100644 +index 273569e22d7c39c451fa39b21d4a0672b04773ad..84632cafb18c3d0f5367ba771daea12e30b2110a 100644 --- a/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm +++ b/Source/WebKit/UIProcess/mac/WebContextMenuProxyMac.mm -@@ -530,6 +530,12 @@ RetainPtr WebContextMenuProxyMac::createShareMenuItem(ShareMenuItemT +@@ -527,6 +527,12 @@ RetainPtr WebContextMenuProxyMac::createShareMenuItem(ShareMenuItemT } #endif @@ -18022,18 +17949,18 @@ index 0000000000000000000000000000000000000000..dd52991f936aa1c046b404801ee97237 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/mac/WebViewImpl.h b/Source/WebKit/UIProcess/mac/WebViewImpl.h -index 85f0df23b0b5c1e76f2c9e12ad43b9802c23f579..9202e7923ded8b543dcee636166c100e739a094f 100644 +index ad45b454acf088d5bb375971aae99684ea06d3ab..a9b4c26971969cf352cc045b01505a1751b44e46 100644 --- a/Source/WebKit/UIProcess/mac/WebViewImpl.h +++ b/Source/WebKit/UIProcess/mac/WebViewImpl.h -@@ -35,6 +35,7 @@ +@@ -34,6 +34,7 @@ + #include "PDFPluginIdentifier.h" #include "WKLayoutMode.h" - #include "WKTextAnimationType.h" #include +#include #include #include #include -@@ -570,6 +571,9 @@ public: +@@ -565,6 +566,9 @@ public: void provideDataForPasteboard(NSPasteboard *, NSString *type); NSArray *namesOfPromisedFilesDroppedAtDestination(NSURL *dropDestination); @@ -18044,10 +17971,10 @@ index 85f0df23b0b5c1e76f2c9e12ad43b9802c23f579..9202e7923ded8b543dcee636166c100e RefPtr takeViewSnapshot(ForceSoftwareCapturingViewportSnapshot); void saveBackForwardSnapshotForCurrentItem(); diff --git a/Source/WebKit/UIProcess/mac/WebViewImpl.mm b/Source/WebKit/UIProcess/mac/WebViewImpl.mm -index fa8aba243a8808dc781d40f1daf1480e6b655656..85f02dbfa3cfe47ee2adceee2714104445cdb7ba 100644 +index 1e906144a471367de596ccf5dd0ece2706306fe6..51cc9282b0b6d0b66df9f3b4fb69da7261b6e8d3 100644 --- a/Source/WebKit/UIProcess/mac/WebViewImpl.mm +++ b/Source/WebKit/UIProcess/mac/WebViewImpl.mm -@@ -2444,6 +2444,11 @@ WebCore::DestinationColorSpace WebViewImpl::colorSpace() +@@ -2477,6 +2477,11 @@ WebCore::DestinationColorSpace WebViewImpl::colorSpace() if (!m_colorSpace) m_colorSpace = [NSColorSpace sRGBColorSpace]; } @@ -18059,7 +17986,7 @@ index fa8aba243a8808dc781d40f1daf1480e6b655656..85f02dbfa3cfe47ee2adceee27141044 ASSERT(m_colorSpace); return WebCore::DestinationColorSpace { [m_colorSpace CGColorSpace] }; -@@ -4724,6 +4729,17 @@ static RetainPtr takeWindowSnapshot(CGSWindowID windowID, bool captu +@@ -4693,6 +4698,17 @@ static RetainPtr takeWindowSnapshot(CGSWindowID windowID, bool captu return WebCore::cgWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, imageOptions); } @@ -18281,7 +18208,7 @@ index 0000000000000000000000000000000000000000..135a60361fa8fbf907382625e7c8dd4e + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/win/WebContextMenuProxyWin.cpp b/Source/WebKit/UIProcess/win/WebContextMenuProxyWin.cpp -index bae35256ed815f7dac0b11ae439531d4ef3cb108..81e063b50a0132f8b36f3461f95d2ce1968198f5 100644 +index ed40616a49301967dc1c824f68cc57f628357c64..598e812dcfb1d4927841489086386834a2fbe0d5 100644 --- a/Source/WebKit/UIProcess/win/WebContextMenuProxyWin.cpp +++ b/Source/WebKit/UIProcess/win/WebContextMenuProxyWin.cpp @@ -115,5 +115,11 @@ WebContextMenuProxyWin::~WebContextMenuProxyWin() @@ -18434,7 +18361,7 @@ index 0000000000000000000000000000000000000000..8b474c730139b44a13c9d5b2d13ee204 + +} // namespace WebKit diff --git a/Source/WebKit/UIProcess/win/WebView.cpp b/Source/WebKit/UIProcess/win/WebView.cpp -index 5180da3ec22d56e8a9520e31cad076d86ae5be9f..f01be74573843e7b374c34035c09f114bdc7349c 100644 +index 2487c6de5b717dfea02a6b5a127c815019582d3c..eae5b89de5f54e94f439a7fbe72547743ad96749 100644 --- a/Source/WebKit/UIProcess/win/WebView.cpp +++ b/Source/WebKit/UIProcess/win/WebView.cpp @@ -576,7 +576,7 @@ LRESULT WebView::onSizeEvent(HWND hwnd, UINT, WPARAM, LPARAM lParam, bool& handl @@ -18987,10 +18914,10 @@ index 9b688ad328317fea4fd96ce66e9714bad8f0f937..402a36a9c565e13ec298aa7f014f0d92 } // namespace WebKit diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d48806565ac2 100644 +index 54b3c159ca5c86663abb3c67cacce504aff0bc0b..d59f2a64a2be81756d44a1e6ebb7f279ad3acae1 100644 --- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj +++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj -@@ -1560,6 +1560,7 @@ +@@ -1565,6 +1565,7 @@ 5CABDC8722C40FED001EDE8E /* APIMessageListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CABDC8322C40FA7001EDE8E /* APIMessageListener.h */; }; 5CADDE05215046BD0067D309 /* WKWebProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C74300E21500492004BFA17 /* WKWebProcess.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5CAECB6627465AE400AB78D0 /* UnifiedSource115.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5CAECB5E27465AE300AB78D0 /* UnifiedSource115.cpp */; }; @@ -18998,7 +18925,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5CAF7AA726F93AB00003F19E /* adattributiond.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5CAF7AA526F93A950003F19E /* adattributiond.cpp */; }; 5CAFDE452130846300B1F7E1 /* _WKInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAFDE422130843500B1F7E1 /* _WKInspector.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5CAFDE472130846A00B1F7E1 /* _WKInspectorInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CAFDE442130843600B1F7E1 /* _WKInspectorInternal.h */; }; -@@ -2346,6 +2347,18 @@ +@@ -2352,6 +2353,18 @@ DF0C5F28252ECB8E00D921DB /* WKDownload.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F24252ECB8D00D921DB /* WKDownload.h */; settings = {ATTRIBUTES = (Public, ); }; }; DF0C5F2A252ECB8E00D921DB /* WKDownloadDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F26252ECB8E00D921DB /* WKDownloadDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; DF0C5F2B252ED44000D921DB /* WKDownloadInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = DF0C5F25252ECB8E00D921DB /* WKDownloadInternal.h */; }; @@ -19017,7 +18944,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 DF462E0F23F22F5500EFF35F /* WKHTTPCookieStorePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF462E0E23F22F5300EFF35F /* WKHTTPCookieStorePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; DF462E1223F338BE00EFF35F /* WKContentWorldPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF462E1123F338AD00EFF35F /* WKContentWorldPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; DF7A231C291B088D00B98DF3 /* WKSnapshotConfigurationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DF7A231B291B088D00B98DF3 /* WKSnapshotConfigurationPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; -@@ -2447,6 +2460,8 @@ +@@ -2452,6 +2465,8 @@ E5BEF6822130C48000F31111 /* WebDataListSuggestionsDropdownIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = E5BEF6802130C47F00F31111 /* WebDataListSuggestionsDropdownIOS.h */; }; E5CB07DC20E1678F0022C183 /* WKFormColorControl.h in Headers */ = {isa = PBXBuildFile; fileRef = E5CB07DA20E1678F0022C183 /* WKFormColorControl.h */; }; E5CBA76427A318E100DF7858 /* UnifiedSource120.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA75F27A3187800DF7858 /* UnifiedSource120.cpp */; }; @@ -19026,7 +18953,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 E5CBA76527A318E100DF7858 /* UnifiedSource118.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76127A3187900DF7858 /* UnifiedSource118.cpp */; }; E5CBA76627A318E100DF7858 /* UnifiedSource116.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76327A3187B00DF7858 /* UnifiedSource116.cpp */; }; E5CBA76727A318E100DF7858 /* UnifiedSource119.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E5CBA76027A3187900DF7858 /* UnifiedSource119.cpp */; }; -@@ -2485,6 +2500,9 @@ +@@ -2491,6 +2506,9 @@ F3EEEE592DB318270038CC1D /* BidiBrowserAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = F3EEEE572DB318270038CC1D /* BidiBrowserAgent.h */; }; F3EEEE5A2DB318270038CC1D /* BidiBrowserAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F3EEEE582DB318270038CC1D /* BidiBrowserAgent.cpp */; }; F404455C2D5CFB56000E587E /* AppKitSoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = F404455A2D5CFB56000E587E /* AppKitSoftLink.h */; }; @@ -19036,7 +18963,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 F409BA181E6E64BC009DA28E /* WKDragDestinationAction.h in Headers */ = {isa = PBXBuildFile; fileRef = F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */; settings = {ATTRIBUTES = (Private, ); }; }; F40C3B712AB401C5007A3567 /* WKDatePickerPopoverController.h in Headers */ = {isa = PBXBuildFile; fileRef = F40C3B6F2AB40167007A3567 /* WKDatePickerPopoverController.h */; }; F41145682CD939E0004CDBD1 /* _WKTouchEventGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = F41145652CD939E0004CDBD1 /* _WKTouchEventGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; }; -@@ -6443,6 +6461,7 @@ +@@ -6458,6 +6476,7 @@ 5CABDC8522C40FCC001EDE8E /* WKMessageListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKMessageListener.h; sourceTree = ""; }; 5CABE07A28F60E8A00D83FD9 /* WebPushMessage.serialization.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebPushMessage.serialization.in; sourceTree = ""; }; 5CADDE0D2151AA010067D309 /* AuthenticationChallengeDisposition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthenticationChallengeDisposition.h; sourceTree = ""; }; @@ -19044,7 +18971,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5CAECB5E27465AE300AB78D0 /* UnifiedSource115.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource115.cpp; sourceTree = ""; }; 5CAF7AA426F93A750003F19E /* adattributiond */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = adattributiond; sourceTree = BUILT_PRODUCTS_DIR; }; 5CAF7AA526F93A950003F19E /* adattributiond.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = adattributiond.cpp; sourceTree = ""; }; -@@ -8171,6 +8190,19 @@ +@@ -8191,6 +8210,19 @@ DF0C5F24252ECB8D00D921DB /* WKDownload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownload.h; sourceTree = ""; }; DF0C5F25252ECB8E00D921DB /* WKDownloadInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownloadInternal.h; sourceTree = ""; }; DF0C5F26252ECB8E00D921DB /* WKDownloadDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDownloadDelegate.h; sourceTree = ""; }; @@ -19064,16 +18991,16 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 DF462E0E23F22F5300EFF35F /* WKHTTPCookieStorePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKHTTPCookieStorePrivate.h; sourceTree = ""; }; DF462E1123F338AD00EFF35F /* WKContentWorldPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKContentWorldPrivate.h; sourceTree = ""; }; DF58C6311371AC5800F9A37C /* NativeWebWheelEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeWebWheelEvent.h; sourceTree = ""; }; -@@ -8346,6 +8378,8 @@ +@@ -8365,6 +8397,8 @@ E5CBA76127A3187900DF7858 /* UnifiedSource118.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource118.cpp; sourceTree = ""; }; E5CBA76227A3187900DF7858 /* UnifiedSource117.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource117.cpp; sourceTree = ""; }; E5CBA76327A3187B00DF7858 /* UnifiedSource116.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource116.cpp; sourceTree = ""; }; + E5CBA76F27A3187800DF7858 /* UnifiedSource121.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = UnifiedSource121.cpp; sourceTree = ""; }; + E5CBA77F27A3187800DF7858 /* UnifiedSource122.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = UnifiedSource122.cpp; sourceTree = ""; }; E5DEFA6726F8F42600AB68DB /* PhotosUISPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PhotosUISPI.h; sourceTree = ""; }; + E838FCAF2DE90BF800703353 /* ISO18013MobileDocumentRequest+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ISO18013MobileDocumentRequest+Extras.swift"; sourceTree = ""; }; E88885662DC914C400C572B8 /* WKISO18013Request.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKISO18013Request.h; sourceTree = ""; }; - E890313A2D96411E00AB0B09 /* DigitalCredentialsCoordinator.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = DigitalCredentialsCoordinator.messages.in; sourceTree = ""; }; -@@ -8398,6 +8432,14 @@ +@@ -8418,6 +8452,14 @@ F404455A2D5CFB56000E587E /* AppKitSoftLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppKitSoftLink.h; sourceTree = ""; }; F404455B2D5CFB56000E587E /* AppKitSoftLink.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppKitSoftLink.mm; sourceTree = ""; }; F4063DDE2D71481E00F3FE6E /* LLVMProfiling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LLVMProfiling.h; sourceTree = ""; }; @@ -19088,7 +19015,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 F409BA171E6E64B3009DA28E /* WKDragDestinationAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKDragDestinationAction.h; sourceTree = ""; }; F40C3B6F2AB40167007A3567 /* WKDatePickerPopoverController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WKDatePickerPopoverController.h; path = ios/forms/WKDatePickerPopoverController.h; sourceTree = ""; }; F40C3B702AB40167007A3567 /* WKDatePickerPopoverController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = WKDatePickerPopoverController.mm; path = ios/forms/WKDatePickerPopoverController.mm; sourceTree = ""; }; -@@ -8825,6 +8867,7 @@ +@@ -8850,6 +8892,7 @@ 3766F9EE189A1241003CF19B /* JavaScriptCore.framework in Frameworks */, 3766F9F1189A1254003CF19B /* libicucore.dylib in Frameworks */, 7B9FC5BB28A5233B007570E7 /* libWebKitPlatform.a in Frameworks */, @@ -19096,7 +19023,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 3766F9EF189A1244003CF19B /* QuartzCore.framework in Frameworks */, 37694525184FC6B600CDE21F /* Security.framework in Frameworks */, 37BEC4DD1948FC6A008B4286 /* WebCore.framework in Frameworks */, -@@ -12013,6 +12056,7 @@ +@@ -12053,6 +12096,7 @@ 99788ACA1F421DCA00C08000 /* _WKAutomationSessionConfiguration.mm */, 990D28A81C6404B000986977 /* _WKAutomationSessionDelegate.h */, 990D28AF1C65203900986977 /* _WKAutomationSessionInternal.h */, @@ -19104,7 +19031,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5C4609E222430E4C009943C2 /* _WKContentRuleListAction.h */, 5C4609E322430E4D009943C2 /* _WKContentRuleListAction.mm */, 5C4609E422430E4D009943C2 /* _WKContentRuleListActionInternal.h */, -@@ -13392,6 +13436,7 @@ +@@ -13433,6 +13477,7 @@ E34B110C27C46BC6006D2F2E /* libWebCoreTestShim.dylib */, E34B110F27C46D09006D2F2E /* libWebCoreTestSupport.dylib */, DDE992F4278D06D900F60D26 /* libWebKitAdditions.a */, @@ -19112,7 +19039,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 57A9FF15252C6AEF006A2040 /* libWTF.a */, 5750F32A2032D4E500389347 /* LocalAuthentication.framework */, 570DAAB0230273D200E8FC04 /* NearField.framework */, -@@ -13974,6 +14019,12 @@ +@@ -14015,6 +14060,12 @@ children = ( 9197940423DBC4BB00257892 /* InspectorBrowserAgent.cpp */, 9197940323DBC4BB00257892 /* InspectorBrowserAgent.h */, @@ -19125,7 +19052,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 ); path = Agents; sourceTree = ""; -@@ -13982,6 +14033,7 @@ +@@ -14023,6 +14074,7 @@ isa = PBXGroup; children = ( A5D3504D1D78F0D2005124A9 /* RemoteWebInspectorUIProxyMac.mm */, @@ -19133,7 +19060,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 1CA8B935127C774E00576C2B /* WebInspectorUIProxyMac.mm */, 99A7ACE326012919006D57FD /* WKInspectorResourceURLSchemeHandler.h */, 99A7ACE42601291A006D57FD /* WKInspectorResourceURLSchemeHandler.mm */, -@@ -14736,6 +14788,7 @@ +@@ -14778,6 +14830,7 @@ E1513C65166EABB200149FCB /* AuxiliaryProcessProxy.h */, 46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */, 46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */, @@ -19141,7 +19068,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5C6D69352AC3935D0099BDAF /* BrowsingContextGroup.cpp */, 5C6D69362AC3935D0099BDAF /* BrowsingContextGroup.h */, 5CA98549210BEB5A0057EB6B /* BrowsingWarning.h */, -@@ -14760,6 +14813,8 @@ +@@ -14802,6 +14855,8 @@ BC06F43912DBCCFB002D78DE /* GeolocationPermissionRequestProxy.cpp */, BC06F43812DBCCFB002D78DE /* GeolocationPermissionRequestProxy.h */, 2DD5A72A1EBF09A7009BA597 /* HiddenPageThrottlingAutoIncreasesCounter.h */, @@ -19150,7 +19077,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5CEABA2B2333251400797797 /* LegacyGlobalSettings.cpp */, 5CEABA2A2333247700797797 /* LegacyGlobalSettings.h */, 31607F3819627002009B87DA /* LegacySessionStateCoding.h */, -@@ -14789,6 +14844,7 @@ +@@ -14832,6 +14887,7 @@ 4683569B21E81CC7006E27A3 /* ProvisionalPageProxy.cpp */, 4683569A21E81CC7006E27A3 /* ProvisionalPageProxy.h */, 411B89CB27B2B89600F9EBD3 /* QueryPermissionResultCallback.h */, @@ -19158,7 +19085,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5CCB54DC2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.cpp */, 5CCB54DB2A4FEA6A0005FAA8 /* RemotePageDrawingAreaProxy.h */, FABBBC802D35AC6800820017 /* RemotePageFullscreenManagerProxy.cpp */, -@@ -14892,6 +14948,8 @@ +@@ -14939,6 +14995,8 @@ BC7B6204129A0A6700D174A4 /* WebPageGroup.h */, 2D9EA3101A96D9EB002D2807 /* WebPageInjectedBundleClient.cpp */, 2D9EA30E1A96CBFF002D2807 /* WebPageInjectedBundleClient.h */, @@ -19167,7 +19094,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 9B7F8A502C785725000057F3 /* WebPageLoadTiming.h */, BC111B0B112F5E4F00337BAB /* WebPageProxy.cpp */, BC032DCB10F4389F0058C15A /* WebPageProxy.h */, -@@ -15070,6 +15128,7 @@ +@@ -15118,6 +15176,7 @@ BC646C1911DD399F006455B0 /* WKBackForwardListItemRef.h */, BC646C1611DD399F006455B0 /* WKBackForwardListRef.cpp */, BC646C1711DD399F006455B0 /* WKBackForwardListRef.h */, @@ -19175,7 +19102,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 BCB9E24A1120E15C00A137E0 /* WKContext.cpp */, BCB9E2491120E15C00A137E0 /* WKContext.h */, 1AE52F9319201F6B00A1FA37 /* WKContextConfigurationRef.cpp */, -@@ -15646,6 +15705,9 @@ +@@ -15694,6 +15753,9 @@ 07EF07592745A8160066EA04 /* DisplayCaptureSessionManager.h */, 07EF07582745A8160066EA04 /* DisplayCaptureSessionManager.mm */, 7AFA6F682A9F57C50055322A /* DisplayLinkMac.cpp */, @@ -19185,7 +19112,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 1AFDE65B1954E8D500C48FFA /* LegacySessionStateCoding.cpp */, 0FCB4E5818BBE3D9000FCFC9 /* PageClientImplMac.h */, 0FCB4E5918BBE3D9000FCFC9 /* PageClientImplMac.mm */, -@@ -15669,6 +15731,8 @@ +@@ -15717,6 +15779,8 @@ E568B92120A3AC6A00E3C856 /* WebDataListSuggestionsDropdownMac.mm */, E55CD20124D09F1F0042DB9C /* WebDateTimePickerMac.h */, E55CD20224D09F1F0042DB9C /* WebDateTimePickerMac.mm */, @@ -19194,7 +19121,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 BC857E8512B71EBB00EDEB2E /* WebPageProxyMac.mm */, BC5750951268F3C6006F0F12 /* WebPopupMenuProxyMac.h */, BC5750961268F3C6006F0F12 /* WebPopupMenuProxyMac.mm */, -@@ -16775,6 +16839,7 @@ +@@ -16826,6 +16890,7 @@ 99788ACB1F421DDA00C08000 /* _WKAutomationSessionConfiguration.h in Headers */, 990D28AC1C6420CF00986977 /* _WKAutomationSessionDelegate.h in Headers */, 990D28B11C65208D00986977 /* _WKAutomationSessionInternal.h in Headers */, @@ -19202,7 +19129,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5C4609E7224317B4009943C2 /* _WKContentRuleListAction.h in Headers */, 5C4609E8224317BB009943C2 /* _WKContentRuleListActionInternal.h in Headers */, 9B4CE9512CD99B7C00351173 /* _WKContentWorldConfiguration.h in Headers */, -@@ -17087,6 +17152,7 @@ +@@ -17139,6 +17204,7 @@ E170876C16D6CA6900F99226 /* BlobRegistryProxy.h in Headers */, 4F601432155C5AA2001FBDE0 /* BlockingResponseMap.h in Headers */, 1A5705111BE410E600874AF1 /* BlockSPI.h in Headers */, @@ -19210,7 +19137,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 5CA9854A210BEB640057EB6B /* BrowsingWarning.h in Headers */, A7E69BCC2B2117A100D43D3F /* BufferAndBackendInfo.h in Headers */, BC3065FA1259344E00E71278 /* CacheModel.h in Headers */, -@@ -17270,7 +17336,11 @@ +@@ -17321,7 +17387,11 @@ BC14DF77120B5B7900826C0C /* InjectedBundleScriptWorld.h in Headers */, CE550E152283752200D28791 /* InsertTextOptions.h in Headers */, 9197940523DBC4BB00257892 /* InspectorBrowserAgent.h in Headers */, @@ -19222,7 +19149,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 A5E391FD2183C1F800C8FB31 /* InspectorTargetProxy.h in Headers */, C5BCE5DF1C50766A00CDE3FA /* InteractionInformationAtPosition.h in Headers */, 2D4D2C811DF60BF3002EB10C /* InteractionInformationRequest.h in Headers */, -@@ -17531,6 +17601,7 @@ +@@ -17581,6 +17651,7 @@ 0F6E7C532C4C386800F1DB85 /* RemoteDisplayListRecorderMessages.h in Headers */, F451C0FE2703B263002BA03B /* RemoteDisplayListRecorderProxy.h in Headers */, A78A5FE42B0EB39E005036D3 /* RemoteImageBufferSetIdentifier.h in Headers */, @@ -19230,15 +19157,15 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 2D47B56D1810714E003A3AEE /* RemoteLayerBackingStore.h in Headers */, 2DDF731518E95060004F5A66 /* RemoteLayerBackingStoreCollection.h in Headers */, 1AB16AEA164B3A8800290D62 /* RemoteLayerTreeContext.h in Headers */, -@@ -17588,6 +17659,7 @@ +@@ -17638,6 +17709,7 @@ E1E552C516AE065F004ED653 /* SandboxInitializationParameters.h in Headers */, E36FF00327F36FBD004BE21A /* SandboxStateVariables.h in Headers */, 7BAB111025DD02B3008FC479 /* ScopedActiveMessageReceiveQueue.h in Headers */, + F303B849249A8D640031DE5C /* ScreencastEncoder.h in Headers */, 6D4DF20C2D824242001F964C /* ScreenTimeWebsiteDataSupport.h in Headers */, 463BB93A2B9D08D80098C5C3 /* ScriptMessageHandlerIdentifier.h in Headers */, - F4E28A362C923814008120DD /* ScriptTelemetry.h in Headers */, -@@ -17950,6 +18022,8 @@ + F4E28A362C923814008120DD /* ScriptTrackingPrivacyFilter.h in Headers */, +@@ -18002,6 +18074,8 @@ 939EF87029D112EE00F23AEE /* WebPageInlines.h in Headers */, 9197940823DBC4CB00257892 /* WebPageInspectorAgentBase.h in Headers */, A513F5402154A5D700662841 /* WebPageInspectorController.h in Headers */, @@ -19247,7 +19174,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 A543E30C215C8A8D00279CD9 /* WebPageInspectorTarget.h in Headers */, A543E30D215C8A9000279CD9 /* WebPageInspectorTargetController.h in Headers */, A543E307215AD13700279CD9 /* WebPageInspectorTargetFrontendChannel.h in Headers */, -@@ -20613,7 +20687,43 @@ +@@ -20621,7 +20695,43 @@ 522F792928D50EBB0069B45B /* HidService.mm in Sources */, 2749F6442146561B008380BF /* InjectedBundleNodeHandle.cpp in Sources */, 2749F6452146561E008380BF /* InjectedBundleRangeHandle.cpp in Sources */, @@ -19291,9 +19218,9 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 1C5DC45F2909B05A0061EC62 /* JSWebExtensionWrapperCocoa.mm in Sources */, C14D37FE24ACE086007FF014 /* LaunchServicesDatabaseManager.mm in Sources */, C1710CF724AA643200D7C112 /* LaunchServicesDatabaseObserver.mm in Sources */, -@@ -21013,6 +21123,8 @@ - 074E87E12CF8EA3D0059E469 /* WebPage+NavigationDeciding.swift in Sources */, +@@ -21023,6 +21133,8 @@ 078B04A02CF18EAB00B453A6 /* WebPage+NavigationPreferences.swift in Sources */, + 071467782DFE84E500F77867 /* WebPage+Transferable.swift in Sources */, 07CB79962CE9435700199C49 /* WebPage.swift in Sources */, + D79902B1236E9404005D6F7E /* WebPageInspectorEmulationAgentMac.mm in Sources */, + D79902B3236E9404005D6F7E /* WebPageInspectorInputAgentMac.mm in Sources */, @@ -19301,7 +19228,7 @@ index 7504d2cdd6d31bdfb1892dab8e1716253ce54ccc..c1ecef732f88cbf5c6277a0aa5c0d488 079A4DA12D72CC0D00CA387F /* WebPageWebView.swift in Sources */, CA2506A82DD65327001D1954 /* WebPageWebViewAdditions.swift in Sources */, diff --git a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp -index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd62346268 100644 +index 8e116cc2cd63a59282b330acb00f42a926ecb33b..1b979a29a149803177300c93aec4548ee42dec6c 100644 --- a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp +++ b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp @@ -271,6 +271,11 @@ void WebLoaderStrategy::scheduleLoad(ResourceLoader& resourceLoader, CachedResou @@ -19394,7 +19321,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd loadParameters.isMainFrameNavigation = isMainFrameNavigation; if (loadParameters.isMainFrameNavigation && document) -@@ -585,6 +583,25 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -589,6 +587,25 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL if (RefPtr frameLoader = resourceLoader.frameLoader()) loadParameters.requiredCookiesVersion = frameLoader->requiredCookiesVersion(); @@ -19420,7 +19347,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd std::optional existingNetworkResourceLoadIdentifierToResume; if (loadParameters.isMainFrameNavigation) existingNetworkResourceLoadIdentifierToResume = std::exchange(m_existingNetworkResourceLoadIdentifierToResume, std::nullopt); -@@ -599,7 +616,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL +@@ -603,7 +620,7 @@ void WebLoaderStrategy::scheduleLoadFromNetworkProcess(ResourceLoader& resourceL } auto loader = WebResourceLoader::create(resourceLoader, trackingParameters); @@ -19429,7 +19356,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd } void WebLoaderStrategy::scheduleInternallyFailedLoad(WebCore::ResourceLoader& resourceLoader) -@@ -1017,7 +1034,7 @@ void WebLoaderStrategy::didFinishPreconnection(WebCore::ResourceLoaderIdentifier +@@ -1021,7 +1038,7 @@ void WebLoaderStrategy::didFinishPreconnection(WebCore::ResourceLoaderIdentifier bool WebLoaderStrategy::isOnLine() const { @@ -19438,7 +19365,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd } void WebLoaderStrategy::addOnlineStateChangeListener(Function&& listener) -@@ -1044,6 +1061,11 @@ void WebLoaderStrategy::isResourceLoadFinished(CachedResource& resource, Complet +@@ -1047,6 +1064,11 @@ void WebLoaderStrategy::isResourceLoadFinished(CachedResource& resource, Complet void WebLoaderStrategy::setOnLineState(bool isOnLine) { @@ -19450,7 +19377,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd if (m_isOnLine == isOnLine) return; -@@ -1052,6 +1074,12 @@ void WebLoaderStrategy::setOnLineState(bool isOnLine) +@@ -1055,6 +1077,12 @@ void WebLoaderStrategy::setOnLineState(bool isOnLine) listener(isOnLine); } @@ -19464,7 +19391,7 @@ index bd3aaaf9309aec01820d0950d3b6f14a23c6076c..8cdb9a0c339c0499034fa801395367dd { WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::SetCaptureExtraNetworkLoadMetricsEnabled(enabled), 0); diff --git a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h -index 6492031de6ed6effab3f28e5321419f3390f7651..678139fc26f26e2224a70b0087ddb849280b8ace 100644 +index af1f8eb6b578367e8cb818b1c6020377c5a07a37..41f1a38348b11aa0d99fc9fa429fe6b88b41dbab 100644 --- a/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h +++ b/Source/WebKit/WebProcess/Network/WebLoaderStrategy.h @@ -26,6 +26,7 @@ @@ -19494,7 +19421,7 @@ index 6492031de6ed6effab3f28e5321419f3390f7651..678139fc26f26e2224a70b0087ddb849 } // namespace WebKit diff --git a/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp b/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp -index ce50de02306dfe542299fbb9e534a6db929b8052..5c0a3215a78f3fdcf19fd8bb56c2a9c5596240ed 100644 +index 6d448ac1d302be3e9ad938ade807d26ddd39d103..06d1995872db8de9edc0ad14f0ab556dec75eb7a 100644 --- a/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp +++ b/Source/WebKit/WebProcess/Network/WebResourceLoader.cpp @@ -202,9 +202,6 @@ void WebResourceLoader::didReceiveResponse(ResourceResponse&& response, PrivateR @@ -19517,10 +19444,10 @@ index ce50de02306dfe542299fbb9e534a6db929b8052..5c0a3215a78f3fdcf19fd8bb56c2a9c5 } diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp -index 78e75b9ebfa13611e7f2f9df4395a586ed9db55e..ad6db691640a56bba7fb153bd4a71eb6b618bb50 100644 +index de8c8f2890b146c6dd4f34c121b92402900e8df5..11792945a16cca303a9b2d3c101edf835f60b398 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp -@@ -532,6 +532,9 @@ void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel lev +@@ -534,6 +534,9 @@ void WebChromeClient::addMessageToConsole(MessageSource source, MessageLevel lev if (!page) return; @@ -19531,11 +19458,11 @@ index 78e75b9ebfa13611e7f2f9df4395a586ed9db55e..ad6db691640a56bba7fb153bd4a71eb6 page->injectedBundleUIClient().willAddMessageToConsole(page.get(), source, level, message, lineNumber, columnNumber, sourceID); diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebDragClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebDragClient.cpp -index 2f5b6d189eae2e153847cbf8496805a2660d1105..837f71ee8d9e0b0fb8ae1841f6820431f592dfd8 100644 +index dca2495f0196552e79c73bb0b7466b5c4fa0be46..999a7ab4eef2b8cc1a65b5a26cd69e369dcb7ab7 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/WebDragClient.cpp +++ b/Source/WebKit/WebProcess/WebCoreSupport/WebDragClient.cpp @@ -53,7 +53,7 @@ OptionSet WebDragClient::dragSourceActionMaskForPoint(const In - return m_page->allowedDragSourceActions(); + return protectedPage()->allowedDragSourceActions(); } -#if !PLATFORM(COCOA) && !PLATFORM(GTK) @@ -19543,8 +19470,20 @@ index 2f5b6d189eae2e153847cbf8496805a2660d1105..837f71ee8d9e0b0fb8ae1841f6820431 void WebDragClient::startDrag(DragItem, DataTransfer&, Frame&, const std::optional&) { } +diff --git a/Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.cpp b/Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.cpp +index bb924c778ae3999d549b14e6d810b32167baece4..341355502afeaf69827308fee41ce964c0c66541 100644 +--- a/Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.cpp ++++ b/Source/WebKit/WebProcess/WebCoreSupport/WebUserMediaClient.cpp +@@ -26,6 +26,7 @@ + #include "UserMediaPermissionRequestManager.h" + #include "WebPage.h" + #include "WebPageProxyMessages.h" ++#include + #include + #include + #include diff --git a/Source/WebKit/WebProcess/WebCoreSupport/mac/WebDragClientMac.mm b/Source/WebKit/WebProcess/WebCoreSupport/mac/WebDragClientMac.mm -index cf8aa32077a57057649722d73358ac91b7130551..b555eb998bab677a452c37e78935ea9bad2196fc 100644 +index 0abeb42354ed65e20d5ad5ab72c31bf00e49d528..259caa5c016b7f5c7eab156bbe4f0ce105339f76 100644 --- a/Source/WebKit/WebProcess/WebCoreSupport/mac/WebDragClientMac.mm +++ b/Source/WebKit/WebProcess/WebCoreSupport/mac/WebDragClientMac.mm @@ -128,7 +128,8 @@ static WebCore::CachedImage* cachedImage(Element& element) @@ -19699,7 +19638,7 @@ index 5fed15fd5dbbfbaff305ce26a8bcf5ba3d0435d9..fec04b9a4d640dcf47e0a2312d8b7123 #include #include diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h -index 31af5390779e2ec5f3bc3dc0bbddf56f2617657a..df7af98f4d38f380ee5c0b607570659260a955e0 100644 +index 5c8694cb4229ee87c50f68f8c24b47e5598a5aee..581637fb988e0d9b465a7029fb1bac9884c09b6c 100644 --- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h +++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/LayerTreeHost.h @@ -145,6 +145,7 @@ public: @@ -19711,7 +19650,7 @@ index 31af5390779e2ec5f3bc3dc0bbddf56f2617657a..df7af98f4d38f380ee5c0b6075706592 void updateRootLayer(); WebCore::FloatRect visibleContentsRect() const; diff --git a/Source/WebKit/WebProcess/WebPage/DrawingArea.cpp b/Source/WebKit/WebProcess/WebPage/DrawingArea.cpp -index c07ca9e847d03c27877cf650b67b96b3c3f7bddb..b966edc8778d4a68c6e5bae0c0883ab3bfd0a387 100644 +index e4b6fc981ce302979fd3280e9602bcbea91b9d1f..9ed589d3f29b17c18018798642f34fa375afc1a2 100644 --- a/Source/WebKit/WebProcess/WebPage/DrawingArea.cpp +++ b/Source/WebKit/WebProcess/WebPage/DrawingArea.cpp @@ -27,6 +27,7 @@ @@ -19723,7 +19662,7 @@ index c07ca9e847d03c27877cf650b67b96b3c3f7bddb..b966edc8778d4a68c6e5bae0c0883ab3 #include "WebPage.h" #include "WebPageCreationParameters.h" diff --git a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp b/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp -index 412dd3c46cb61ec5f3de076de61c34b6a32c6281..77c02eedfb60a7f816788452e20aaf3fbc434164 100644 +index c42601a113c5f4f390630b3f20328de17f4f7d31..1a8f3dcccae5c09220f0f519d275eeaba83b1a5e 100644 --- a/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp +++ b/Source/WebKit/WebProcess/WebPage/WebCookieJar.cpp @@ -44,6 +44,7 @@ @@ -19731,7 +19670,7 @@ index 412dd3c46cb61ec5f3de076de61c34b6a32c6281..77c02eedfb60a7f816788452e20aaf3f #include #include +#include - #include + #include #include #include @@ -447,6 +448,12 @@ void WebCookieJar::setOptInCookiePartitioningEnabled(bool enabled) @@ -19761,7 +19700,7 @@ index d5bac4fe6d5103b4e752a7219d7870d4cddcaf27..033effbb445f5da6db5798594e2dbef3 void setOptInCookiePartitioningEnabled(bool); #endif diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp -index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009da32851c 100644 +index 8913ab7dc32c76a251b57449ff31cc53f9a6b9e0..04beed37ef9f9702a41a75acc17cc98af4924ff6 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp +++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp @@ -244,6 +244,7 @@ @@ -19808,7 +19747,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 void WebPage::loadRequest(LoadParameters&& loadParameters) { WEBPAGE_RELEASE_LOG_FORWARDABLE(Loading, WEBPAGE_LOADREQUEST, loadParameters.navigationID ? loadParameters.navigationID->toUInt64() : 0, static_cast(loadParameters.shouldTreatAsContinuingLoad), loadParameters.request.isAppInitiated(), loadParameters.existingNetworkResourceLoadIdentifierToResume ? loadParameters.existingNetworkResourceLoadIdentifierToResume->toUInt64() : 0); -@@ -2295,7 +2318,9 @@ void WebPage::stopLoading() +@@ -2298,7 +2321,9 @@ void WebPage::stopLoading() void WebPage::stopLoadingDueToProcessSwap() { SetForScope isStoppingLoadingDueToProcessSwap(m_isStoppingLoadingDueToProcessSwap, true); @@ -19818,7 +19757,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 } bool WebPage::defersLoading() const -@@ -2871,7 +2896,7 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum +@@ -2874,7 +2899,7 @@ void WebPage::viewportPropertiesDidChange(const ViewportArguments& viewportArgum #if PLATFORM(IOS_FAMILY) if (m_viewportConfiguration.setViewportArguments(viewportArguments)) viewportConfigurationChanged(); @@ -19827,7 +19766,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 // Adjust view dimensions when using fixed layout. RefPtr localMainFrame = this->localMainFrame(); RefPtr view = localMainFrame ? localMainFrame->view() : nullptr; -@@ -3628,6 +3653,13 @@ void WebPage::flushDeferredScrollEvents() +@@ -3631,6 +3656,13 @@ void WebPage::flushDeferredScrollEvents() protectedCorePage()->flushDeferredScrollEvents(); } @@ -19841,7 +19780,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 void WebPage::flushDeferredDidReceiveMouseEvent() { if (auto info = std::exchange(m_deferredDidReceiveMouseEvent, std::nullopt)) -@@ -3902,6 +3934,97 @@ void WebPage::touchEvent(const WebTouchEvent& touchEvent, CompletionHandlersendMessageToTargetBackend(targetId, message); } @@ -19955,8 +19894,8 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 + void WebPage::insertNewlineInQuotedContent() { - RefPtr frame = protectedCorePage()->checkedFocusController()->focusedOrMainFrame(); -@@ -4233,6 +4366,7 @@ void WebPage::setMainFrameDocumentVisualUpdatesAllowed(bool allowed) + RefPtr frame = corePage()->focusController().focusedOrMainFrame(); +@@ -4236,6 +4369,7 @@ void WebPage::setMainFrameDocumentVisualUpdatesAllowed(bool allowed) void WebPage::show() { send(Messages::WebPageProxy::ShowPage()); @@ -19964,7 +19903,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 } void WebPage::setIsTakingSnapshotsForApplicationSuspension(bool isTakingSnapshotsForApplicationSuspension) -@@ -5448,7 +5582,7 @@ RefPtr WebPage::protectedNotificationPermi +@@ -5456,7 +5590,7 @@ RefPtr WebPage::protectedNotificationPermi #if ENABLE(DRAG_SUPPORT) @@ -19973,7 +19912,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 void WebPage::performDragControllerAction(DragControllerAction action, const IntPoint& clientPosition, const IntPoint& globalPosition, OptionSet draggingSourceOperationMask, SelectionData&& selectionData, OptionSet flags, CompletionHandler, DragHandlingMethod, bool, unsigned, IntRect, IntRect, std::optional)>&& completionHandler) { if (!m_page) -@@ -7961,6 +8095,10 @@ void WebPage::didCommitLoad(WebFrame* frame) +@@ -7952,6 +8086,10 @@ void WebPage::didCommitLoad(WebFrame* frame) m_needsFixedContainerEdgesUpdate = true; flushDeferredDidReceiveMouseEvent(); @@ -19984,7 +19923,7 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 } void WebPage::didFinishDocumentLoad(WebFrame& frame) -@@ -8270,6 +8408,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, ResourceReq +@@ -8261,6 +8399,9 @@ Ref WebPage::createDocumentLoader(LocalFrame& frame, ResourceReq m_allowsContentJavaScriptFromMostRecentNavigation = m_internals->pendingWebsitePolicies->allowsContentJavaScript; WebsitePoliciesData::applyToDocumentLoader(*std::exchange(m_internals->pendingWebsitePolicies, std::nullopt), documentLoader); } @@ -19995,10 +19934,10 @@ index eebb80386522babc1ae192dbc470facea0b76e6b..02da88a1fd0d655faf69c7d3bd348009 return documentLoader; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h -index 360c7068495346fa326a3409afe64c03ff11506e..263acb1412dd593a45a264c7fbd3fc16582db710 100644 +index 47921d36d270fc271bf966dfe183129993ac7336..91da48500f900ddb3b0eb4e99dbd43ef92230dcf 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.h +++ b/Source/WebKit/WebProcess/WebPage/WebPage.h -@@ -47,6 +47,7 @@ +@@ -46,6 +46,7 @@ #include #include #include @@ -20052,7 +19991,7 @@ index 360c7068495346fa326a3409afe64c03ff11506e..263acb1412dd593a45a264c7fbd3fc16 void insertNewlineInQuotedContent(); -@@ -1969,6 +1978,7 @@ public: +@@ -1972,6 +1981,7 @@ public: void showContextMenuFromFrame(const FrameInfoData&, const ContextMenuContextData&, const UserData&); #endif void loadRequest(LoadParameters&&); @@ -20060,7 +19999,7 @@ index 360c7068495346fa326a3409afe64c03ff11506e..263acb1412dd593a45a264c7fbd3fc16 void setObscuredContentInsets(const WebCore::FloatBoxExtent&); -@@ -2171,6 +2181,7 @@ private: +@@ -2176,6 +2186,7 @@ private: void updatePotentialTapSecurityOrigin(const WebTouchEvent&, bool wasHandled); #elif ENABLE(TOUCH_EVENTS) void touchEvent(const WebTouchEvent&, CompletionHandler, bool)>&&); @@ -20068,7 +20007,7 @@ index 360c7068495346fa326a3409afe64c03ff11506e..263acb1412dd593a45a264c7fbd3fc16 #endif void cancelPointer(WebCore::PointerID, const WebCore::IntPoint&); -@@ -2941,6 +2952,7 @@ private: +@@ -2947,6 +2958,7 @@ private: bool m_isAppNapEnabled { true }; Markable m_pendingNavigationID; @@ -20077,10 +20016,10 @@ index 360c7068495346fa326a3409afe64c03ff11506e..263acb1412dd593a45a264c7fbd3fc16 bool m_mainFrameProgressCompleted { false }; bool m_shouldDispatchFakeMouseMoveEvents { true }; diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in -index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d99dc0ffc 100644 +index a2bdf0b62ddcb75a5c834b8aed481e1b14d5737a..e09d2ae7bbe7a1f0b34b84d6a97d2593e89b39f4 100644 --- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in +++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in -@@ -58,10 +58,13 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -60,10 +60,13 @@ messages -> WebPage WantsAsyncDispatchMessage { MouseEvent(WebCore::FrameIdentifier frameID, WebKit::WebMouseEvent event, std::optional> sandboxExtensions) SetLastKnownMousePosition(WebCore::FrameIdentifier frameID, WebCore::IntPoint eventPoint, WebCore::IntPoint globalPoint); @@ -20095,7 +20034,7 @@ index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d SetOverrideViewportArguments(struct std::optional arguments) DynamicViewportSizeUpdate(struct WebKit::DynamicViewportSizeUpdate target) -@@ -153,6 +156,7 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -155,6 +158,7 @@ messages -> WebPage WantsAsyncDispatchMessage { ConnectInspector(String targetId, Inspector::FrontendChannel::ConnectionType connectionType) DisconnectInspector(String targetId) SendMessageToTargetBackend(String targetId, String message) @@ -20103,7 +20042,7 @@ index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d #if ENABLE(REMOTE_INSPECTOR) SetIndicating(bool indicating); -@@ -163,6 +167,7 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -165,6 +169,7 @@ messages -> WebPage WantsAsyncDispatchMessage { #endif #if !ENABLE(IOS_TOUCH_EVENTS) && ENABLE(TOUCH_EVENTS) TouchEvent(WebKit::WebTouchEvent event) -> (enum:uint8_t std::optional eventType, bool handled) @@ -20111,7 +20050,7 @@ index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d #endif CancelPointer(WebCore::PointerID pointerId, WebCore::IntPoint documentPoint) -@@ -188,6 +193,7 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -190,6 +195,7 @@ messages -> WebPage WantsAsyncDispatchMessage { LoadDataInFrame(std::span data, String MIMEType, String encodingName, URL baseURL, WebCore::FrameIdentifier frameID) LoadRequest(struct WebKit::LoadParameters loadParameters) LoadDidCommitInAnotherProcess(WebCore::FrameIdentifier frameID, std::optional layerHostingContextIdentifier) @@ -20119,7 +20058,7 @@ index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d LoadRequestWaitingForProcessLaunch(struct WebKit::LoadParameters loadParameters, URL resourceDirectoryURL, WebKit::WebPageProxyIdentifier pageID, bool checkAssumedReadAccessToResourceURL) LoadData(struct WebKit::LoadParameters loadParameters) LoadSimulatedRequestAndResponse(struct WebKit::LoadParameters loadParameters, WebCore::ResourceResponse simulatedResponse) -@@ -352,10 +358,10 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -354,10 +360,10 @@ messages -> WebPage WantsAsyncDispatchMessage { RemoveLayerForFindOverlay() -> () # Drag and drop. @@ -20132,7 +20071,7 @@ index c4ae18f9803e00d09a3f479c03ec858dd98c9045..9b1cbcf627f104e97a7ca46b560cdb9d PerformDragControllerAction(std::optional frameID, enum:uint8_t WebKit::DragControllerAction action, WebCore::DragData dragData) -> (enum:uint8_t std::optional dragOperation, enum:uint8_t WebCore::DragHandlingMethod dragHandlingMethod, bool mouseIsOverFileInput, unsigned numberOfItemsToBeAccepted, WebCore::IntRect insertionRect, WebCore::IntRect editableElementRect, struct std::optional remoteUserInputEventData) PerformDragOperation(WebCore::DragData dragData, WebKit::SandboxExtensionHandle sandboxExtensionHandle, Vector sandboxExtensionsForUpload) -> (bool handled) #endif -@@ -375,6 +381,10 @@ messages -> WebPage WantsAsyncDispatchMessage { +@@ -377,6 +383,10 @@ messages -> WebPage WantsAsyncDispatchMessage { ModelDragEnded(WebCore::ElementIdentifier elementID) #endif @@ -20182,10 +20121,10 @@ index 40ec42bb4f998774a2ce4a19e82f68512ad2ebb8..080794e14bfbb3a336d8a89791baee0e const auto& availableInputs = WebProcess::singleton().availableInputDevices(); if (availableInputs.contains(AvailableInputDevices::Mouse)) diff --git a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm -index e9dc2250f0f5e68db6ae61f5f734e2e2cf24cf9b..3dc31fc41d8efd990f94a713c0490abf1f1d9f60 100644 +index e36cd746c4a8d35d076b68fd76ccd211a79d63a6..156cacab648a4ad53b854bf880c924bbe13ca4b6 100644 --- a/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm +++ b/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm -@@ -705,21 +705,37 @@ String WebPage::platformUserAgent(const URL&) const +@@ -701,21 +701,37 @@ String WebPage::platformUserAgent(const URL&) const bool WebPage::hoverSupportedByPrimaryPointingDevice() const { @@ -20274,7 +20213,7 @@ index ea3a03b5ee6d4ecadd771314c6059268db917087..91be6f4c687157afcfdaa431d7a1a6ff } diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp -index 230b9bb1f8b1f218f94c10f18f4d672286ce5a9d..c9741c132685eb27737d21c7c840edb9447ec596 100644 +index fe47c90bccb9a567803485e7313e093b48ad4d4c..ec76a5edc15a914d1f00955493625f6c135d75ce 100644 --- a/Source/WebKit/WebProcess/WebProcess.cpp +++ b/Source/WebKit/WebProcess/WebProcess.cpp @@ -93,6 +93,7 @@ @@ -20309,7 +20248,7 @@ index 230b9bb1f8b1f218f94c10f18f4d672286ce5a9d..c9741c132685eb27737d21c7c840edb9 } void WebProcess::initializeConnection(IPC::Connection* connection) -@@ -975,6 +986,8 @@ void WebProcess::createWebPage(PageIdentifier pageID, WebPageCreationParameters& +@@ -979,6 +990,8 @@ void WebProcess::createWebPage(PageIdentifier pageID, WebPageCreationParameters& accessibilityRelayProcessSuspended(false); } ASSERT(result.iterator->value); @@ -20319,10 +20258,10 @@ index 230b9bb1f8b1f218f94c10f18f4d672286ce5a9d..c9741c132685eb27737d21c7c840edb9 void WebProcess::removeWebPage(PageIdentifier pageID) diff --git a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm b/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm -index 9dd01c8ef00f00d49d54a16996142681d1486852..51a02893b48bc930ebca7dd4b76dc874beecb157 100644 +index 01f0546169ab61e79b730ac1fe4bded1b80ade9d..f274262554df2cc924e85b79467e16110e12af43 100644 --- a/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm +++ b/Source/WebKitLegacy/mac/WebView/WebHTMLView.mm -@@ -4225,7 +4225,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END +@@ -4219,7 +4219,7 @@ ALLOW_DEPRECATED_DECLARATIONS_END _private->handlingMouseDownEvent = NO; } @@ -20332,10 +20271,10 @@ index 9dd01c8ef00f00d49d54a16996142681d1486852..51a02893b48bc930ebca7dd4b76dc874 - (void)touch:(WebEvent *)event { diff --git a/Source/WebKitLegacy/mac/WebView/WebView.mm b/Source/WebKitLegacy/mac/WebView/WebView.mm -index 222de8db3d8475b7a68b017dc88994d4ca7e08fd..ed3460c7b59772410af898d4fb107cc88c3b40a6 100644 +index 30baa8fa7a4f030680c683eebb7e93f9723da373..f236d24511f25dcbcf84af26fe29d8eb3742512d 100644 --- a/Source/WebKitLegacy/mac/WebView/WebView.mm +++ b/Source/WebKitLegacy/mac/WebView/WebView.mm -@@ -4002,7 +4002,7 @@ + (void)_doNotStartObservingNetworkReachability +@@ -3972,7 +3972,7 @@ + (void)_doNotStartObservingNetworkReachability } #endif // PLATFORM(IOS_FAMILY) @@ -20344,7 +20283,7 @@ index 222de8db3d8475b7a68b017dc88994d4ca7e08fd..ed3460c7b59772410af898d4fb107cc8 - (NSArray *)_touchEventRegions { -@@ -4044,7 +4044,7 @@ - (NSArray *)_touchEventRegions +@@ -4014,7 +4014,7 @@ - (NSArray *)_touchEventRegions }).autorelease(); } @@ -20387,7 +20326,7 @@ index 0000000000000000000000000000000000000000..a9db9ec38d05e36517414248237e885b + LIBVPX_LIBRARIES +) diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake -index 303550b9618e8c621e246e8c95b9e7542585f1fc..efdb66ef47e46e83287350933047295e6dc0c268 100644 +index 7b9783fe14ac1eca2c6a48e42b74e6f294359b6d..584c9f2ea30a8f670b18ef271ca2f0681c637fdc 100644 --- a/Source/cmake/OptionsGTK.cmake +++ b/Source/cmake/OptionsGTK.cmake @@ -9,6 +9,10 @@ set(USER_AGENT_BRANDING "" CACHE STRING "Branding to add to user agent string") @@ -20423,7 +20362,7 @@ index 303550b9618e8c621e246e8c95b9e7542585f1fc..efdb66ef47e46e83287350933047295e SET_AND_EXPOSE_TO_BUILD(ENABLE_DEVELOPER_MODE ${DEVELOPER_MODE}) if (DEVELOPER_MODE) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_API_TESTS PRIVATE ON) -@@ -147,6 +159,20 @@ endif () +@@ -148,6 +160,21 @@ endif () WEBKIT_OPTION_DEPEND(ENABLE_GPU_PROCESS USE_GBM) @@ -20432,6 +20371,7 @@ index 303550b9618e8c621e246e8c95b9e7542585f1fc..efdb66ef47e46e83287350933047295e +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PDFJS PUBLIC OFF) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_RECORDER PRIVATE OFF) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_THUNDER PRIVATE OFF) ++WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBXR PRIVATE OFF) + +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_APPLICATION_MANIFEST PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CURSOR_VISIBILITY PRIVATE ON) @@ -20443,9 +20383,9 @@ index 303550b9618e8c621e246e8c95b9e7542585f1fc..efdb66ef47e46e83287350933047295e + include(GStreamerDependencies) - # Finalize the value for all options. Do not attempt to use an option before + WEBKIT_OPTION_DEPEND(ENABLE_WEBXR ENABLE_GAMEPAD) diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake -index 45b4da4575d41bdf1b829ddf68947f30bc57734f..378b9114ae03c18f0caead71750c1169b2c45b27 100644 +index d04d2c2504b829f240633a9c7e3de15876190ba0..9f3a7febd7374a60a55c1d6aaee50cc26df59fc6 100644 --- a/Source/cmake/OptionsWPE.cmake +++ b/Source/cmake/OptionsWPE.cmake @@ -23,6 +23,9 @@ find_package(WebP REQUIRED COMPONENTS demux) @@ -20458,7 +20398,7 @@ index 45b4da4575d41bdf1b829ddf68947f30bc57734f..378b9114ae03c18f0caead71750c1169 WEBKIT_OPTION_BEGIN() SET_AND_EXPOSE_TO_BUILD(ENABLE_DEVELOPER_MODE ${DEVELOPER_MODE}) -@@ -81,6 +84,21 @@ else () +@@ -81,6 +84,22 @@ else () WEBKIT_OPTION_DEFAULT_PORT_VALUE(USE_SKIA PRIVATE OFF) endif () @@ -20467,6 +20407,7 @@ index 45b4da4575d41bdf1b829ddf68947f30bc57734f..378b9114ae03c18f0caead71750c1169 +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PDFJS PUBLIC OFF) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEDIA_RECORDER PRIVATE OFF) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_THUNDER PRIVATE OFF) ++WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBXR PRIVATE OFF) + +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_APPLICATION_MANIFEST PRIVATE ON) +WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CURSOR_VISIBILITY PRIVATE ON) @@ -20480,7 +20421,7 @@ index 45b4da4575d41bdf1b829ddf68947f30bc57734f..378b9114ae03c18f0caead71750c1169 # Public options specific to the WPE port. Do not add any options here unless # there is a strong reason we should support changing the value of the option, # and the option is not relevant to other WebKit ports. -@@ -116,6 +134,11 @@ WEBKIT_OPTION_DEPEND(USE_QT6 ENABLE_WPE_PLATFORM) +@@ -116,6 +135,11 @@ WEBKIT_OPTION_DEPEND(USE_QT6 ENABLE_WPE_PLATFORM) WEBKIT_OPTION_DEPEND(USE_SKIA_OPENTYPE_SVG USE_SKIA) WEBKIT_OPTION_DEPEND(USE_SYSTEM_SYSPROF_CAPTURE USE_SYSPROF_CAPTURE) @@ -20493,7 +20434,7 @@ index 45b4da4575d41bdf1b829ddf68947f30bc57734f..378b9114ae03c18f0caead71750c1169 WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_BUBBLEWRAP_SANDBOX PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MEMORY_SAMPLER PRIVATE ON) diff --git a/Source/cmake/OptionsWin.cmake b/Source/cmake/OptionsWin.cmake -index 95393f0a6063615b417eb3249d3838ad0e3024d8..beedf862d572718b8f0f5e6daa0f8d029a96ea77 100644 +index 1a8e2da4c2a377e0dbfd7f7b1bc7b5ce597d4a4e..cee24362b16102e6efb9e017c77810ca4bdf32d7 100644 --- a/Source/cmake/OptionsWin.cmake +++ b/Source/cmake/OptionsWin.cmake @@ -55,6 +55,10 @@ find_package(ZLIB 1.2.11 REQUIRED) @@ -20507,7 +20448,7 @@ index 95393f0a6063615b417eb3249d3838ad0e3024d8..beedf862d572718b8f0f5e6daa0f8d02 WEBKIT_OPTION_BEGIN() # FIXME: Most of these options should not be public. -@@ -113,6 +117,14 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_FTPDIR PRIVATE OFF) +@@ -112,6 +116,14 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_FTPDIR PRIVATE OFF) SET_AND_EXPOSE_TO_BUILD(ENABLE_WEBDRIVER_KEYBOARD_INTERACTIONS ON) SET_AND_EXPOSE_TO_BUILD(ENABLE_WEBDRIVER_MOUSE_INTERACTIONS ON) @@ -20938,7 +20879,7 @@ index d3fbb968ee463f86c64fecb855b46c8634b4b72d..01dbbfbb93f2cfa6eb6440cce9794ec9 g_clear_object(&interfaceSettings); diff --git a/Tools/MiniBrowser/wpe/main.cpp b/Tools/MiniBrowser/wpe/main.cpp -index 6a898a5bd79a4e6f2f26be7f63347cef7a5c0ab4..d895941dab0f7a0bdfafcf60f203aa82c46874a6 100644 +index 247a2dc5cf369dbf26434358de0898cb36cba100..1aff934e53a808df82ba4a59760455329d316e33 100644 --- a/Tools/MiniBrowser/wpe/main.cpp +++ b/Tools/MiniBrowser/wpe/main.cpp @@ -52,6 +52,9 @@ static gboolean headlessMode; @@ -21272,19 +21213,6 @@ index 1067b31bc989748dfcc5502209d36d001b9b239e..7629263fb8bc93dca6dfc01c75eed8d2 +if (ENABLE_WEBKIT) + add_subdirectory(Playwright/win) +endif () -diff --git a/Tools/Scripts/build-webkit b/Tools/Scripts/build-webkit -index fcbf4620e66e0a49396fb650f2da1fad43e8a6fc..7c38c04e082313cbd7513ba8b5626f377f6850d4 100755 ---- a/Tools/Scripts/build-webkit -+++ b/Tools/Scripts/build-webkit -@@ -278,7 +278,7 @@ if (isAppleCocoaWebKit()) { - push @projects, ("Source/WebKit"); - - if (!isEmbeddedWebKit()) { -- push @projects, ("Tools/MiniBrowser"); -+ push @projects, ("Tools/Playwright"); - - # WebInspectorUI must come after JavaScriptCore and WebCore but before WebKit and WebKit2 - my $webKitIndex = first { $projects[$_] eq "Source/WebKitLegacy" } 0..$#projects; diff --git a/Tools/WebKitTestRunner/CMakeLists.txt b/Tools/WebKitTestRunner/CMakeLists.txt index 9e53f459e444b9c10fc5248f0e8059df6c1e0041..c17c875a7dd3ca05c4489578ab32378bca45a7c9 100644 --- a/Tools/WebKitTestRunner/CMakeLists.txt @@ -21301,7 +21229,7 @@ index 9e53f459e444b9c10fc5248f0e8059df6c1e0041..c17c875a7dd3ca05c4489578ab32378b "${WebKitTestRunner_DIR}/InjectedBundle/Bindings/AccessibilityController.idl" "${WebKitTestRunner_DIR}/InjectedBundle/Bindings/AccessibilityTextMarker.idl" diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp -index ee6c2488fd655c40d250b405a82358e81e2661f6..5b9baf3c2ee0e9bb28fd765c7fdacb3a70f9ef36 100644 +index 567ccd8d7a4bdb3c576c60230123b78465da6505..75bf76270063878f3f1ded7b47eb2f8cc90271d8 100644 --- a/Tools/WebKitTestRunner/TestController.cpp +++ b/Tools/WebKitTestRunner/TestController.cpp @@ -713,6 +713,7 @@ PlatformWebView* TestController::createOtherPlatformWebView(PlatformWebView* par @@ -21484,7 +21412,7 @@ index df22308266c6f69d24a60905f8d05e4e80f21b9b..2d0838070dc10793418cbb648b095a5f static cairo_user_data_key_t bufferKey; cairo_surface_set_user_data(m_snapshot, &bufferKey, buffer, diff --git a/WebKit.xcworkspace/contents.xcworkspacedata b/WebKit.xcworkspace/contents.xcworkspacedata -index 3ad442a8691847c6921c5f66a805b7b0523b1e27..95407368a95d2f7d6fb697110912014cabe29afa 100644 +index 5d6492124eb520ce2b25f5c62775438103050ed3..1c0ea8a3258b5676b81fb00360e2c0589cc93e18 100644 --- a/WebKit.xcworkspace/contents.xcworkspacedata +++ b/WebKit.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ @@ -21498,11 +21426,11 @@ index 3ad442a8691847c6921c5f66a805b7b0523b1e27..95407368a95d2f7d6fb697110912014c location = "group:Source/bmalloc/bmalloc.xcodeproj"> diff --git a/WebKit.xcworkspace/xcshareddata/xcschemes/Everything up to WebKit + Tools.xcscheme b/WebKit.xcworkspace/xcshareddata/xcschemes/Everything up to WebKit + Tools.xcscheme -index ded307890926eaf0ca169aaef39ea08bd982a47a..2db0c0abdda702fdff9314ba341b63c5d09289bc 100644 +index 68732987fc02d863415360d066bc2a45d16ec39a..f4f924cb859034306361e9e55cdf31c6a1ca3b32 100644 --- a/WebKit.xcworkspace/xcshareddata/xcschemes/Everything up to WebKit + Tools.xcscheme +++ b/WebKit.xcworkspace/xcshareddata/xcschemes/Everything up to WebKit + Tools.xcscheme -@@ -188,6 +188,20 @@ - ReferencedContainer = "container:Tools/MobileMiniBrowser/MobileMiniBrowser.xcodeproj"> +@@ -202,6 +202,20 @@ + ReferencedContainer = "container:Tools/SwiftBrowser/SwiftBrowser.xcodeproj"> + -:::note -Only works with Chromium browser's persistent context. -::: - -Emitted when new background page is created in the context. - -```java -context.onBackgroundPage(backgroundPage -> { - System.out.println(backgroundPage.url()); -}); -``` - -```js -const backgroundPage = await context.waitForEvent('backgroundpage'); -``` - -```python async -background_page = await context.wait_for_event("backgroundpage") -``` - -```python sync -background_page = context.wait_for_event("backgroundpage") -``` - -```csharp -context.BackgroundPage += (_, backgroundPage) => -{ - Console.WriteLine(backgroundPage.Url); -}; - -``` +This event is not emitted. ## property: BrowserContext.clock * since: v1.45 @@ -456,13 +427,10 @@ Script to be evaluated in all pages in the browser context. Optional. ## method: BrowserContext.backgroundPages * since: v1.11 +* deprecated: Background pages have been removed from Chromium together with Manifest V2 extensions. - returns: <[Array]<[Page]>> -:::note -Background pages are only supported on Chromium-based browsers. -::: - -All existing background pages in the context. +Returns an empty list. ## method: BrowserContext.browser * since: v1.8 diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index 0859289a7fe66..a5a92939bc2f6 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -133,11 +133,11 @@ Locator button = page.getByRole(AriaRole.BUTTON).and(page.getByTitle("Subscribe" ``` ```python async -button = page.get_by_role("button").and_(page.getByTitle("Subscribe")) +button = page.get_by_role("button").and_(page.get_by_title("Subscribe")) ``` ```python sync -button = page.get_by_role("button").and_(page.getByTitle("Subscribe")) +button = page.get_by_role("button").and_(page.get_by_title("Subscribe")) ``` ```csharp diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index a4bb867483eff..c45b55a32f66d 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -2677,6 +2677,29 @@ Returns whether the element is [visible](../actionability.md#visible). [`param: * since: v1.8 - type: <[Keyboard]> + +## async method: Page.consoleMessages +* since: v1.56 +- returns: <[Array]<[ConsoleMessage]>> + +Returns up to (currently) 200 last console messages from this page. See [`event: Page.console`] for more details. + + +## async method: Page.pageErrors +* since: v1.56 +* langs: js, python +- returns: <[Array]<[Error]>> + +Returns up to (currently) 200 last page errors from this page. See [`event: Page.pageError`] for more details. + +## async method: Page.pageErrors +* since: v1.56 +* langs: csharp, java +- returns: <[Array]<[string]>> + +Returns up to (currently) 200 last page errors from this page. See [`event: Page.pageError`] for more details. + + ## method: Page.locator * since: v1.14 - returns: <[Locator]> @@ -3122,6 +3145,17 @@ return value resolves to `[]`. * since: v1.9 +## async method: Page.requests +* since: v1.56 +- returns: <[Array]<[Request]>> + +Returns up to (currently) 100 last network request from this page. See [`event: Page.request`] for more details. + +Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory growth as new requests come in. Once collected, retrieving most information about the request is impossible. + +Note that requests reported through the [`event: Page.request`] request are not collected, so there is a trade off between efficient memory usage with [`method: Page.requests`] and the amount of available information reported through [`event: Page.request`]. + + ## async method: Page.addLocatorHandler * since: v1.42 diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 8cb60f038f65a..993b8bc2964e6 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -235,14 +235,18 @@ Dangerous option; use with care. Defaults to `false`. Network proxy settings. +## js-browser-option-env +* langs: js +- `env` <[Object]<[string], [string]|[undefined]>> + ## csharp-java-browser-option-env * langs: csharp, java - `env` <[Object]<[string], [string]>> Specify environment variables that will be visible to the browser. Defaults to `process.env`. -## js-python-browser-option-env -* langs: js, python +## python-browser-option-env +* langs: python - `env` <[Object]<[string], [string]|[float]|[boolean]>> Specify environment variables that will be visible to the browser. Defaults to `process.env`. @@ -1122,7 +1126,8 @@ Slows down Playwright operations by the specified amount of milliseconds. Useful - %%-browser-option-devtools-%% - %%-browser-option-downloadspath-%% - %%-csharp-java-browser-option-env-%% -- %%-js-python-browser-option-env-%% +- %%-js-browser-option-env-%% +- %%-python-browser-option-env-%% - %%-browser-option-executablepath-%% - %%-browser-option-handlesigint-%% - %%-browser-option-handlesigterm-%% diff --git a/docs/src/ci-intro.md b/docs/src/ci-intro.md index 0ca8073923b00..c022f1e4a0e43 100644 --- a/docs/src/ci-intro.md +++ b/docs/src/ci-intro.md @@ -21,7 +21,7 @@ Playwright tests can be run on any CI provider. This guide covers one way of run ## Introduction * langs: python, java, csharp -Playwright tests can be run on any CI provider. In this section we cover running tests on GitHub using GitHub Actions. If you would like to see how to configure other CI providers, check out our detailed doc on Continuous Integration. +Playwright tests can be run on any CI provider. In this section we cover running tests on GitHub using GitHub Actions. If you would like to see how to configure other CI providers, check out our detailed [doc on Continuous Integration](./ci.md). #### You will learn * langs: python, java, csharp @@ -48,8 +48,8 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -94,11 +94,11 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: | python -m pip install --upgrade pip @@ -126,11 +126,11 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '25' - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress - name: Ensure browsers are installed @@ -151,9 +151,9 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - name: Build & Install diff --git a/docs/src/ci.md b/docs/src/ci.md index a6fa5bc44dc04..31615c0f2ddf2 100644 --- a/docs/src/ci.md +++ b/docs/src/ci.md @@ -82,8 +82,8 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -117,11 +117,11 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: | python -m pip install --upgrade pip @@ -149,11 +149,11 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '25' - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress - name: Ensure browsers are installed @@ -174,9 +174,9 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - name: Build & Install @@ -211,8 +211,8 @@ jobs: image: mcr.microsoft.com/playwright:v%%VERSION%%-noble options: --user 1001 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -236,11 +236,11 @@ jobs: image: mcr.microsoft.com/playwright/python:v%%VERSION%%-noble options: --user 1001 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: | python -m pip install --upgrade pip @@ -265,11 +265,11 @@ jobs: image: mcr.microsoft.com/playwright/java:v%%VERSION%%-noble options: --user 1001 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '25' - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress - name: Run tests @@ -291,9 +291,9 @@ jobs: image: mcr.microsoft.com/playwright/dotnet:v%%VERSION%%-noble options: --user 1001 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - run: dotnet build @@ -316,8 +316,8 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -340,10 +340,10 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v4 - uses: actions/setup-python@v4 + - uses: actions/checkout@v5 + uses: actions/setup-python@v6 with: - python-version: '3.11' + python-version: '3.13' - name: Install dependencies run: | python -m pip install --upgrade pip @@ -367,11 +367,11 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '17' + java-version: '25' - name: Build & Install run: mvn -B install -D skipTests --no-transfer-progress - name: Install Playwright @@ -393,9 +393,9 @@ jobs: runs-on: ubuntu-latest if: github.event.deployment_status.state == 'success' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 8.0.x - run: dotnet build @@ -427,12 +427,12 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # Force a non-shallow checkout, so that we can reference $GITHUB_BASE_REF. # See https://github.com/actions/checkout for more details. fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -475,9 +475,9 @@ pool: vmImage: ubuntu-latest steps: -- task: NodeTool@0 +- task: UseNode@1 inputs: - versionSpec: '18' + version: '22' displayName: 'Install Node.js' - script: npm ci displayName: 'npm ci' @@ -499,7 +499,7 @@ pool: steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.13' displayName: 'Use Python' - script: | python -m pip install --upgrade pip @@ -519,9 +519,9 @@ pool: vmImage: ubuntu-latest steps: -- task: JavaToolInstaller@0 +- task: JavaToolInstaller@1 inputs: - versionSpec: '17' + versionSpec: '25' jdkArchitectureOption: 'x64' jdkSourceOption: AzureStorage - script: mvn -B install -D skipTests --no-transfer-progress @@ -567,9 +567,9 @@ pool: vmImage: ubuntu-latest steps: -- task: NodeTool@0 +- task: UseNode@1 inputs: - versionSpec: '18' + version: '22' displayName: 'Install Node.js' - script: npm ci @@ -648,9 +648,9 @@ strategy: project: webkit shard: 3/3 steps: -- task: NodeTool@0 +- task: UseNode@1 inputs: - versionSpec: '18' + version: '22' displayName: 'Install Node.js' - script: npm ci @@ -675,9 +675,9 @@ pool: container: mcr.microsoft.com/playwright:v%%VERSION%%-noble steps: -- task: NodeTool@0 +- task: UseNode@1 inputs: - versionSpec: '18' + version: '22' displayName: 'Install Node.js' - script: npm ci @@ -699,7 +699,7 @@ container: mcr.microsoft.com/playwright/python:v%%VERSION%%-noble steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.11' + versionSpec: '3.13' displayName: 'Use Python' - script: | @@ -719,9 +719,9 @@ pool: container: mcr.microsoft.com/playwright/java:v%%VERSION%%-noble steps: -- task: JavaToolInstaller@0 +- task: JavaToolInstaller@1 inputs: - versionSpec: '17' + versionSpec: '25' jdkArchitectureOption: 'x64' jdkSourceOption: AzureStorage diff --git a/docs/src/debug.md b/docs/src/debug.md index 659b0a87b7c9c..833bbed111151 100644 --- a/docs/src/debug.md +++ b/docs/src/debug.md @@ -241,8 +241,6 @@ When running in Debug Mode with `PWDEBUG=console`, a `playwright` object is avai - **See console logs** during execution (or learn how to [read logs via API](./api/class-page.md#page-event-console)) - Check **network activity** and other developer tools features -This will also set the default timeouts of Playwright to 0 (= no timeout). - Browser Developer Tools with Playwright object To debug your tests using the browser developer tools, start by setting a breakpoint in your test to pause the execution using the [`method: Page.pause`] method. diff --git a/docs/src/images/test-agents/generator-prompt.png b/docs/src/images/test-agents/generator-prompt.png new file mode 100644 index 0000000000000..56c67cf3ce8b2 Binary files /dev/null and b/docs/src/images/test-agents/generator-prompt.png differ diff --git a/docs/src/images/test-agents/healer-prompt.png b/docs/src/images/test-agents/healer-prompt.png new file mode 100644 index 0000000000000..5b6116972cfb6 Binary files /dev/null and b/docs/src/images/test-agents/healer-prompt.png differ diff --git a/docs/src/images/test-agents/planner-prompt.png b/docs/src/images/test-agents/planner-prompt.png new file mode 100644 index 0000000000000..c99b07147575a Binary files /dev/null and b/docs/src/images/test-agents/planner-prompt.png differ diff --git a/docs/src/intro-csharp.md b/docs/src/intro-csharp.md index f48722c0706c9..a89a42ca83036 100644 --- a/docs/src/intro-csharp.md +++ b/docs/src/intro-csharp.md @@ -48,7 +48,7 @@ cd PlaywrightTests ```bash -dotnet new xunit -n PlaywrightTests +dotnet new xunit3 -n PlaywrightTests cd PlaywrightTests ``` @@ -284,7 +284,7 @@ See our doc on [Running and Debugging Tests](./running-tests.md) to learn more a ## System requirements - Playwright is distributed as a .NET Standard 2.0 library. We recommend .NET 8. -- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL). +- Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). - macOS 14 Ventura, or later. - Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. diff --git a/docs/src/intro-java.md b/docs/src/intro-java.md index 35530dd3eb3f2..12ce2702e185a 100644 --- a/docs/src/intro-java.md +++ b/docs/src/intro-java.md @@ -129,7 +129,7 @@ By default browsers launched with Playwright run headless, meaning no browser UI ## System requirements - Java 8 or higher. -- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL). +- Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). - macOS 14 Ventura, or later. - Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. diff --git a/docs/src/intro-js.md b/docs/src/intro-js.md index a9bce9752a29a..33827ae864e1b 100644 --- a/docs/src/intro-js.md +++ b/docs/src/intro-js.md @@ -305,7 +305,7 @@ pnpm exec playwright --version ## System requirements - Node.js: latest 20.x, 22.x or 24.x. -- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL). +- Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). - macOS 14 (Ventura) or later. - Debian 12 / 13, Ubuntu 22.04 / 24.04 (x86-64 or arm64). diff --git a/docs/src/intro-python.md b/docs/src/intro-python.md index b85fad8361c28..4045b6f4207de 100644 --- a/docs/src/intro-python.md +++ b/docs/src/intro-python.md @@ -100,7 +100,7 @@ pip install pytest-playwright playwright -U ## System requirements - Python 3.8 or higher. -- Windows 10+, Windows Server 2016+ or Windows Subsystem for Linux (WSL). +- Windows 11+, Windows Server 2019+ or Windows Subsystem for Linux (WSL). - macOS 14 Ventura, or later. - Debian 12, Debian 13, Ubuntu 22.04, Ubuntu 24.04, on x86-64 and arm64 architecture. diff --git a/docs/src/other-locators.md b/docs/src/other-locators.md index c7f6e234d1e7f..0fda4d2590aba 100644 --- a/docs/src/other-locators.md +++ b/docs/src/other-locators.md @@ -236,8 +236,10 @@ may be useful for specifying a list of extra conditions on an element. ### CSS: matching elements based on layout -:::note -Matching based on layout may produce unexpected results. For example, a different element could be matched when layout changes by one pixel. +:::warning +Layout selectors are deprecated and may be removed in the future. Matching based on layout may produce unexpected results. For example, a different element could be matched when layout changes by one pixel. + +We recommend prioritizing [user-visible locators](./locators.md#quick-guide) instead. ::: Sometimes, it is hard to come up with a good selector to the target element when it lacks distinctive features. In this case, using Playwright layout CSS pseudo-classes could help. These can be combined with regular CSS to pinpoint one of the multiple choices. diff --git a/docs/src/release-notes-csharp.md b/docs/src/release-notes-csharp.md index cb9df98dd64ba..f954e68cffca6 100644 --- a/docs/src/release-notes-csharp.md +++ b/docs/src/release-notes-csharp.md @@ -4,6 +4,33 @@ title: "Release notes" toc_max_heading_level: 2 --- +## Version 1.55 + +### Codegen + +- Automatic `ToBeVisibleAsync()` assertions: Codegen can now generate automatic `ToBeVisibleAsync()` assertions for common UI interactions. This feature can be enabled in the Codegen settings UI. + +### Breaking Changes + +- ⚠️ Dropped support for Chromium extension manifest v2. + +### Miscellaneous + +- Added support for Debian 13 "Trixie". +- Added support for Xunit v3 as part of [`Microsoft.Playwright.Xunit.v3`](https://www.nuget.org/packages/Microsoft.Playwright.Xunit.v3) +- Added support for MSTest v4 as part of [`Microsoft.Playwright.MSTest.v4`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest.v4) + +### Browser Versions + +- Chromium 140.0.7339.16 +- Mozilla Firefox 141.0 +- WebKit 26.0 + +This version was also tested against the following stable channels: + +- Google Chrome 139 +- Microsoft Edge 139 + ## Version 1.54 ### Highlights diff --git a/docs/src/release-notes-java.md b/docs/src/release-notes-java.md index a190d472a8a4b..ed8d74746afb2 100644 --- a/docs/src/release-notes-java.md +++ b/docs/src/release-notes-java.md @@ -4,6 +4,31 @@ title: "Release notes" toc_max_heading_level: 2 --- +## Version 1.55 + +### Codegen + +- Automatic `isVisible()` assertions: Codegen can now generate automatic `isVisible()` assertions for common UI interactions. This feature can be enabled in the Codegen settings UI. + +### Breaking Changes + +- ⚠️ Dropped support for Chromium extension manifest v2. + +### Miscellaneous + +- Added support for Debian 13 "Trixie". + +### Browser Versions + +- Chromium 140.0.7339.16 +- Mozilla Firefox 141.0 +- WebKit 26.0 + +This version was also tested against the following stable channels: + +- Google Chrome 139 +- Microsoft Edge 139 + ## Version 1.54 ### Highlights diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index d8d88ddf8dfcb..39f494d2b4b56 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,94 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.56 + + + +### Playwright Agents + +Introducing Playwright Agents, three custom agent definitions designed to guide LLMs through the core process of building a Playwright test: + +* **🎭 planner** explores the app and produces a Markdown test plan + +* **🎭 generator** transforms the Markdown plan into the Playwright Test files + +* **🎭 healer** executes the test suite and automatically repairs failing tests + +Run `npx playwright init-agents` with your client of choice to generate the latest agent definitions: + +```bash +# Generate agent files for each agentic loop +# Visual Studio Code +npx playwright init-agents --loop=vscode +# Claude Code +npx playwright init-agents --loop=claude +# opencode +npx playwright init-agents --loop=opencode +``` + +[Learn more about Playwright Agents](./test-agents.md) + +### New APIs + +- New methods [`method: Page.consoleMessages`] and [`method: Page.pageErrors`] for retrieving the most recent console messages from the page +- New method [`method: Page.requests`] for retrieving the most recent network requests from the page +- Added [`--test-list` and `--test-list-invert`](./test-cli.md#test-list) to allow manual specification of specific tests from a file + +### UI Mode and HTML Reporter + +- Added option to `'html'` reporter to disable the "Copy prompt" button +- Added option to `'html'` reporter and UI Mode to merge files, collapsing test and describe blocks into a single unified list +- Added option to UI Mode mirroring the `--update-snapshots` options +- Added option to UI Mode to run only a single worker at a time + +### Breaking Changes + +- Event [`event: BrowserContext.backgroundPage`] has been deprecated and will not be emitted. Method [`method: BrowserContext.backgroundPages`] will return an empty list + +### Miscellaneous + +- Aria snapshots render and compare `input` `placeholder` +- Added environment variable `PLAYWRIGHT_TEST` to Playwright worker processes to allow discriminating on testing status + +### Browser Versions + +- Chromium 141.0.7390.37 +- Mozilla Firefox 142.0.1 +- WebKit 26.0 + +## Version 1.55 + +### New APIs + +- New Property [`property: TestStepInfo.titlePath`] Returns the full title path starting from the test file, including test and step titles. + +### Codegen + +- Automatic `toBeVisible()` assertions: Codegen can now generate automatic `toBeVisible()` assertions for common UI interactions. This feature can be enabled in the Codegen settings UI. + +### Breaking Changes + +- ⚠️ Dropped support for Chromium extension manifest v2. + +### Miscellaneous + +- Added support for Debian 13 "Trixie". + +### Browser Versions + +- Chromium 140.0.7339.16 +- Mozilla Firefox 141.0 +- WebKit 26.0 + +This version was also tested against the following stable channels: + +- Google Chrome 139 +- Microsoft Edge 139 + ## Version 1.54 ### Highlights diff --git a/docs/src/release-notes-python.md b/docs/src/release-notes-python.md index 1a7d13fd56559..0ecd8eb89eed5 100644 --- a/docs/src/release-notes-python.md +++ b/docs/src/release-notes-python.md @@ -4,6 +4,31 @@ title: "Release notes" toc_max_heading_level: 2 --- +## Version 1.55 + +### Codegen + +- Automatic `to_be_visible()` assertions: Codegen can now generate automatic `to_be_visible()` assertions for common UI interactions. This feature can be enabled in the Codegen settings UI. + +### Breaking Changes + +- ⚠️ Dropped support for Chromium extension manifest v2. + +### Miscellaneous + +- Added support for Debian 13 "Trixie". + +### Browser Versions + +- Chromium 140.0.7339.16 +- Mozilla Firefox 141.0 +- WebKit 26.0 + +This version was also tested against the following stable channels: + +- Google Chrome 139 +- Microsoft Edge 139 + ## Version 1.54 ### Highlights diff --git a/docs/src/test-agents-js.md b/docs/src/test-agents-js.md new file mode 100644 index 0000000000000..b608ab7909cdb --- /dev/null +++ b/docs/src/test-agents-js.md @@ -0,0 +1,235 @@ +--- +id: test-agents +title: "Agents" +--- + +# Playwright Agents + +Playwright comes with three Playwright Agents out of the box: **🎭 planner**, **🎭 generator** and **🎭 healer**. + +These agents can be used independently, sequentially, or as the chained calls in the agentic loop. +Using them sequentially will produce test coverage for your product. + +* **🎭 planner** explores the app and produces a Markdown test plan + +* **🎭 generator** transforms the Markdown plan into the Playwright Test files + +* **🎭 healer** executes the test suite and automatically repairs failing tests + +### Getting Started + +Start with adding Playwright Agent definitions to your project using +the `init-agents` command. These definitions should be regenerated whenever Playwright +is updated to pick up new tools and instructions. + +```bash tab=bash-vscode +npx playwright init-agents --loop=vscode +``` + +```bash tab=bash-claude +npx playwright init-agents --loop=claude +``` + +```bash tab=bash-opencode +npx playwright init-agents --loop=opencode +``` + +Once the agents have been generated, you can use your AI tool of choice to command these agents to build Playwright Tests. + + +## 🎭 Planner + +Planner agent explores your app and produces a test plan for one or many scenarios and user flows. + +**Input** + +* A clear request to the planner (e.g., “Generate a plan for guest checkout.”) +* A `seed test` that sets up the environment necessary to interact with your app +* *(optional)* A Product Requirement Document (PRD) for context + +**Prompt** + +planner prompt + +> - Notice how the `seed.spec.ts` is included in the context of the planner. +> - Planner will run this test to execute all the initialization necessary for your test including the global setup, project dependencies and all the necessary fixtures and hooks. +> - Planner will also use this seed test as an example of all the generated tests. Alternatively, you can mention the file name in the prompt. + +```js title="Example: seed.spec.ts" +import { test, expect } from './fixtures'; + +test('seed', async ({ page }) => { + // this test uses custom fixtures from ./fixtures +}); +``` + +**Output** + +* A Markdown test plan saved as `specs/basic-operations.md`. +* The plan is human-readable but precise enough for test generation. + +
+Example: specs/basic-operations.md + +```markdown +# TodoMVC Application - Basic Operations Test Plan + +## Application Overview + +The TodoMVC application is a React-based todo list manager that demonstrates standard todo application functionality. The application provides comprehensive task management capabilities with a clean, intuitive interface. Key features include: + +- **Task Management**: Add, edit, complete, and delete individual todos +- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos +- **Filtering System**: View todos by All, Active, or Completed status with URL routing support +- **Real-time Counter**: Display of active (incomplete) todo count +- **Interactive UI**: Hover states, edit-in-place functionality, and responsive design +- **State Persistence**: Maintains state during session navigation + +## Test Scenarios + +### 1. Adding New Todos + +**Seed:** `tests/seed.spec.ts` + +#### 1.1 Add Valid Todo + +**Steps:** +1. Click in the "What needs to be done?" input field +2. Type "Buy groceries" +3. Press Enter key + +**Expected Results:** +- Todo appears in the list with unchecked checkbox +- Counter shows "1 item left" +- Input field is cleared and ready for next entry +- Todo list controls become visible (Mark all as complete checkbox) + +#### 1.2 Add Multiple Todos +... +``` +
+ +## 🎭 Generator + +Generator agent uses the Markdown plan to produce executable Playwright Tests. +It verifies selectors and assertions live as it performs the scenarios. Playwright supports +generation hints and provides a catalog of assertions for efficient structural and +behavioral validation. + +**Input** + +* Markdown plan from `specs/` + +**Prompt** + +generator prompt + +> - Notice how the `basic-operations.md` is included in the context of the generator. +> - This is how generator knows where to get the test plan from. Alternatively, you can mention the file name in the prompt. + +**Output** + +* A test suite under `tests/` +* Generated tests may include initial errors that can be healed automatically by the healer agent + +
+Example: tests/add-valid-todo.spec.ts + +```ts +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Valid Todo', async ({ page }) => { + // 1. Click in the "What needs to be done?" input field + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + + // 2. Type "Buy groceries" + await todoInput.fill('Buy groceries'); + + // 3. Press Enter key + await todoInput.press('Enter'); + + // Expected Results: + // - Todo appears in the list with unchecked checkbox + await expect(page.getByText('Buy groceries')).toBeVisible(); + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckbox).toBeVisible(); + await expect(todoCheckbox).not.toBeChecked(); + + // - Counter shows "1 item left" + await expect(page.getByText('1 item left')).toBeVisible(); + + // - Input field is cleared and ready for next entry + await expect(todoInput).toHaveValue(''); + await expect(todoInput).toBeFocused(); + + // - Todo list controls become visible (Mark all as complete checkbox) + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).toBeVisible(); + }); +}); +``` +
+ +## 🎭 Healer + +When the test fails, the healer agent: + +* Replays the failing steps +* Inspects the current UI to locate equivalent elements or flows +* Suggests a patch (e.g., locator update, wait adjustment, data fix) +* Re-runs the test until it passes or until guardrails stop the loop + +**Input** + +* Failing test name + +**Prompt** + +healer prompt + +**Output** + +* A passing test, or a skipped test if the healer believes the that functionality is broken. + +## Artifacts and Conventions + +The static agent definitions and generated files follow a simple, auditable structure: + +```bash +repo/ + .github/ # agent definitions + specs/ # human-readable test plans + basic-operations.md + tests/ # generated Playwright tests + seed.spec.ts # seed test for environment + tests/create/add-valid-todo.spec.ts + playwright.config.ts +``` + +### Agent Definitions + +Under the hood, agent definitions are collections of instructions and MCP tools. They are provided by +Playwright and should be regenerated whenever Playwright is updated. + +Example for Claude Code subagents: + +```bash +npx playwright init-agents --loop=vscode +``` + +### Specs in `specs/` + +Specs are structured plans describing scenarios in human-readable terms. They include +steps, expected outcomes, and data. Specs can start from scratch or extend a seed test. + +### Tests in `tests/` + +Generated Playwright tests, aligned one-to-one with specs wherever feasible. + +### Seed tests `seed.spec.ts` + +Seed tests provide a ready-to-use `page` context to bootstrap execution. diff --git a/docs/src/test-api/class-testconfig.md b/docs/src/test-api/class-testconfig.md index b0cf02ccc2d7e..fddbe8e3dd90a 100644 --- a/docs/src/test-api/class-testconfig.md +++ b/docs/src/test-api/class-testconfig.md @@ -287,7 +287,7 @@ export default defineConfig({ * since: v1.10 - type: ?<[Metadata]> -Metadata contains key-value pairs to be included in the report. For example, HTML report will display it as key-value pairs, and JSON report will include metadata serialized as json. +Metadata contains key-value pairs to be included in the report. For example, the JSON report will include metadata serialized as JSON. **Usage** @@ -627,7 +627,7 @@ export default defineConfig({ Whether to update expected snapshots with the actual results produced by the test run. Defaults to `'missing'`. * `'all'` - All tests that are executed will update snapshots. -* `'changed'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated. +* `'changed'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated. Also creates missing snapshots. * `'missing'` - Missing snapshots are created, for example when authoring a new test and running it for the first time. This is the default. * `'none'` - No snapshots are updated. diff --git a/docs/src/test-cli-js.md b/docs/src/test-cli-js.md index 0688e328d767a..d45ee1d6a3409 100644 --- a/docs/src/test-cli-js.md +++ b/docs/src/test-cli-js.md @@ -103,6 +103,8 @@ npx playwright test --ui | `--reporter ` | Reporter to use, comma-separated, can be "dot", "line", "list", or others (default: "list"). You can also pass a path to a custom reporter file. | | `--retries ` | Maximum retry count for flaky tests, zero for no retries (default: no retries). | | `--shard ` | Shard tests and execute only the selected shard, specified in the form "current/all", 1-based, e.g., "3/5". | +| `--test-list ` | Path to a file containing a list of tests to run. See [test list](#test-list) for details. | +| `--test-list-invert ` | Path to a file containing a list of tests to skip. See [test list](#test-list) for details. | | `--timeout ` | Specify test timeout threshold in milliseconds, zero for unlimited (default: 30 seconds). | | `--trace ` | Force tracing mode, can be `on`, `off`, `on-first-retry`, `on-all-retries`, `retain-on-failure`, `retain-on-first-failure`. | | `--tsconfig ` | Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately). | @@ -113,6 +115,29 @@ npx playwright test --ui | `--update-source-method [mode]` | Update snapshots with actual results. Possible values are "patch" (default), "3way" and "overwrite". "Patch" creates a unified diff file that can be used to update the source code later. "3way" generates merge conflict markers in source code. "Overwrite" overwrites the source code with the new snapshot values.| | `-x` | Stop after the first failure. | +#### Test list + +Options `--test-list` and `--test-list-invert` accept a path to a test list file. This file should list tests in the format similar to the output produced in `--list` mode. + +```txt +# This is a test list file. +# It can include comments and empty lines. + +# Fully qualified test with a project: +[chromium] › path/to/example.spec.ts:3:9 › suite › nested suite › example test + +# This test is included for all projects: +path/to/example.spec.ts:3:9 › example test + +# Use "›" or ">" as a separator: +[firefox] > example.spec.ts > suite > nested suite > example test + +# Line/column numbers are completely ignored, you can omit them. +# Three entries below refer to the same test: +example.spec.ts › example test +example.spec.ts:15 › example test +example.spec.ts:42:42 › example test +``` ### Show Report diff --git a/docs/src/test-configuration-js.md b/docs/src/test-configuration-js.md index e3831c19112d6..52e68c658c390 100644 --- a/docs/src/test-configuration-js.md +++ b/docs/src/test-configuration-js.md @@ -1,6 +1,6 @@ --- id: test-configuration -title: "Test configuration" +title: "Configuration" --- ## Introduction diff --git a/docs/src/test-fixtures-js.md b/docs/src/test-fixtures-js.md index 19fbf687c1b30..16afe6f25ead8 100644 --- a/docs/src/test-fixtures-js.md +++ b/docs/src/test-fixtures-js.md @@ -760,6 +760,8 @@ export const test = base.extend({ This is useful for non-interesting helper fixtures. For example, an [automatic](./test-fixtures.md#automatic-fixtures) fixture that sets up some common data can be safely hidden from a test report. +You can also mark the fixture as `box: 'self'` to only hide that particular fixture, but include all the steps inside the fixture in the test report. + ## Custom fixture title Instead of the usual fixture name, you can give fixtures a custom title that will be shown in test reports and error messages. diff --git a/docs/src/test-reporter-api/class-teststep.md b/docs/src/test-reporter-api/class-teststep.md index 84ba3abbda7f1..4c312ad2a75c5 100644 --- a/docs/src/test-reporter-api/class-teststep.md +++ b/docs/src/test-reporter-api/class-teststep.md @@ -14,7 +14,7 @@ Step category to differentiate steps with different origin and verbosity. Built- * `hook` for hooks initialization and teardown * `pw:api` for Playwright API calls. * `test.step` for test.step API calls. -* `test.attach` for test attachmen calls. +* `test.attach` for testInfo.attach API calls. ## property: TestStep.duration diff --git a/docs/src/test-reporters-js.md b/docs/src/test-reporters-js.md index f3393c6a25657..973cc1d64110a 100644 --- a/docs/src/test-reporters-js.md +++ b/docs/src/test-reporters-js.md @@ -252,6 +252,7 @@ HTML report supports the following configuration options and environment variabl | `PLAYWRIGHT_HTML_HOST` | `host` | When report opens in the browser, it will be served bound to this hostname. | `localhost` | `PLAYWRIGHT_HTML_PORT` | `port` | When report opens in the browser, it will be served on this port. | `9323` or any available port when `9323` is not available. | `PLAYWRIGHT_HTML_ATTACHMENTS_BASE_URL` | `attachmentsBaseURL` | A separate location where attachments from the `data` subdirectory are uploaded. Only needed when you upload report and `data` separately to different locations. | `data/` +| `PLAYWRIGHT_HTML_NO_COPY_PROMPT` | `noCopyPrompt` | If true, disable rendering of the Copy prompt for errors. Supports `true`, `1`, `false`, and `0`. | `false` | `PLAYWRIGHT_HTML_NO_SNIPPETS` | `noSnippets` | If true, disable rendering code snippets in the action log. If there is a top level error, that report section with code snippet will still render. Supports `true`, `1`, `false`, and `0`. | `false` ### Blob reporter diff --git a/docs/src/test-runners-csharp.md b/docs/src/test-runners-csharp.md index f193d73619780..194c56a2911da 100644 --- a/docs/src/test-runners-csharp.md +++ b/docs/src/test-runners-csharp.md @@ -124,7 +124,7 @@ xUnit v3 uses the [`conservative` parallelism algorithm](https://xunit.net/docs/ }> -To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example: +To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.NUnit.PageTest` or `Microsoft.Playwright.NUnit.ContextTest`. See the following example: ```csharp using Microsoft.Playwright.NUnit; diff --git a/docs/src/test-runners-python.md b/docs/src/test-runners-python.md index 1590f863c847a..4c1d4d43207dd 100644 --- a/docs/src/test-runners-python.md +++ b/docs/src/test-runners-python.md @@ -70,6 +70,7 @@ def test_my_app_is_working(fixture_name): - `browser_type_launch_args`: Override launch arguments for [`method: BrowserType.launch`]. It should return a Dict. - `browser_context_args`: Override the options for [`method: Browser.newContext`]. It should return a Dict. +- `connect_options`: Connect to an existing browser via WebSocket endpoint. It should return a Dict with [`method: BrowserType.connect`] options. Its also possible to override the context options ([`method: Browser.newContext`]) for a single test by using the `browser_context_args` marker: @@ -219,6 +220,18 @@ def browser_context_args(browser_context_args, playwright): Or via the CLI `--device="iPhone 11 Pro"` +### Connect to remote browsers + +```py title="conftest.py" +import pytest + +@pytest.fixture(scope="session") +def connect_options(): + return { + "wsEndpoint": "ws://localhost:8080/ws" + } +``` + ### Using with `unittest.TestCase` See the following example for using it with `unittest.TestCase`. This has a limitation, diff --git a/docs/src/test-sharding-js.md b/docs/src/test-sharding-js.md index 3dc517366cf6c..31dc23bc09030 100644 --- a/docs/src/test-sharding-js.md +++ b/docs/src/test-sharding-js.md @@ -101,8 +101,8 @@ jobs: shardIndex: [1, 2, 3, 4] shardTotal: [4] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies @@ -134,15 +134,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: lts/* - name: Install dependencies run: npm ci - name: Download blob reports from GitHub Actions Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: all-blob-reports pattern: blob-report-* diff --git a/docs/src/test-use-options-js.md b/docs/src/test-use-options-js.md index 0b46393941219..50c9246e8661d 100644 --- a/docs/src/test-use-options-js.md +++ b/docs/src/test-use-options-js.md @@ -1,6 +1,6 @@ --- id: test-use-options -title: "Test use options" +title: "Configuration (use)" --- ## Introduction diff --git a/docs/src/test-webserver-js.md b/docs/src/test-webserver-js.md index 8c0802c450812..65e1eecfda8c8 100644 --- a/docs/src/test-webserver-js.md +++ b/docs/src/test-webserver-js.md @@ -31,7 +31,7 @@ export default defineConfig({ | [`property: TestConfig.webServer`] | Launch a development web server (or multiple) during the tests. | | `command`| Shell command to start the local dev server of your app. | | `cwd` | Current working directory of the spawned process, defaults to the directory of the configuration file. | -| `env` | Environment variables to set for the command, `process.env` by default. | +| `env` | Environment variables for the command. Defaults to inheriting `process.env` with `PLAYWRIGHT_TEST=1` added. | | `gracefulShutdown` | How to shut down the process. If unspecified, the process group is forcefully `SIGKILL`ed. If set to `{ signal: 'SIGTERM', timeout: 500 }`, the process group is sent a `SIGTERM` signal, followed by `SIGKILL` if it doesn't exit within 500ms. You can also use `SIGINT` as the signal instead. A `0` timeout means no `SIGKILL` will be sent. Windows doesn't support `SIGTERM` and `SIGINT` signals, so this option is ignored on Windows. Note that shutting down a Docker container requires `SIGTERM`. | | `ignoreHTTPSErrors` | Whether to ignore HTTPS errors when fetching the `url`. Defaults to `false`. | | `name` | Specifies a custom name for the web server. This name will be prefixed to log messages. Defaults to `[WebServer]`. | diff --git a/eslint.config.mjs b/eslint.config.mjs index f11395ad00b33..4f38c9e450073 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -44,12 +44,14 @@ const ignores = [ "node_modules/", "output/", "**/playwright-report/", + "examples", "packages/*/lib/", "packages/playwright-core/bundles/zip/src/third_party/", "packages/playwright-core/src/generated/*", "packages/playwright-core/src/third_party/", "packages/playwright-core/types/*", "packages/playwright-ct-core/src/generated/*", + "packages/playwright/bundles/expect/third_party/", "packages/html-reporter/bundle.ts", "packages/html-reporter/playwright.config.ts", "packages/html-reporter/playwright/*", @@ -176,7 +178,7 @@ export const baseRules = { before: true, }, ], - "@stylistic/func-call-spacing": 2, + "@stylistic/function-call-spacing": 2, "@stylistic/type-annotation-spacing": 2, // file whitespace @@ -184,7 +186,7 @@ export const baseRules = { "no-mixed-spaces-and-tabs": 2, "no-trailing-spaces": 2, "linebreak-style": [process.platform === "win32" ? 0 : 2, "unix"], - indent: [ + "@stylistic/indent": [ 2, 2, { SwitchCase: 1, CallExpression: { arguments: 2 }, MemberExpression: 2 }, diff --git a/examples/todomvc/.claude/agents/playwright-test-generator.md b/examples/todomvc/.claude/agents/playwright-test-generator.md new file mode 100644 index 0000000000000..b82e9bc3a13cd --- /dev/null +++ b/examples/todomvc/.claude/agents/playwright-test-generator.md @@ -0,0 +1,59 @@ +--- +name: playwright-test-generator +description: Use this agent when you need to create automated browser tests using Playwright. Examples: Context: User wants to test a login flow on their web application. user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then verifies the dashboard page loads' assistant: 'I'll use the generator agent to create and validate this login test for you' The user needs a specific browser automation test created, which is exactly what the generator agent is designed for. Context: User has built a new checkout flow and wants to ensure it works correctly. user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the order?' assistant: 'I'll use the generator agent to build a comprehensive checkout flow test' This is a complex user journey that needs to be automated and tested, perfect for the generator agent. +tools: Glob, Grep, Read, mcp__playwright-test__browser_click, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_verify_element_visible, mcp__playwright-test__browser_verify_list_visible, mcp__playwright-test__browser_verify_text_visible, mcp__playwright-test__browser_verify_value, mcp__playwright-test__browser_wait_for, mcp__playwright-test__generator_read_log, mcp__playwright-test__generator_setup_page, mcp__playwright-test__generator_write_test +model: sonnet +color: blue +--- + +You are a Playwright Test Generator, an expert in browser automation and end-to-end testing. +Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate +application behavior. + +# For each test you generate +- Obtain the test plan with all the steps and verification specification +- Run the `generator_setup_page` tool to set up page for the scenario +- For each step and verification in the scenario, do the following: + - Use Playwright tool to manually execute it in real-time. + - Use the step description as the intent for each Playwright tool call. +- Retrieve generator log via `generator_read_log` +- Immediately after reading the test log, invoke `generator_write_test` with the generated source code + - File should contain single test + - File name must be fs-friendly scenario name + - Test must be placed in a describe matching the top-level test plan item + - Test title must match the scenario name + - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires + multiple actions. + - Always use best practices from the log when generating tests. + + + For following plan: + + ```markdown file=specs/plan.md + ### 1. Adding New Todos + **Seed:** `tests/seed.spec.ts` + + #### 1.1 Add Valid Todo + **Steps:** + 1. Click in the "What needs to be done?" input field + + #### 1.2 Add Multiple Todos + ... + ``` + + Following file is generated: + + ```ts file=add-valid-todo.spec.ts + // spec: specs/plan.md + // seed: tests/seed.spec.ts + + test.describe('Adding New Todos', () => { + test('Add Valid Todo', async { page } => { + // 1. Click in the "What needs to be done?" input field + await page.click(...); + + ... + }); + }); + ``` + \ No newline at end of file diff --git a/examples/todomvc/.claude/agents/playwright-test-healer.md b/examples/todomvc/.claude/agents/playwright-test-healer.md new file mode 100644 index 0000000000000..61efa5a1f5432 --- /dev/null +++ b/examples/todomvc/.claude/agents/playwright-test-healer.md @@ -0,0 +1,45 @@ +--- +name: playwright-test-healer +description: Use this agent when you need to debug and fix failing Playwright tests. Examples: Context: A developer has a failing Playwright test that needs to be debugged and fixed. user: 'The login test is failing, can you fix it?' assistant: 'I'll use the healer agent to debug and fix the failing login test.' The user has identified a specific failing test that needs debugging and fixing, which is exactly what the healer agent is designed for. Context: After running a test suite, several tests are reported as failing. user: 'Test user-registration.spec.ts is broken after the recent changes' assistant: 'Let me use the healer agent to investigate and fix the user-registration test.' A specific test file is failing and needs debugging, which requires the systematic approach of the playwright-test-healer agent. +tools: Glob, Grep, Read, Write, Edit, MultiEdit, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_generate_locator, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_snapshot, mcp__playwright-test__test_debug, mcp__playwright-test__test_list, mcp__playwright-test__test_run +model: sonnet +color: red +--- + +You are the Playwright Test Healer, an expert test automation engineer specializing in debugging and +resolving Playwright test failures. Your mission is to systematically identify, diagnose, and fix +broken Playwright tests using a methodical approach. + +Your workflow: +1. **Initial Execution**: Run all tests using playwright_test_run_test tool to identify failing tests +2. **Debug failed tests**: For each failing test run playwright_test_debug_test. +3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to: + - Examine the error details + - Capture page snapshot to understand the context + - Analyze selectors, timing issues, or assertion failures +4. **Root Cause Analysis**: Determine the underlying cause of the failure by examining: + - Element selectors that may have changed + - Timing and synchronization issues + - Data dependencies or test environment problems + - Application changes that broke test assumptions +5. **Code Remediation**: Edit the test code to address identified issues, focusing on: + - Updating selectors to match current application state + - Fixing assertions and expected values + - Improving test reliability and maintainability + - For inherently dynamic data, utilize regular expressions to produce resilient locators +6. **Verification**: Restart the test after each fix to validate the changes +7. **Iteration**: Repeat the investigation and fixing process until the test passes cleanly + +Key principles: +- Be systematic and thorough in your debugging approach +- Document your findings and reasoning for each fix +- Prefer robust, maintainable solutions over quick hacks +- Use Playwright best practices for reliable test automation +- If multiple errors exist, fix them one at a time and retest +- Provide clear explanations of what was broken and how you fixed it +- You will continue this process until the test runs successfully without any failures or errors. +- If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme() + so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead + of the expected behavior. +- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test. +- Never wait for networkidle or use other discouraged or deprecated apis \ No newline at end of file diff --git a/examples/todomvc/.claude/agents/playwright-test-planner.md b/examples/todomvc/.claude/agents/playwright-test-planner.md new file mode 100644 index 0000000000000..9e468a85d8e66 --- /dev/null +++ b/examples/todomvc/.claude/agents/playwright-test-planner.md @@ -0,0 +1,93 @@ +--- +name: playwright-test-planner +description: Use this agent when you need to create comprehensive test plan for a web application or website. Examples: Context: User wants to test a new e-commerce checkout flow. user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout' assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test scenarios.' The user needs test planning for a specific web page, so use the planner agent to explore and create test scenarios. Context: User has deployed a new feature and wants thorough testing coverage. user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?' assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test scenarios.' This requires web exploration and test scenario creation, perfect for the planner agent. +tools: Glob, Grep, Read, Write, mcp__playwright-test__browser_click, mcp__playwright-test__browser_close, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_navigate_back, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_take_screenshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_wait_for, mcp__playwright-test__planner_setup_page +model: sonnet +color: green +--- + +You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test +scenario design. Your expertise includes functional testing, edge case identification, and comprehensive test coverage +planning. + +You will: + +1. **Navigate and Explore** + - Invoke the `planner_setup_page` tool once to set up page before using any other tools + - Explore the browser snapshot + - Do not take screenshots unless absolutely necessary + - Use browser_* tools to navigate and discover interface + - Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality + +2. **Analyze User Flows** + - Map out the primary user journeys and identify critical paths through the application + - Consider different user types and their typical behaviors + +3. **Design Comprehensive Scenarios** + + Create detailed test scenarios that cover: + - Happy path scenarios (normal user behavior) + - Edge cases and boundary conditions + - Error handling and validation + +4. **Structure Test Plans** + + Each scenario must include: + - Clear, descriptive title + - Detailed step-by-step instructions + - Expected outcomes where appropriate + - Assumptions about starting state (always assume blank/fresh state) + - Success criteria and failure conditions + +5. **Create Documentation** + + Save your test plan as requested: + - Executive summary of the tested page/application + - Individual scenarios as separate sections + - Each scenario formatted with numbered steps + - Clear expected results for verification + + +# TodoMVC Application - Comprehensive Test Plan + +## Application Overview + +The TodoMVC application is a React-based todo list manager that provides core task management functionality. The +application features: + +- **Task Management**: Add, edit, complete, and delete individual todos +- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos +- **Filtering**: View todos by All, Active, or Completed status +- **URL Routing**: Support for direct navigation to filtered views via URLs +- **Counter Display**: Real-time count of active (incomplete) todos +- **Persistence**: State maintained during session (browser refresh behavior not tested) + +## Test Scenarios + +### 1. Adding New Todos + +**Seed:** `tests/seed.spec.ts` + +#### 1.1 Add Valid Todo +**Steps:** +1. Click in the "What needs to be done?" input field +2. Type "Buy groceries" +3. Press Enter key + +**Expected Results:** +- Todo appears in the list with unchecked checkbox +- Counter shows "1 item left" +- Input field is cleared and ready for next entry +- Todo list controls become visible (Mark all as complete checkbox) + +#### 1.2 +... + + +**Quality Standards**: +- Write steps that are specific enough for any tester to follow +- Include negative testing scenarios +- Ensure scenarios are independent and can be run in any order + +**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and +professional formatting suitable for sharing with development and QA teams. \ No newline at end of file diff --git "a/examples/todomvc/.github/chatmodes/ \360\237\216\255 planner.chatmode.md" "b/examples/todomvc/.github/chatmodes/ \360\237\216\255 planner.chatmode.md" new file mode 100644 index 0000000000000..5eca16632b9ff --- /dev/null +++ "b/examples/todomvc/.github/chatmodes/ \360\237\216\255 planner.chatmode.md" @@ -0,0 +1,92 @@ +--- +description: Use this agent when you need to create comprehensive test plan for a web application or website. +tools: ['edit/createFile', 'edit/createDirectory', 'search/fileSearch', 'search/textSearch', 'search/listDirectory', 'search/readFile', 'playwright-test/browser_click', 'playwright-test/browser_close', 'playwright-test/browser_console_messages', 'playwright-test/browser_drag', 'playwright-test/browser_evaluate', 'playwright-test/browser_file_upload', 'playwright-test/browser_handle_dialog', 'playwright-test/browser_hover', 'playwright-test/browser_navigate', 'playwright-test/browser_navigate_back', 'playwright-test/browser_network_requests', 'playwright-test/browser_press_key', 'playwright-test/browser_select_option', 'playwright-test/browser_snapshot', 'playwright-test/browser_take_screenshot', 'playwright-test/browser_type', 'playwright-test/browser_wait_for', 'playwright-test/planner_setup_page'] +--- + +You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test +scenario design. Your expertise includes functional testing, edge case identification, and comprehensive test coverage +planning. + +You will: + +1. **Navigate and Explore** + - Invoke the `planner_setup_page` tool once to set up page before using any other tools + - Explore the browser snapshot + - Do not take screenshots unless absolutely necessary + - Use browser_* tools to navigate and discover interface + - Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality + +2. **Analyze User Flows** + - Map out the primary user journeys and identify critical paths through the application + - Consider different user types and their typical behaviors + +3. **Design Comprehensive Scenarios** + + Create detailed test scenarios that cover: + - Happy path scenarios (normal user behavior) + - Edge cases and boundary conditions + - Error handling and validation + +4. **Structure Test Plans** + + Each scenario must include: + - Clear, descriptive title + - Detailed step-by-step instructions + - Expected outcomes where appropriate + - Assumptions about starting state (always assume blank/fresh state) + - Success criteria and failure conditions + +5. **Create Documentation** + + Save your test plan as requested: + - Executive summary of the tested page/application + - Individual scenarios as separate sections + - Each scenario formatted with numbered steps + - Clear expected results for verification + + +# TodoMVC Application - Comprehensive Test Plan + +## Application Overview + +The TodoMVC application is a React-based todo list manager that provides core task management functionality. The +application features: + +- **Task Management**: Add, edit, complete, and delete individual todos +- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos +- **Filtering**: View todos by All, Active, or Completed status +- **URL Routing**: Support for direct navigation to filtered views via URLs +- **Counter Display**: Real-time count of active (incomplete) todos +- **Persistence**: State maintained during session (browser refresh behavior not tested) + +## Test Scenarios + +### 1. Adding New Todos + +**Seed:** `tests/seed.spec.ts` + +#### 1.1 Add Valid Todo +**Steps:** +1. Click in the "What needs to be done?" input field +2. Type "Buy groceries" +3. Press Enter key + +**Expected Results:** +- Todo appears in the list with unchecked checkbox +- Counter shows "1 item left" +- Input field is cleared and ready for next entry +- Todo list controls become visible (Mark all as complete checkbox) + +#### 1.2 +... + + +**Quality Standards**: +- Write steps that are specific enough for any tester to follow +- Include negative testing scenarios +- Ensure scenarios are independent and can be run in any order + +**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and +professional formatting suitable for sharing with development and QA teams. +Context: User wants to test a new e-commerce checkout flow. user: 'I need test scenarios for our new checkout process at https://mystore.com/checkout' assistant: 'I'll use the planner agent to navigate to your checkout page and create comprehensive test scenarios.' The user needs test planning for a specific web page, so use the planner agent to explore and create test scenarios. +Context: User has deployed a new feature and wants thorough testing coverage. user: 'Can you help me test our new user dashboard at https://app.example.com/dashboard?' assistant: 'I'll launch the planner agent to explore your dashboard and develop detailed test scenarios.' This requires web exploration and test scenario creation, perfect for the planner agent. \ No newline at end of file diff --git "a/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" "b/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" new file mode 100644 index 0000000000000..6ada1226f41ea --- /dev/null +++ "b/examples/todomvc/.github/chatmodes/\360\237\216\255 generator.chatmode.md" @@ -0,0 +1,58 @@ +--- +description: Use this agent when you need to create automated browser tests using Playwright. +tools: ['search/fileSearch', 'search/textSearch', 'search/listDirectory', 'search/readFile', 'playwright-test/browser_click', 'playwright-test/browser_drag', 'playwright-test/browser_evaluate', 'playwright-test/browser_file_upload', 'playwright-test/browser_handle_dialog', 'playwright-test/browser_hover', 'playwright-test/browser_navigate', 'playwright-test/browser_press_key', 'playwright-test/browser_select_option', 'playwright-test/browser_snapshot', 'playwright-test/browser_type', 'playwright-test/browser_verify_element_visible', 'playwright-test/browser_verify_list_visible', 'playwright-test/browser_verify_text_visible', 'playwright-test/browser_verify_value', 'playwright-test/browser_wait_for', 'playwright-test/generator_read_log', 'playwright-test/generator_setup_page', 'playwright-test/generator_write_test'] +--- + +You are a Playwright Test Generator, an expert in browser automation and end-to-end testing. +Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate +application behavior. + +# For each test you generate +- Obtain the test plan with all the steps and verification specification +- Run the `generator_setup_page` tool to set up page for the scenario +- For each step and verification in the scenario, do the following: + - Use Playwright tool to manually execute it in real-time. + - Use the step description as the intent for each Playwright tool call. +- Retrieve generator log via `generator_read_log` +- Immediately after reading the test log, invoke `generator_write_test` with the generated source code + - File should contain single test + - File name must be fs-friendly scenario name + - Test must be placed in a describe matching the top-level test plan item + - Test title must match the scenario name + - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires + multiple actions. + - Always use best practices from the log when generating tests. + + + For following plan: + + ```markdown file=specs/plan.md + ### 1. Adding New Todos + **Seed:** `tests/seed.spec.ts` + + #### 1.1 Add Valid Todo + **Steps:** + 1. Click in the "What needs to be done?" input field + + #### 1.2 Add Multiple Todos + ... + ``` + + Following file is generated: + + ```ts file=add-valid-todo.spec.ts + // spec: specs/plan.md + // seed: tests/seed.spec.ts + + test.describe('Adding New Todos', () => { + test('Add Valid Todo', async { page } => { + // 1. Click in the "What needs to be done?" input field + await page.click(...); + + ... + }); + }); + ``` + +Context: User wants to test a login flow on their web application. user: 'I need a test that logs into my app at localhost:3000 with username admin@test.com and password 123456, then verifies the dashboard page loads' assistant: 'I'll use the generator agent to create and validate this login test for you' The user needs a specific browser automation test created, which is exactly what the generator agent is designed for. +Context: User has built a new checkout flow and wants to ensure it works correctly. user: 'Can you create a test that adds items to cart, proceeds to checkout, fills in payment details, and confirms the order?' assistant: 'I'll use the generator agent to build a comprehensive checkout flow test' This is a complex user journey that needs to be automated and tested, perfect for the generator agent. \ No newline at end of file diff --git "a/examples/todomvc/.github/chatmodes/\360\237\216\255 healer.chatmode.md" "b/examples/todomvc/.github/chatmodes/\360\237\216\255 healer.chatmode.md" new file mode 100644 index 0000000000000..6604d740305a0 --- /dev/null +++ "b/examples/todomvc/.github/chatmodes/\360\237\216\255 healer.chatmode.md" @@ -0,0 +1,44 @@ +--- +description: Use this agent when you need to debug and fix failing Playwright tests. +tools: ['edit/createFile', 'edit/createDirectory', 'edit/editFiles', 'search/fileSearch', 'search/textSearch', 'search/listDirectory', 'search/readFile', 'playwright-test/browser_console_messages', 'playwright-test/browser_evaluate', 'playwright-test/browser_generate_locator', 'playwright-test/browser_network_requests', 'playwright-test/browser_snapshot', 'playwright-test/test_debug', 'playwright-test/test_list', 'playwright-test/test_run'] +--- + +You are the Playwright Test Healer, an expert test automation engineer specializing in debugging and +resolving Playwright test failures. Your mission is to systematically identify, diagnose, and fix +broken Playwright tests using a methodical approach. + +Your workflow: +1. **Initial Execution**: Run all tests using playwright_test_run_test tool to identify failing tests +2. **Debug failed tests**: For each failing test run playwright_test_debug_test. +3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to: + - Examine the error details + - Capture page snapshot to understand the context + - Analyze selectors, timing issues, or assertion failures +4. **Root Cause Analysis**: Determine the underlying cause of the failure by examining: + - Element selectors that may have changed + - Timing and synchronization issues + - Data dependencies or test environment problems + - Application changes that broke test assumptions +5. **Code Remediation**: Edit the test code to address identified issues, focusing on: + - Updating selectors to match current application state + - Fixing assertions and expected values + - Improving test reliability and maintainability + - For inherently dynamic data, utilize regular expressions to produce resilient locators +6. **Verification**: Restart the test after each fix to validate the changes +7. **Iteration**: Repeat the investigation and fixing process until the test passes cleanly + +Key principles: +- Be systematic and thorough in your debugging approach +- Document your findings and reasoning for each fix +- Prefer robust, maintainable solutions over quick hacks +- Use Playwright best practices for reliable test automation +- If multiple errors exist, fix them one at a time and retest +- Provide clear explanations of what was broken and how you fixed it +- You will continue this process until the test runs successfully without any failures or errors. +- If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme() + so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead + of the expected behavior. +- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test. +- Never wait for networkidle or use other discouraged or deprecated apis +Context: A developer has a failing Playwright test that needs to be debugged and fixed. user: 'The login test is failing, can you fix it?' assistant: 'I'll use the healer agent to debug and fix the failing login test.' The user has identified a specific failing test that needs debugging and fixing, which is exactly what the healer agent is designed for. +Context: After running a test suite, several tests are reported as failing. user: 'Test user-registration.spec.ts is broken after the recent changes' assistant: 'Let me use the healer agent to investigate and fix the user-registration test.' A specific test file is failing and needs debugging, which requires the systematic approach of the playwright-test-healer agent. \ No newline at end of file diff --git a/examples/todomvc/.mcp.json b/examples/todomvc/.mcp.json new file mode 100644 index 0000000000000..d9dfc1253b94a --- /dev/null +++ b/examples/todomvc/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "playwright-test": { + "command": "npx", + "args": [ + "playwright", + "run-test-mcp-server" + ] + } + } +} \ No newline at end of file diff --git a/examples/todomvc/.vscode/mcp.json b/examples/todomvc/.vscode/mcp.json new file mode 100644 index 0000000000000..df015d2ffa3a6 --- /dev/null +++ b/examples/todomvc/.vscode/mcp.json @@ -0,0 +1,14 @@ +{ + "servers": { + "playwright-test": { + "type": "stdio", + "command": "npx", + "args": [ + "playwright", + "run-test-mcp-server" + ], + "cwd": "${workspaceFolder}" + } + }, + "inputs": [] +} \ No newline at end of file diff --git a/examples/todomvc/playwright.config.ts b/examples/todomvc/playwright.config.ts index 831aa9035595b..e2fbf59bb8d82 100644 --- a/examples/todomvc/playwright.config.ts +++ b/examples/todomvc/playwright.config.ts @@ -12,8 +12,6 @@ export default defineConfig({ /* Maximum time one test can run for. */ timeout: 15_000, - captureGitInfo: { commit: true, diff: true }, - expect: { /** @@ -59,19 +57,19 @@ export default defineConfig({ }, }, - { - name: 'firefox', - use: { - ...devices['Desktop Firefox'], - }, - }, + // { + // name: 'firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, - { - name: 'webkit', - use: { - ...devices['Desktop Safari'], - }, - }, + // { + // name: 'webkit', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, /* Test against mobile viewports. */ // { diff --git a/examples/todomvc/prompt.claudecode.md b/examples/todomvc/prompt.claudecode.md new file mode 100644 index 0000000000000..21ee6db859ab1 --- /dev/null +++ b/examples/todomvc/prompt.claudecode.md @@ -0,0 +1,14 @@ +## Objective +Test basic functionality of todo app. + +## Test setup: tests/template.spec.ts:11 + +## Steps +- Use `playwright-test-planner` subagent to create a test plan for "Basic operations". + Use seed test from `tests/seed.spec.ts` to init page. Save the test plan as `specs/basic-operations.md`. + +- For each scenario in `specs/basic-operations.md`, use `playwright-test-generator` + subagent to perform the scenario and generate the test source code into `tests/` folder. + Process all scenarios sequentially, do not run in parallel. + +- Use `playwright-test-healer` subagent to fix the failing tests. diff --git a/examples/todomvc/specs/basic-operations.md b/examples/todomvc/specs/basic-operations.md new file mode 100644 index 0000000000000..0adc624bdd6fe --- /dev/null +++ b/examples/todomvc/specs/basic-operations.md @@ -0,0 +1,468 @@ +# TodoMVC Application - Basic Operations Test Plan + +## Application Overview + +The TodoMVC application is a React-based todo list manager that demonstrates standard todo application functionality. The application provides comprehensive task management capabilities with a clean, intuitive interface. Key features include: + +- **Task Management**: Add, edit, complete, and delete individual todos +- **Bulk Operations**: Mark all todos as complete/incomplete and clear all completed todos +- **Filtering System**: View todos by All, Active, or Completed status with URL routing support +- **Real-time Counter**: Display of active (incomplete) todo count +- **Interactive UI**: Hover states, edit-in-place functionality, and responsive design +- **State Persistence**: Maintains state during session navigation + +## Test Scenarios + +### 1. Adding New Todos + +**Seed:** `tests/seed.spec.ts` + +#### 1.1 Add Valid Todo + +**Steps:** +1. Click in the "What needs to be done?" input field +2. Type "Buy groceries" +3. Press Enter key + +**Expected Results:** +- Todo appears in the list with unchecked checkbox +- Counter shows "1 item left" +- Input field is cleared and ready for next entry +- Todo list controls become visible (Mark all as complete checkbox) + +#### 1.2 Add Multiple Todos + +**Steps:** +1. Add first todo: "Buy groceries" and press Enter +2. Add second todo: "Walk the dog" and press Enter +3. Add third todo: "Call dentist" and press Enter + +**Expected Results:** +- All three todos appear in the list in the order added +- Counter shows "3 items left" +- Each todo has its own unchecked checkbox +- Input field remains active and cleared after each addition + +#### 1.3 Add Todo with Special Characters + +**Steps:** +1. Type "Buy coffee & donuts (2-3 pieces) @$5.99!" in input field +2. Press Enter + +**Expected Results:** +- Todo appears exactly as typed with all special characters preserved +- Counter shows "1 item left" +- No encoding or display issues with special characters + +#### 1.4 Add Empty Todo (Negative Test) + +**Steps:** +1. Click in input field but don't type anything +2. Press Enter + +**Expected Results:** +- No todo is added to the list +- List remains empty +- No counter appears +- Input field remains focused and empty + +#### 1.5 Add Todo with Only Whitespace (Negative Test) + +**Steps:** +1. Type only spaces " " in input field +2. Press Enter + +**Expected Results:** +- No todo is added to the list +- List remains empty +- Input field is cleared +- No counter appears + +### 2. Marking Todos Complete/Incomplete + +#### 2.1 Mark Single Todo Complete + +**Steps:** +1. Add todo "Buy groceries" +2. Click the checkbox next to "Buy groceries" + +**Expected Results:** +- Checkbox becomes checked +- Todo text may show strikethrough or completed styling +- Counter shows "0 items left" +- "Clear completed" button appears +- Delete button (×) becomes visible on hover + +#### 2.2 Mark Multiple Todos Complete + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Click checkbox for "Buy groceries" +3. Click checkbox for "Call dentist" + +**Expected Results:** +- Two todos show as completed +- Counter shows "1 item left" (for "Walk the dog") +- "Clear completed" button appears +- Only "Walk the dog" remains unchecked + +#### 2.3 Toggle Todo Back to Incomplete + +**Steps:** +1. Add todo "Buy groceries" +2. Click checkbox to mark complete +3. Click checkbox again to mark incomplete + +**Expected Results:** +- Checkbox becomes unchecked +- Completed styling is removed +- Counter shows "1 item left" +- "Clear completed" button disappears if no other completed todos exist + +#### 2.4 Mark All Todos Complete + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Click the "Mark all as complete" checkbox + +**Expected Results:** +- All todo checkboxes become checked +- Counter shows "0 items left" +- "Clear completed" button appears +- "Mark all as complete" checkbox shows as checked + +#### 2.5 Toggle All Todos Back to Incomplete + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Click "Mark all as complete" checkbox +3. Click "Mark all as complete" checkbox again + +**Expected Results:** +- All todo checkboxes become unchecked +- Counter shows "2 items left" +- "Clear completed" button disappears +- "Mark all as complete" checkbox shows as unchecked + +### 3. Editing Todos + +#### 3.1 Edit Todo Text + +**Steps:** +1. Add todo "Buy groceries" +2. Double-click on the todo text "Buy groceries" +3. Clear text and type "Buy organic groceries" +4. Press Enter + +**Expected Results:** +- Todo enters edit mode with text selected +- Text changes to "Buy organic groceries" +- Todo exits edit mode +- Counter remains "1 item left" + +#### 3.2 Cancel Edit with Escape + +**Steps:** +1. Add todo "Buy groceries" +2. Double-click on the todo text +3. Change text to "Buy organic groceries" +4. Press Escape key + +**Expected Results:** +- Todo exits edit mode +- Text reverts to original "Buy groceries" +- No changes are saved +- Todo remains in its original state + +#### 3.3 Edit Todo to Empty Text (Negative Test) + +**Steps:** +1. Add todo "Buy groceries" +2. Double-click on the todo text +3. Clear all text +4. Press Enter + +**Expected Results:** +- Todo should be deleted/removed from list +- Counter decrements appropriately +- List becomes empty if this was the only todo + +#### 3.4 Edit Multiple Todos + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Double-click "Buy groceries", change to "Buy organic groceries", press Enter +3. Double-click "Walk the dog", change to "Walk the cat", press Enter + +**Expected Results:** +- Both todos are updated with new text +- Counter remains "2 items left" +- Both todos maintain their completion state + +### 4. Deleting Todos + +#### 4.1 Delete Single Todo + +**Steps:** +1. Add todo "Buy groceries" +2. Hover over the todo item +3. Click the delete button (×) + +**Expected Results:** +- Todo is removed from the list +- List becomes empty +- Counter disappears +- Todo controls (filters, mark all) disappear + +#### 4.2 Delete Multiple Todos + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Hover over "Walk the dog" and click delete (×) +3. Hover over "Call dentist" and click delete (×) + +**Expected Results:** +- Only "Buy groceries" remains in the list +- Counter shows "1 item left" +- List controls remain visible + +#### 4.3 Delete Completed Todo + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Mark "Buy groceries" as complete +3. Hover over "Buy groceries" and click delete (×) + +**Expected Results:** +- "Buy groceries" is removed from list +- Only "Walk the dog" remains +- Counter shows "1 item left" +- "Clear completed" button disappears + +#### 4.4 Clear All Completed Todos + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" and "Call dentist" as complete +3. Click "Clear completed" button + +**Expected Results:** +- Both completed todos are removed +- Only "Walk the dog" remains +- Counter shows "1 item left" +- "Clear completed" button disappears + +### 5. Filtering Todos + +#### 5.1 Filter by All + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" as complete +3. Click "All" filter link + +**Expected Results:** +- All todos are visible (both completed and active) +- URL shows "#/" +- "All" filter appears active/highlighted +- Counter shows "2 items left" + +#### 5.2 Filter by Active + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" as complete +3. Click "Active" filter link + +**Expected Results:** +- Only incomplete todos are visible ("Walk the dog", "Call dentist") +- Completed todo "Buy groceries" is hidden +- URL shows "#/active" +- "Active" filter appears active/highlighted +- Counter shows "2 items left" + +#### 5.3 Filter by Completed + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" and "Walk the dog" as complete +3. Click "Completed" filter link + +**Expected Results:** +- Only completed todos are visible ("Buy groceries", "Walk the dog") +- Active todo "Call dentist" is hidden +- URL shows "#/completed" +- "Completed" filter appears active/highlighted +- Counter still shows "1 item left" (maintains global count) + +#### 5.4 Navigate Between Filters + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Mark "Buy groceries" as complete +3. Click "Active" filter +4. Click "Completed" filter +5. Click "All" filter + +**Expected Results:** +- Each filter shows appropriate todos +- URL updates correctly for each filter +- Active filter is highlighted appropriately +- Counter remains consistent across filters +- Todos maintain their state when switching views + +### 6. Counter and Status Display + +#### 6.1 Counter with Single Item + +**Steps:** +1. Add one todo "Buy groceries" + +**Expected Results:** +- Counter displays "1 item left" (singular form) +- Counter updates immediately when todo is added + +#### 6.2 Counter with Multiple Items + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" + +**Expected Results:** +- Counter displays "2 items left" (plural form) +- Counter shows correct count + +#### 6.3 Counter Updates with Completion + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" as complete +3. Mark "Walk the dog" as complete + +**Expected Results:** +- Counter starts at "3 items left" +- After first completion: "2 items left" +- After second completion: "1 item left" +- Counter updates immediately with each change + +#### 6.4 Counter with All Items Complete + +**Steps:** +1. Add todo "Buy groceries" +2. Mark it as complete + +**Expected Results:** +- Counter shows "0 items left" +- "Clear completed" button is visible +- Filter links remain functional + +### 7. Bulk Operations + +#### 7.1 Mark All Complete When None Completed + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Click "Mark all as complete" checkbox + +**Expected Results:** +- All todos become checked/completed +- Counter shows "0 items left" +- "Clear completed" button appears +- "Mark all as complete" checkbox shows as checked + +#### 7.2 Mark All Incomplete When All Completed + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Mark both todos as complete individually +3. Click "Mark all as complete" checkbox + +**Expected Results:** +- All todos become unchecked/incomplete +- Counter shows "2 items left" +- "Clear completed" button disappears +- "Mark all as complete" checkbox shows as unchecked + +#### 7.3 Mark All with Mixed State + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" +2. Mark "Buy groceries" as complete +3. Click "Mark all as complete" checkbox + +**Expected Results:** +- All todos become completed (including the already completed one) +- Counter shows "0 items left" +- "Mark all as complete" checkbox shows as checked + +### 8. Edge Cases and Error Handling + +#### 8.1 Very Long Todo Text + +**Steps:** +1. Type a very long todo text (200+ characters) +2. Press Enter + +**Expected Results:** +- Todo is added successfully +- Text wraps appropriately in the display +- Interface remains usable +- Edit functionality works with long text + +#### 8.2 Rapid Sequential Actions + +**Steps:** +1. Quickly add multiple todos by typing and pressing Enter rapidly +2. Quickly toggle completion states +3. Rapidly switch between filters + +**Expected Results:** +- All actions are processed correctly +- Counter updates accurately +- No todos are lost or duplicated +- Interface remains responsive + +#### 8.3 Direct URL Navigation +**Steps:** +1. Navigate directly to `{base_url}#/active` +1. Navigate directly to `{base_url}#/completed` +2. Navigate directly to `{base_url}#/` + +**Expected Results:** +- Page loads correctly for each URL +- Appropriate filter is active +- Interface is fully functional +- No JavaScript errors occur + +#### 8.4 Todo Operations Across Filters + +**Steps:** +1. Add todos: "Buy groceries", "Walk the dog" +2. Navigate to "Active" filter +3. Mark "Buy groceries" as complete +4. Navigate to "Completed" filter +5. Delete "Buy groceries" + +**Expected Results:** +- Operations work correctly across filter views +- Todo states are maintained when switching filters +- Counter updates appropriately +- UI remains consistent + +## Test Data Considerations + +- **Todo Text Variations**: Test with short text, long text, special characters, Unicode characters, HTML entities +- **Volume Testing**: Test with 1, 2, 10, 50+ todos to ensure performance +- **State Combinations**: Test all combinations of completed/incomplete todos with different filters +- **Boundary Values**: Test edge cases like exactly 0 items, exactly 1 item, maximum reasonable todo count + +## Success Criteria + +All test scenarios should pass without: +- JavaScript console errors +- Visual layout issues +- Incorrect counter displays +- Lost or corrupted todo data +- Non-functional UI elements +- Accessibility violations + +The application should maintain consistent behavior across all supported browsers and provide a smooth, intuitive user experience for basic todo management operations. diff --git a/examples/todomvc/tests/create/add-empty-todo.spec.ts b/examples/todomvc/tests/create/add-empty-todo.spec.ts new file mode 100644 index 0000000000000..1ed79f985bcca --- /dev/null +++ b/examples/todomvc/tests/create/add-empty-todo.spec.ts @@ -0,0 +1,29 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Empty Todo (Negative Test)', async ({ page }) => { + // 1. Click in input field but don't type anything + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + + // 2. Press Enter + await todoInput.press('Enter'); + + // Expected Results: + // - No todo is added to the list + await expect(page.locator('.todo-list li')).toHaveCount(0); + + // - List remains empty + await expect(page.locator('.todo-list')).not.toBeVisible(); + + // - No counter appears + await expect(page.getByText(/\d+ items? left/)).not.toBeVisible(); + + // - Input field remains focused and empty + await expect(todoInput).toBeFocused(); + await expect(todoInput).toHaveValue(''); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/create/add-multiple-todos.spec.ts b/examples/todomvc/tests/create/add-multiple-todos.spec.ts new file mode 100644 index 0000000000000..a698ca5546761 --- /dev/null +++ b/examples/todomvc/tests/create/add-multiple-todos.spec.ts @@ -0,0 +1,47 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Multiple Todos', async ({ page }) => { + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + + // 1. Add first todo: "Buy groceries" and press Enter + await todoInput.fill('Buy groceries'); + await todoInput.press('Enter'); + + // 2. Add second todo: "Walk the dog" and press Enter + await todoInput.fill('Walk the dog'); + await todoInput.press('Enter'); + + // 3. Add third todo: "Call dentist" and press Enter + await todoInput.fill('Call dentist'); + await todoInput.press('Enter'); + + // Expected Results: + // - All three todos appear in the list in the order added + const todoItems = page.getByTestId('todo-item'); + await expect(todoItems).toHaveCount(3); + + await expect(todoItems.nth(0)).toContainText('Buy groceries'); + await expect(todoItems.nth(1)).toContainText('Walk the dog'); + await expect(todoItems.nth(2)).toContainText('Call dentist'); + + // - Counter shows "3 items left" + await expect(page.getByText('3 items left')).toBeVisible(); + + // - Each todo has its own unchecked checkbox + const todoCheckboxes = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckboxes).toHaveCount(3); + + for (let i = 0; i < 3; i++) { + await expect(todoCheckboxes.nth(i)).toBeVisible(); + await expect(todoCheckboxes.nth(i)).not.toBeChecked(); + } + + // - Input field remains active and cleared after each addition + await expect(todoInput).toHaveValue(''); + await expect(todoInput).toBeFocused(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/create/add-todo-with-only-whitespace.spec.ts b/examples/todomvc/tests/create/add-todo-with-only-whitespace.spec.ts new file mode 100644 index 0000000000000..6a7b70d89d802 --- /dev/null +++ b/examples/todomvc/tests/create/add-todo-with-only-whitespace.spec.ts @@ -0,0 +1,31 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Todo with Only Whitespace (Negative Test)', async ({ page }) => { + // 1. Type only spaces " " in input field + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.fill(' '); + + // 2. Press Enter + await todoInput.press('Enter'); + + // Expected Results: + // - No todo is added to the list + await expect(page.locator('.todo-list li')).toHaveCount(0); + + // - List remains empty + await expect(page.locator('.todo-list')).not.toBeVisible(); + + // - Input field retains the whitespace (actual behavior differs from spec) + await expect(todoInput).toHaveValue(' '); + + // - No counter appears + await expect(page.getByText(/\d+ items? left/)).not.toBeVisible(); + + // - Input field remains focused + await expect(todoInput).toBeFocused(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/create/add-todo-with-special-characters.spec.ts b/examples/todomvc/tests/create/add-todo-with-special-characters.spec.ts new file mode 100644 index 0000000000000..3b6bcaf427552 --- /dev/null +++ b/examples/todomvc/tests/create/add-todo-with-special-characters.spec.ts @@ -0,0 +1,30 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Todo with Special Characters', async ({ page }) => { + // 1. Type "Buy coffee & donuts (2-3 pieces) @$5.99!" in input field + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.fill('Buy coffee & donuts (2-3 pieces) @$5.99!'); + + // 2. Press Enter + await todoInput.press('Enter'); + + // Expected Results: + // - Todo appears exactly as typed with all special characters preserved + await expect(page.getByText('Buy coffee & donuts (2-3 pieces) @$5.99!')).toBeVisible(); + + // - Counter shows "1 item left" + await expect(page.getByText('1 item left')).toBeVisible(); + + // - No encoding or display issues with special characters + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckbox).toBeVisible(); + await expect(todoCheckbox).not.toBeChecked(); + + // Verify input field is cleared + await expect(todoInput).toHaveValue(''); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/create/add-valid-todo.spec.ts b/examples/todomvc/tests/create/add-valid-todo.spec.ts new file mode 100644 index 0000000000000..0353dba82e384 --- /dev/null +++ b/examples/todomvc/tests/create/add-valid-todo.spec.ts @@ -0,0 +1,35 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Adding New Todos', () => { + test('Add Valid Todo', async ({ page }) => { + // 1. Click in the "What needs to be done?" input field + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + + // 2. Type "Buy groceries" + await todoInput.fill('Buy groceries'); + + // 3. Press Enter key + await todoInput.press('Enter'); + + // Expected Results: + // - Todo appears in the list with unchecked checkbox + await expect(page.getByText('Buy groceries')).toBeVisible(); + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckbox).toBeVisible(); + await expect(todoCheckbox).not.toBeChecked(); + + // - Counter shows "1 item left" + await expect(page.getByText('1 item left')).toBeVisible(); + + // - Input field is cleared and ready for next entry + await expect(todoInput).toHaveValue(''); + await expect(todoInput).toBeFocused(); + + // - Todo list controls become visible (Mark all as complete checkbox) + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/edit/cancel-edit-with-escape.spec.ts b/examples/todomvc/tests/edit/cancel-edit-with-escape.spec.ts new file mode 100644 index 0000000000000..9fc5ad164b3f4 --- /dev/null +++ b/examples/todomvc/tests/edit/cancel-edit-with-escape.spec.ts @@ -0,0 +1,46 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Editing Todos', () => { + test('Cancel Edit with Escape', async ({ page }) => { + // 1. Add todo "Buy groceries" + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Buy groceries'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + // Verify todo was added + await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries'); + await expect(page.locator('.todo-count')).toHaveText('1 item left'); + + // 2. Double-click on the todo text + await page.getByTestId('todo-title').dblclick(); + + // Verify todo enters edit mode with text selected + const editInput = page.getByRole('textbox', { name: 'Edit' }); + await expect(editInput).toBeVisible(); + await expect(editInput).toHaveValue('Buy groceries'); + await expect(editInput).toBeFocused(); + + // 3. Change text to "Buy organic groceries" + await editInput.fill('Buy organic groceries'); + + // Verify text was changed in edit input + await expect(editInput).toHaveValue('Buy organic groceries'); + + // 4. Press Escape key + await page.keyboard.press('Escape'); + + // Expected results: + // - Todo exits edit mode + await expect(editInput).not.toBeVisible(); + + // - Text reverts to original "Buy groceries" + await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries'); + + // - No changes are saved (verified by text reversion above) + // - Todo remains in its original state + await expect(page.locator('.todo-count')).toHaveText('1 item left'); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/edit/edit-multiple-todos.spec.ts b/examples/todomvc/tests/edit/edit-multiple-todos.spec.ts new file mode 100644 index 0000000000000..643921ed7d730 --- /dev/null +++ b/examples/todomvc/tests/edit/edit-multiple-todos.spec.ts @@ -0,0 +1,61 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Editing Todos', () => { + test('Edit Multiple Todos', async ({ page }) => { + // 1. Add todos: "Buy groceries", "Walk the dog" + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Buy groceries'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Walk the dog'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + // Verify both todos were added + await expect(page.getByText('Buy groceries')).toBeVisible(); + await expect(page.getByText('Walk the dog')).toBeVisible(); + await expect(page.locator('.todo-count')).toHaveText('2 items left'); + + // 2. Double-click "Buy groceries", change to "Buy organic groceries", press Enter + await page.getByText('Buy groceries').dblclick(); + + const editInput = page.getByRole('textbox', { name: 'Edit' }); + await expect(editInput).toBeVisible(); + await expect(editInput).toHaveValue('Buy groceries'); + + await editInput.fill('Buy organic groceries'); + await page.keyboard.press('Enter'); + + // 3. Double-click "Walk the dog", change to "Walk the cat", press Enter + await page.getByText('Walk the dog').dblclick(); + + const editInput2 = page.getByRole('textbox', { name: 'Edit' }); + await expect(editInput2).toBeVisible(); + await expect(editInput2).toHaveValue('Walk the dog'); + + await editInput2.fill('Walk the cat'); + await page.keyboard.press('Enter'); + + // Expected results: + // - Both todos are updated with new text + await expect(page.getByText('Buy organic groceries')).toBeVisible(); + await expect(page.getByText('Walk the cat')).toBeVisible(); + + // - Counter remains "2 items left" + await expect(page.locator('.todo-count')).toHaveText('2 items left'); + + // - Both todos maintain their completion state (uncompleted) + const todo1Checkbox = page.getByText('Buy organic groceries').locator('..').getByRole('checkbox', { name: 'Toggle Todo' }); + const todo2Checkbox = page.getByText('Walk the cat').locator('..').getByRole('checkbox', { name: 'Toggle Todo' }); + + await expect(todo1Checkbox).not.toBeChecked(); + await expect(todo2Checkbox).not.toBeChecked(); + + // Verify edit inputs are no longer visible + await expect(editInput).not.toBeVisible(); + await expect(editInput2).not.toBeVisible(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/edit/edit-todo-text.spec.ts b/examples/todomvc/tests/edit/edit-todo-text.spec.ts new file mode 100644 index 0000000000000..f0629ba745a2a --- /dev/null +++ b/examples/todomvc/tests/edit/edit-todo-text.spec.ts @@ -0,0 +1,42 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Editing Todos', () => { + test('Edit Todo Text', async ({ page }) => { + // 1. Add todo "Buy groceries" + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Buy groceries'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + // Verify todo was added + await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries'); + await expect(page.locator('.todo-count')).toHaveText('1 item left'); + + // 2. Double-click on the todo text "Buy groceries" + await page.getByTestId('todo-title').dblclick(); + + // Verify todo enters edit mode with text selected + const editInput = page.getByRole('textbox', { name: 'Edit' }); + await expect(editInput).toBeVisible(); + await expect(editInput).toHaveValue('Buy groceries'); + await expect(editInput).toBeFocused(); + + // 3. Clear text and type "Buy organic groceries" + await editInput.fill('Buy organic groceries'); + + // 4. Press Enter + await page.keyboard.press('Enter'); + + // Expected results: + // - Text changes to "Buy organic groceries" + await expect(page.getByTestId('todo-title')).toHaveText('Buy organic groceries'); + + // - Todo exits edit mode + await expect(editInput).not.toBeVisible(); + + // - Counter remains "1 item left" + await expect(page.locator('.todo-count')).toHaveText('1 item left'); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/edit/edit-todo-to-empty-text.spec.ts b/examples/todomvc/tests/edit/edit-todo-to-empty-text.spec.ts new file mode 100644 index 0000000000000..961e3e39216ac --- /dev/null +++ b/examples/todomvc/tests/edit/edit-todo-to-empty-text.spec.ts @@ -0,0 +1,44 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Editing Todos', () => { + test('Edit Todo to Empty Text (Negative Test)', async ({ page }) => { + // 1. Add todo "Buy groceries" + await page.getByRole('textbox', { name: 'What needs to be done?' }).click(); + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Buy groceries'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + // Verify todo was added + await expect(page.getByTestId('todo-title')).toHaveText('Buy groceries'); + await expect(page.locator('.todo-count')).toHaveText('1 item left'); + + // 2. Double-click on the todo text + await page.getByTestId('todo-title').dblclick(); + + // Verify todo enters edit mode + const editInput = page.getByRole('textbox', { name: 'Edit' }); + await expect(editInput).toBeVisible(); + await expect(editInput).toHaveValue('Buy groceries'); + + // 3. Clear all text + await editInput.fill(''); + + // 4. Press Enter + await page.keyboard.press('Enter'); + + // Expected results: + // - Todo should be deleted/removed from list + await expect(page.getByTestId('todo-title')).not.toBeVisible(); + + // - Counter decrements appropriately (disappears when no todos) + await expect(page.locator('.todo-count')).not.toBeVisible(); + + // - List becomes empty if this was the only todo + await expect(page.locator('.todo-list')).not.toBeVisible(); + + // - Only the main input should remain visible + await expect(page.getByRole('textbox', { name: 'What needs to be done?' })).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/fixtures.ts b/examples/todomvc/tests/fixtures.ts new file mode 100644 index 0000000000000..c58c0055c58a0 --- /dev/null +++ b/examples/todomvc/tests/fixtures.ts @@ -0,0 +1,12 @@ +/* eslint-disable notice/notice */ + +import { test as baseTest } from '@playwright/test'; + +export { expect } from '@playwright/test'; + +export const test = baseTest.extend({ + page: async ({ page }, use) => { + await page.goto('https://demo.playwright.dev/todomvc'); + await use(page); + }, +}); diff --git a/examples/todomvc/tests/integration.spec.ts b/examples/todomvc/tests/integration.spec.ts index 007896bb2cd5e..f41b0a2d581b5 100644 --- a/examples/todomvc/tests/integration.spec.ts +++ b/examples/todomvc/tests/integration.spec.ts @@ -1,14 +1,10 @@ /* eslint-disable notice/notice */ -import { test, expect } from '@playwright/test'; +import { test, expect } from './fixtures'; import type { Page } from '@playwright/test'; test.describe.configure({ mode: 'parallel' }); -test.beforeEach(async ({ page }) => { - await page.goto('https://demo.playwright.dev/todomvc'); -}); - const TODO_ITEMS = [ 'buy some cheese', 'feed the cat', diff --git a/examples/todomvc/tests/seed.spec.ts b/examples/todomvc/tests/seed.spec.ts new file mode 100644 index 0000000000000..24cf199121ca9 --- /dev/null +++ b/examples/todomvc/tests/seed.spec.ts @@ -0,0 +1,6 @@ +import { test, expect } from './fixtures'; + +test('seed', async ({ page }) => { + // This test tells agents how to start recording the test + // so that the page was already configured. +}); diff --git a/examples/todomvc/tests/toggle/mark-all-todos-complete.spec.ts b/examples/todomvc/tests/toggle/mark-all-todos-complete.spec.ts new file mode 100644 index 0000000000000..3bd89cb4df8c8 --- /dev/null +++ b/examples/todomvc/tests/toggle/mark-all-todos-complete.spec.ts @@ -0,0 +1,39 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Marking Todos Complete/Incomplete', () => { + test('Mark All Todos Complete', async ({ page }) => { + // 1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" + const newTodoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + + await newTodoInput.fill('Buy groceries'); + await newTodoInput.press('Enter'); + + await newTodoInput.fill('Walk the dog'); + await newTodoInput.press('Enter'); + + await newTodoInput.fill('Call dentist'); + await newTodoInput.press('Enter'); + + // 2. Click the "Mark all as complete" checkbox + await page.getByRole('checkbox', { name: '❯Mark all as complete' }).click(); + + // Expected results to verify: + // - All todo checkboxes become checked + const todoCheckboxes = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await expect(todoCheckboxes.nth(0)).toBeChecked(); + await expect(todoCheckboxes.nth(1)).toBeChecked(); + await expect(todoCheckboxes.nth(2)).toBeChecked(); + + // - Counter shows "0 items left" + await expect(page.getByText('0 items left')).toBeVisible(); + + // - "Clear completed" button appears + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + + // - "Mark all as complete" checkbox shows as checked + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).toBeChecked(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/toggle/mark-multiple-todos-complete.spec.ts b/examples/todomvc/tests/toggle/mark-multiple-todos-complete.spec.ts new file mode 100644 index 0000000000000..4a6ed1a2daf84 --- /dev/null +++ b/examples/todomvc/tests/toggle/mark-multiple-todos-complete.spec.ts @@ -0,0 +1,48 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Marking Todos Complete/Incomplete', () => { + test('Mark Multiple Todos Complete', async ({ page }) => { + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + + // 1. Add todos: "Buy groceries", "Walk the dog", "Call dentist" + await todoInput.click(); + await todoInput.fill('Buy groceries'); + await todoInput.press('Enter'); + + await todoInput.fill('Walk the dog'); + await todoInput.press('Enter'); + + await todoInput.fill('Call dentist'); + await todoInput.press('Enter'); + + // Verify all todos are added + await expect(page.getByText('3 items left')).toBeVisible(); + + // 2. Click checkbox for "Buy groceries" + await page.getByRole('listitem').filter({ hasText: 'Buy groceries' }).getByLabel('Toggle Todo').click(); + + // 3. Click checkbox for "Call dentist" + await page.getByRole('listitem').filter({ hasText: 'Call dentist' }).getByLabel('Toggle Todo').click(); + + // Expected Results: + // - Two todos show as completed + const buyGroceriesCheckbox = page.getByRole('listitem').filter({ hasText: 'Buy groceries' }).getByLabel('Toggle Todo'); + const callDentistCheckbox = page.getByRole('listitem').filter({ hasText: 'Call dentist' }).getByLabel('Toggle Todo'); + const walkDogCheckbox = page.getByRole('listitem').filter({ hasText: 'Walk the dog' }).getByLabel('Toggle Todo'); + + await expect(buyGroceriesCheckbox).toBeChecked(); + await expect(callDentistCheckbox).toBeChecked(); + + // - Counter shows "1 item left" (for "Walk the dog") + await expect(page.getByText('1 item left')).toBeVisible(); + + // - "Clear completed" button appears + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + + // - Only "Walk the dog" remains unchecked + await expect(walkDogCheckbox).not.toBeChecked(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/toggle/mark-single-todo-complete.spec.ts b/examples/todomvc/tests/toggle/mark-single-todo-complete.spec.ts new file mode 100644 index 0000000000000..b76a25f412207 --- /dev/null +++ b/examples/todomvc/tests/toggle/mark-single-todo-complete.spec.ts @@ -0,0 +1,39 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Marking Todos Complete/Incomplete', () => { + test('Mark Single Todo Complete', async ({ page }) => { + // 1. Add todo "Buy groceries" + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + await todoInput.fill('Buy groceries'); + await todoInput.press('Enter'); + + // Verify todo was added successfully + await expect(page.getByText('Buy groceries')).toBeVisible(); + await expect(page.getByText('1 item left')).toBeVisible(); + + // 2. Click the checkbox next to "Buy groceries" + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await todoCheckbox.click(); + + // Expected Results: + // - Checkbox becomes checked + await expect(todoCheckbox).toBeChecked(); + + // - Todo text may show strikethrough or completed styling (verified by checking the todo is still visible) + await expect(page.getByText('Buy groceries')).toBeVisible(); + + // - Counter shows "0 items left" + await expect(page.getByText('0 items left')).toBeVisible(); + + // - "Clear completed" button appears + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + + // - Delete button (×) becomes visible on hover + await page.getByText('Buy groceries').hover(); + await expect(page.getByRole('button', { name: 'Delete' })).toBeVisible(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/toggle/toggle-all-todos-incomplete.spec.ts b/examples/todomvc/tests/toggle/toggle-all-todos-incomplete.spec.ts new file mode 100644 index 0000000000000..11ec10d2276a7 --- /dev/null +++ b/examples/todomvc/tests/toggle/toggle-all-todos-incomplete.spec.ts @@ -0,0 +1,37 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Marking Todos Complete/Incomplete', () => { + test('Toggle All Todos Back to Incomplete', async ({ page }) => { + // 1. Add todos: "Buy groceries", "Walk the dog" + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Buy groceries'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + await page.getByRole('textbox', { name: 'What needs to be done?' }).fill('Walk the dog'); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + + // 2. Click "Mark all as complete" checkbox + await page.getByRole('checkbox', { name: '❯Mark all as complete' }).click(); + + // 3. Click "Mark all as complete" checkbox again + await page.getByRole('checkbox', { name: '❯Mark all as complete' }).click(); + + // Verify: All todo checkboxes become unchecked + const buyGroceriesCheckbox = page.getByRole('listitem').filter({ hasText: 'Buy groceries' }).getByRole('checkbox', { name: 'Toggle Todo' }); + const walkDogCheckbox = page.getByRole('listitem').filter({ hasText: 'Walk the dog' }).getByRole('checkbox', { name: 'Toggle Todo' }); + + await expect(buyGroceriesCheckbox).not.toBeChecked(); + await expect(walkDogCheckbox).not.toBeChecked(); + + // Verify: Counter shows "2 items left" + await expect(page.locator('text=2 items left')).toBeVisible(); + + // Verify: "Clear completed" button disappears + await expect(page.getByRole('button', { name: 'Clear completed' })).not.toBeVisible(); + + // Verify: "Mark all as complete" checkbox shows as unchecked + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).not.toBeChecked(); + }); +}); \ No newline at end of file diff --git a/examples/todomvc/tests/toggle/toggle-todo-incomplete.spec.ts b/examples/todomvc/tests/toggle/toggle-todo-incomplete.spec.ts new file mode 100644 index 0000000000000..4f775fed381cf --- /dev/null +++ b/examples/todomvc/tests/toggle/toggle-todo-incomplete.spec.ts @@ -0,0 +1,46 @@ +// spec: specs/basic-operations.md +// seed: tests/seed.spec.ts + +import { test, expect } from '../fixtures'; + +test.describe('Marking Todos Complete/Incomplete', () => { + test('Toggle Todo Back to Incomplete', async ({ page }) => { + // 1. Add todo "Buy groceries" + const todoInput = page.getByRole('textbox', { name: 'What needs to be done?' }); + await todoInput.click(); + await todoInput.fill('Buy groceries'); + await todoInput.press('Enter'); + + // Verify todo was added + const todoItem = page.getByText('Buy groceries'); + await expect(todoItem).toBeVisible(); + await expect(page.getByText('1 item left')).toBeVisible(); + + // 2. Click checkbox to mark complete + const todoCheckbox = page.getByRole('checkbox', { name: 'Toggle Todo' }); + await todoCheckbox.click(); + + // Verify todo is marked complete + await expect(todoCheckbox).toBeChecked(); + await expect(page.getByText('0 items left')).toBeVisible(); + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + + // 3. Click checkbox again to mark incomplete + await todoCheckbox.click(); + + // Expected results to verify: + // - Checkbox becomes unchecked + await expect(todoCheckbox).not.toBeChecked(); + + // - Completed styling is removed (checkbox is unchecked indicates this) + // - Counter shows "1 item left" + await expect(page.getByText('1 item left')).toBeVisible(); + + // - "Clear completed" button disappears if no other completed todos exist + await expect(page.getByRole('button', { name: 'Clear completed' })).not.toBeVisible(); + + // Additional verification that the todo is still present and functional + await expect(todoItem).toBeVisible(); + await expect(page.getByRole('checkbox', { name: '❯Mark all as complete' })).not.toBeChecked(); + }); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 00229fb661a86..5a52322f9dad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playwright-internal", - "version": "1.55.0-next", + "version": "1.56.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playwright-internal", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "workspaces": [ "packages/*" @@ -15,53 +15,54 @@ "@actions/core": "^1.10.0", "@actions/github": "^6.0.0", "@babel/code-frame": "^7.26.2", - "@eslint/compat": "^1.2.6", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.19.0", - "@modelcontextprotocol/sdk": "^1.17.3", + "@eslint/compat": "^1.3.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.34.0", + "@modelcontextprotocol/sdk": "^1.17.5", "@octokit/graphql-schema": "^15.26.0", - "@stylistic/eslint-plugin": "^3.0.1", + "@stylistic/eslint-plugin": "^5.2.3", "@types/codemirror": "^5.60.7", "@types/formidable": "^2.0.4", - "@types/immutable": "^3.8.7", - "@types/node": "^18.19.68", - "@types/react": "^18.0.12", - "@types/react-dom": "^18.0.5", + "@types/node": "18.19.76", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", "@types/ws": "^8.5.3", "@types/xml2js": "^0.4.9", - "@typescript-eslint/eslint-plugin": "^8.26.1", - "@typescript-eslint/parser": "^8.26.1", - "@typescript-eslint/utils": "^8.26.1", + "@typescript-eslint/eslint-plugin": "^8.41.0", + "@typescript-eslint/parser": "^8.41.0", + "@typescript-eslint/utils": "^8.41.0", "@vitejs/plugin-basic-ssl": "^1.1.0", "@vitejs/plugin-react": "^4.2.1", "@zip.js/zip.js": "^2.7.29", "ansi-styles": "^4.3.0", "chokidar": "^3.5.3", - "chromium-bidi": "^7.2.0", + "chromium-bidi": "^9.0.0", "colors": "^1.4.0", "concurrently": "^6.2.1", "cross-env": "^7.0.3", "dotenv": "^16.4.5", - "electron": "^30.1.2", + "electron": "^38.1.0", "esbuild": "^0.25.0", - "eslint": "^9.19.0", - "eslint-plugin-import": "^2.31.0", + "eslint": "^9.34.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-notice": "^1.0.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", "formidable": "^2.1.1", "immutable": "^4.3.7", "license-checker": "^25.0.1", "mime": "^3.0.0", "node-stream-zip": "^1.15.0", - "react": "^18.1.0", - "react-dom": "^18.1.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "ssim.js": "^3.5.0", - "typescript": "^5.8.2", - "vite": "^6.3.4", + "typescript": "^5.9.2", + "vite": "^6.3.6", "ws": "^8.17.1", "xml2js": "^0.5.0", - "yaml": "2.6.0" + "yaml": "2.6.0", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.24.6" }, "engines": { "node": ">=18" @@ -72,7 +73,6 @@ "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", "dev": true, - "license": "MIT", "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" @@ -83,22 +83,23 @@ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", "dev": true, - "license": "MIT", "dependencies": { "@actions/io": "^1.0.1" } }, "node_modules/@actions/github": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz", - "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz", + "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", "dev": true, - "license": "MIT", "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + "@octokit/plugin-paginate-rest": "^9.2.2", + "@octokit/plugin-rest-endpoint-methods": "^10.4.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "undici": "^5.28.5" } }, "node_modules/@actions/http-client": { @@ -106,7 +107,6 @@ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", "dev": true, - "license": "MIT", "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" @@ -116,14 +116,12 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -133,44 +131,41 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", - "license": "MIT", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -185,16 +180,23 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -202,13 +204,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", - "license": "MIT", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -217,28 +218,42 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -248,61 +263,55 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -312,12 +321,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -327,12 +335,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -342,58 +349,51 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "dev": true, - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", - "license": "MIT", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", - "license": "MIT", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -404,7 +404,6 @@ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -421,14 +420,22 @@ "global-agent": "^3.0.0" } }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], - "license": "MIT", "optional": true, "os": [ "aix" @@ -438,13 +445,12 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "android" @@ -454,13 +460,12 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "android" @@ -470,13 +475,12 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "android" @@ -486,13 +490,12 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -502,13 +505,12 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -518,13 +520,12 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -534,13 +535,12 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -550,13 +550,12 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -566,13 +565,12 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -582,13 +580,12 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -598,13 +595,12 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -614,13 +610,12 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -630,13 +625,12 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -646,13 +640,12 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -662,13 +655,12 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -678,13 +670,12 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -694,13 +685,12 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -710,13 +700,12 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -726,13 +715,12 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -742,13 +730,12 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -757,14 +744,28 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "sunos" @@ -774,13 +775,12 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -790,13 +790,12 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -806,13 +805,12 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -822,11 +820,10 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.8.0.tgz", + "integrity": "sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==", "dev": true, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -845,7 +842,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -858,22 +854,20 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/compat": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.6.tgz", - "integrity": "sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.3.2.tgz", + "integrity": "sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": "^9.10.0" + "eslint": "^8.40 || 9" }, "peerDependenciesMeta": { "eslint": { @@ -882,11 +876,10 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", @@ -896,12 +889,20 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", - "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -910,11 +911,10 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -933,27 +933,16 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/js": { - "version": "9.20.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", - "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -961,44 +950,28 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true, - "license": "MIT", "engines": { "node": ">=14" } @@ -1008,45 +981,28 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1056,11 +1012,10 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1070,59 +1025,50 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", - "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.5.tgz", + "integrity": "sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", @@ -1141,12 +1087,23 @@ "node": ">=18" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1160,7 +1117,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -1170,7 +1126,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1184,17 +1139,15 @@ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", - "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -1213,7 +1166,6 @@ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" @@ -1227,7 +1179,6 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", @@ -1242,7 +1193,6 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-15.26.0.tgz", "integrity": "sha512-SoVbh+sXe9nsoweFbLT3tAk3XWYbYLs5ku05wij1zhyQ2U3lewdrhjo5Tb7lfaOGWNHSkPZT4uuPZp8neF7P7A==", "dev": true, - "license": "MIT", "dependencies": { "graphql": "^16.0.0", "graphql-tag": "^2.10.3" @@ -1252,15 +1202,13 @@ "version": "24.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" }, @@ -1275,15 +1223,13 @@ "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" } @@ -1293,7 +1239,6 @@ "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" }, @@ -1308,15 +1253,13 @@ "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" } @@ -1326,7 +1269,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", @@ -1342,7 +1284,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", @@ -1357,11 +1298,19 @@ "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "dev": true, - "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@playwright/browser-chromium": { "resolved": "packages/playwright-browser-chromium", "link": true @@ -1402,265 +1351,258 @@ "resolved": "packages/playwright-test", "link": true }, - "node_modules/@playwright/test-runner-mcp": { - "resolved": "packages/playwright-test-mcp", - "link": true + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", - "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz", + "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==", "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", - "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz", + "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", - "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz", + "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", - "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz", + "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", - "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz", + "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", - "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz", + "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", - "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz", + "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==", "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", - "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz", + "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==", "cpu": [ "arm" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", - "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz", + "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", - "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz", + "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", - "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz", + "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==", "cpu": [ "loong64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", - "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz", + "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==", "cpu": [ "ppc64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", - "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz", + "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==", "cpu": [ "riscv64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", - "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz", + "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==", "cpu": [ "riscv64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", - "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz", + "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==", "cpu": [ "s390x" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", - "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz", + "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", - "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz", + "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz", + "integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", - "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz", + "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", - "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz", + "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==", "cpu": [ "ia32" ], - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", - "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz", + "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -1670,15 +1612,13 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -1687,40 +1627,74 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-3.1.0.tgz", - "integrity": "sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.3.1.tgz", + "integrity": "sha512-Ykums1VYonM0TgkD0VteVq9mrlO2FhF48MDJnPyv3MktIB2ydtuhlO0AfWm7xnW1kyf5bjOqA6xc7JjviuVTxg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.13.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.41.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "picomatch": "^4.0.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": ">=8.40.0" + "eslint": ">=9.0.0" } }, "node_modules/@sveltejs/acorn-typescript": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", - "license": "MIT", "peerDependencies": { "acorn": "^8.9.0" } }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", + "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "vitefu": "^1.0.6" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, - "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -1732,7 +1706,6 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -1742,10 +1715,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "license": "MIT", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dependencies": { "@babel/types": "^7.0.0" } @@ -1754,19 +1726,17 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "license": "MIT", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/cacheable-request": { @@ -1774,7 +1744,6 @@ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, - "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", @@ -1783,27 +1752,24 @@ } }, "node_modules/@types/codemirror": { - "version": "5.60.15", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", - "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", + "version": "5.60.16", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.16.tgz", + "integrity": "sha512-V/yHdamffSS075jit+fDxaOAmdP2liok8NSNJnAZfDJErzOheuygHZEhAJrfmk5TEyM32MhkZjwo/idX791yxw==", "dev": true, - "license": "MIT", "dependencies": { "@types/tern": "*" } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "license": "MIT" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" }, "node_modules/@types/formidable": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz", "integrity": "sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1812,40 +1778,25 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/immutable": { - "version": "3.8.7", - "resolved": "https://registry.npmjs.org/@types/immutable/-/immutable-3.8.7.tgz", - "integrity": "sha512-nsHFDX48Tl3RaP4BF47HHe5njx40Pcp+0a8CIqzJata80Fp7JzkcuGB7UhZBGjH9aA1fMEahIqvPQQNmro5YLg==", - "deprecated": "This is a stub types definition for Facebook's Immutable (https://github.com/facebook/immutable-js). Facebook's Immutable provides its own type definitions, so you don't need @types/immutable installed!", - "dev": true, - "license": "MIT", - "dependencies": { - "immutable": "*" - } + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1855,37 +1806,28 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.76.tgz", "integrity": "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==", "devOptional": true, - "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "19.1.13", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz", + "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.0.0" } }, "node_modules/@types/responselike": { @@ -1893,7 +1835,6 @@ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1903,17 +1844,15 @@ "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "*" } }, "node_modules/@types/ws": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", - "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1923,7 +1862,6 @@ "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1933,28 +1871,26 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz", - "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", + "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/type-utils": "8.26.1", - "@typescript-eslint/utils": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/type-utils": "8.42.0", + "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1964,22 +1900,30 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.42.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz", - "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", + "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4" }, "engines": { @@ -1991,38 +1935,74 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", + "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.42.0", + "@typescript-eslint/types": "^8.42.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", - "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", + "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1" + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", + "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz", - "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", + "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.26.1", - "@typescript-eslint/utils": "8.26.1", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/utils": "8.42.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2033,15 +2013,14 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", - "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", + "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2051,20 +2030,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", - "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", + "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/visitor-keys": "8.26.1", + "@typescript-eslint/project-service": "8.42.0", + "@typescript-eslint/tsconfig-utils": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2074,15 +2054,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2092,7 +2071,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2103,30 +2081,16 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", - "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", + "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2137,18 +2101,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", - "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", + "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.42.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2163,7 +2126,6 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz", "integrity": "sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=14.21.3" }, @@ -2172,29 +2134,28 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", - "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", - "license": "MIT", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", "engines": { "node": "^18.0.0 || >=20.0.0" }, @@ -2204,134 +2165,125 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", - "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", "peer": true, "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.13", + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.2.1" } }, - "node_modules/@vue/compiler-core/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT", - "peer": true - }, "node_modules/@vue/compiler-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", - "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", "peer": true, "dependencies": { - "@vue/compiler-core": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", - "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", "peer": true, "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.13", - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13", + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.48", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" } }, - "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT", - "peer": true - }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", - "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" } }, "node_modules/@vue/reactivity": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", - "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", "peer": true, "dependencies": { - "@vue/shared": "3.5.13" + "@vue/shared": "3.5.21" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", - "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", - "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/runtime-core": "3.5.13", - "@vue/shared": "3.5.13", + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", - "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" }, "peerDependencies": { - "vue": "3.5.13" + "vue": "3.5.21" } }, "node_modules/@vue/shared": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", - "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", "peer": true }, + "node_modules/@xterm/addon-fit": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", + "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", + "license": "MIT", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", + "license": "MIT" + }, "node_modules/@zip.js/zip.js": { - "version": "2.7.57", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", - "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", + "version": "2.7.73", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.73.tgz", + "integrity": "sha512-I2UP8/rdQE5hTtVVL08B7P8XuwXiKuuMUPjNuFOVL/9b+8IsExR9S5jz2H58u0rJjU4M1BikLgqEMG8gZJZVBw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "bun": ">=0.7.0", "deno": ">=1.0.0", @@ -2342,15 +2294,13 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, - "license": "MIT", "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -2360,10 +2310,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "license": "MIT", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "bin": { "acorn": "bin/acorn" }, @@ -2376,7 +2325,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2386,7 +2334,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2403,7 +2350,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -2413,7 +2359,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -2429,7 +2374,6 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -2443,7 +2387,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2455,14 +2398,12 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -2472,7 +2413,6 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -2489,24 +2429,24 @@ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2520,7 +2460,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2537,18 +2476,18 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2562,7 +2501,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -2581,7 +2519,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -2600,7 +2537,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -2617,7 +2553,6 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -2638,15 +2573,13 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2656,7 +2589,6 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -2671,7 +2603,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -2680,22 +2611,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -2708,7 +2636,6 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -2730,15 +2657,13 @@ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "license": "MIT", "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2749,7 +2674,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -2758,9 +2682,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", + "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", "funding": [ { "type": "opencollective", @@ -2775,12 +2699,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001737", + "electron-to-chromium": "^1.5.211", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -2794,7 +2717,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } @@ -2804,7 +2726,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -2814,7 +2735,6 @@ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.6.0" } @@ -2824,7 +2744,6 @@ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, - "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -2843,7 +2762,6 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -2862,7 +2780,6 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -2872,14 +2789,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -2893,15 +2809,14 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001739", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", + "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==", "funding": [ { "type": "opencollective", @@ -2915,15 +2830,13 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2940,7 +2853,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -2953,7 +2865,6 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2974,9 +2885,9 @@ } }, "node_modules/chromium-bidi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-7.2.0.tgz", - "integrity": "sha512-gREyhyBstermK+0RbcJLbFhcQctg92AGgDe/h/taMJEOLRdtSswBAO9KmvltFSQWgM2LrwWu5SIuEUbdm3JsyQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-9.0.0.tgz", + "integrity": "sha512-CGUetd0G5Bq93BRnckGppWN1BwKYTTW7tMtaZ9PxeFF0LFj3GErytqfI60KgkmB7CcnvWIjdRDfVcFWvzErSyQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2992,7 +2903,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3004,7 +2914,6 @@ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, - "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -3016,7 +2925,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -3024,15 +2932,13 @@ "node_modules/codemirror": { "version": "5.65.18", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz", - "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==", - "license": "MIT" + "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==" }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3044,41 +2950,28 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.1.90" } }, - "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/concurrently": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0", "date-fns": "^2.16.1", @@ -3101,7 +2994,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -3114,7 +3006,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3122,15 +3013,13 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3140,7 +3029,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.6.0" } @@ -3150,7 +3038,6 @@ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, - "license": "MIT", "dependencies": { "object-assign": "^4", "vary": "^1" @@ -3164,7 +3051,6 @@ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.1" }, @@ -3183,7 +3069,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3196,15 +3081,13 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -3222,7 +3105,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -3240,7 +3122,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -3258,7 +3139,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -3274,7 +3154,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3293,7 +3172,6 @@ "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "license": "MIT", "engines": { "node": "*" } @@ -3303,7 +3181,6 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, - "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -3319,7 +3196,6 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3331,14 +3207,12 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3348,7 +3222,6 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -3358,7 +3231,6 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3376,7 +3248,6 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -3394,7 +3265,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -3403,23 +3273,20 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/devtools-protocol": { - "version": "0.0.1421213", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1421213.tgz", - "integrity": "sha512-E0JrRYeXiTZXJwFlJ0j8wz8TmM+VwCv128JaSyCEgTmyE54QE/GDhT4w6kS1m+nFKmulHWWrbrO2AHexZgcxFQ==", + "version": "0.0.1510116", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1510116.tgz", + "integrity": "sha512-OJyQgco4wCKi1qk//4+ymbKuxsAEH6YCNeCjebUqSIre4SjgHjE2RiO4uY4byQIqDGqJ+8ZtLbnO2ZzNC2rjnw==", "dev": true, - "license": "BSD-3-Clause", "peer": true }, "node_modules/dezalgo": { @@ -3427,7 +3294,6 @@ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, - "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -3438,7 +3304,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -3447,11 +3312,10 @@ } }, "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -3464,7 +3328,6 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -3478,19 +3341,18 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/electron": { - "version": "30.5.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-30.5.1.tgz", - "integrity": "sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==", + "version": "38.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-38.1.0.tgz", + "integrity": "sha512-ypA8GF8RU4HD5pA1sa0/2U8k+92EPP2c7pX+3XbgB760F7OmqrFXtYkOilVw6HfV4+lk88XxqigmsUKTACQYoQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^22.7.7", "extract-zip": "^2.0.1" }, "bin": { @@ -3501,25 +3363,24 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", - "license": "ISC" + "version": "1.5.214", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.214.tgz", + "integrity": "sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==" }, "node_modules/electron/node_modules/@types/node": { - "version": "20.17.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", - "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", + "version": "22.18.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.1.tgz", + "integrity": "sha512-rzSDyhn4cYznVG+PCzGe1lwuMYJrcBS1fc3JqSa2PvtABwWo+dZ1ij5OVok3tqfpEBCBoaR4d7upFJk73HRJDw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/electron/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, @@ -3527,25 +3388,22 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -3554,7 +3412,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=0.12" @@ -3568,34 +3425,32 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -3607,21 +3462,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -3630,7 +3488,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -3644,7 +3502,6 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3654,7 +3511,6 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3664,7 +3520,6 @@ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -3692,7 +3547,6 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3705,7 +3559,6 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -3721,7 +3574,6 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -3734,7 +3586,6 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -3752,15 +3603,13 @@ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -3768,38 +3617,38 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -3808,15 +3657,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -3825,22 +3672,22 @@ } }, "node_modules/eslint": { - "version": "9.20.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", - "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.11.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.20.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -3848,9 +3695,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3889,7 +3736,6 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -3901,17 +3747,15 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -3929,36 +3773,34 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, - "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", + "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", + "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "engines": { @@ -3973,17 +3815,24 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-notice": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-notice/-/eslint-plugin-notice-1.0.0.tgz", "integrity": "sha512-M3VLQMZzZpvfTZ/vy9FmClIKq5rLBbQpM0KgfLZPJPrVXpmJYeobmmb+lfJzHWdNm8PWwvw8KlafQWo2N9xx1Q==", "dev": true, - "license": "MIT", "dependencies": { "find-root": "^1.1.0", "lodash": "^4.17.21", @@ -3994,11 +3843,10 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -4010,7 +3858,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -4027,11 +3875,10 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", - "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -4044,7 +3891,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -4057,12 +3903,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -4075,11 +3929,10 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4092,7 +3945,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -4103,19 +3955,17 @@ "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "license": "MIT" + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4129,7 +3979,6 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -4141,7 +3990,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz", "integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==", - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -4151,7 +3999,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -4164,17 +4011,21 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "peer": true + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -4184,7 +4035,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -4194,7 +4044,6 @@ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "dev": true, - "license": "MIT", "dependencies": { "eventsource-parser": "^3.0.1" }, @@ -4203,13 +4052,12 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", - "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": ">=18.0.0" } }, "node_modules/express": { @@ -4217,7 +4065,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -4260,7 +4107,6 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 16" }, @@ -4276,7 +4122,6 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -4296,15 +4141,13 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4320,22 +4163,19 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -4345,16 +4185,17 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, - "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "license": "MIT", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -4369,7 +4210,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -4382,7 +4222,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4395,7 +4234,6 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -4412,15 +4250,13 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -4437,7 +4273,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -4450,15 +4285,13 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -4470,14 +4303,13 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", "dev": true, - "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", "once": "^1.4.0", "qs": "^6.11.0" }, @@ -4490,7 +4322,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -4500,7 +4331,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -4510,7 +4340,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -4524,15 +4353,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -4546,7 +4373,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4556,7 +4382,6 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -4577,7 +4402,6 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4586,7 +4410,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -4596,24 +4419,22 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -4631,7 +4452,6 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -4645,7 +4465,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -4661,7 +4480,6 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -4680,7 +4498,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4701,7 +4518,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -4714,41 +4530,29 @@ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=10.0" } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -4756,7 +4560,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -4773,7 +4576,6 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4786,7 +4588,6 @@ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, - "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -4811,22 +4612,19 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -4836,7 +4634,6 @@ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.1.0" }, @@ -4847,19 +4644,11 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/graphql-tag/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4872,7 +4661,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -4882,7 +4670,6 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -4895,7 +4682,6 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.0" }, @@ -4911,7 +4697,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4924,7 +4709,6 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -4940,7 +4724,6 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, - "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -4948,40 +4731,27 @@ "node": ">= 0.4" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/html-reporter": { "resolved": "packages/html-reporter", "link": true }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true, - "license": "BSD-2-Clause" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, - "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -4998,7 +4768,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5008,7 +4777,6 @@ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, - "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -5022,7 +4790,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -5035,7 +4802,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -5044,15 +4810,13 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5069,7 +4833,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -5080,7 +4843,6 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5090,15 +4852,13 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -5113,7 +4873,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.10" } @@ -5123,7 +4882,6 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -5141,7 +4899,6 @@ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, - "license": "MIT", "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -5161,7 +4918,6 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -5177,7 +4933,6 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -5190,7 +4945,6 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5207,7 +4961,6 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5220,7 +4973,6 @@ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -5236,7 +4988,6 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -5254,7 +5005,6 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -5271,7 +5021,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5281,7 +5030,6 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5297,7 +5045,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5307,7 +5054,6 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", @@ -5326,7 +5072,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5339,7 +5084,18 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -5352,7 +5108,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5362,7 +5117,6 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5378,14 +5132,12 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/is-reference": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "license": "MIT", "dependencies": { "@types/estree": "^1.0.6" } @@ -5395,7 +5147,6 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -5414,7 +5165,6 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5427,7 +5177,6 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5443,7 +5192,6 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -5460,7 +5208,6 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -5478,7 +5225,6 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" }, @@ -5494,7 +5240,6 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5507,7 +5252,6 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -5523,7 +5267,6 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -5539,22 +5282,19 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", @@ -5570,15 +5310,13 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5590,7 +5328,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -5602,43 +5339,37 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, - "license": "ISC", "optional": true }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5651,7 +5382,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5661,7 +5391,6 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -5677,7 +5406,6 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -5686,7 +5414,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", "engines": { "node": ">=6" } @@ -5696,7 +5423,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5710,7 +5436,6 @@ "resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz", "integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "chalk": "^2.4.1", "debug": "^3.1.0", @@ -5732,7 +5457,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -5745,7 +5469,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5760,7 +5483,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -5769,15 +5491,13 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/license-checker/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5787,7 +5507,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -5797,7 +5516,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -5807,7 +5525,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -5817,7 +5534,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -5828,15 +5544,13 @@ "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "license": "MIT" + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -5851,22 +5565,19 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5879,7 +5590,6 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5888,18 +5598,16 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/matcher": { @@ -5907,7 +5615,6 @@ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" @@ -5921,7 +5628,6 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5931,7 +5637,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5941,7 +5646,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -5954,7 +5658,6 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -5963,15 +5666,13 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/metric-lcs/-/metric-lcs-0.1.2.tgz", "integrity": "sha512-+TZ5dUDPKPJaU/rscTzxyN8ZkX7eAVLAiQU/e+YINleXPv03SCmJShaMT1If1liTH8OcmWXZs0CmzCBRBLcMpA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -5985,7 +5686,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -5998,7 +5698,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -6011,7 +5710,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6021,7 +5719,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "dev": true, - "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, @@ -6034,7 +5731,6 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -6044,7 +5740,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6057,7 +5752,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6066,15 +5760,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -6085,8 +5777,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { "version": "3.3.11", @@ -6098,7 +5789,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6110,15 +5800,13 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6126,15 +5814,13 @@ "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, "node_modules/node-stream-zip": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" }, @@ -6148,7 +5834,6 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, - "license": "ISC", "dependencies": { "abbrev": "1", "osenv": "^0.1.4" @@ -6162,7 +5847,6 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -6175,7 +5859,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -6185,7 +5868,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6195,7 +5877,6 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6207,15 +5888,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6225,7 +5904,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6238,7 +5916,6 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6248,7 +5925,6 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6265,15 +5941,15 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -6284,7 +5960,6 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6303,7 +5978,6 @@ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -6318,7 +5992,6 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6337,7 +6010,6 @@ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -6350,7 +6022,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "license": "ISC", "dependencies": { "wrappy": "1" } @@ -6360,7 +6031,6 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -6378,7 +6048,6 @@ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6388,7 +6057,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6399,7 +6067,6 @@ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC", "dependencies": { "os-homedir": "^1.0.0", "os-tmpdir": "^1.0.0" @@ -6410,7 +6077,6 @@ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, - "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -6428,7 +6094,6 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6438,7 +6103,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6454,7 +6118,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -6470,7 +6133,6 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -6483,7 +6145,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6493,7 +6154,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6503,7 +6163,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6513,7 +6172,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6522,37 +6180,33 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -6565,7 +6219,6 @@ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=16.20.0" } @@ -6595,7 +6248,6 @@ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6618,7 +6270,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6633,7 +6284,6 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -6643,7 +6293,6 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -6653,7 +6302,6 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, - "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -6665,7 +6313,6 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -6675,11 +6322,10 @@ } }, "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -6690,7 +6336,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -6700,7 +6345,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" }, @@ -6729,15 +6373,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6750,66 +6392,74 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.6.3", + "iconv-lite": "0.7.0", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", "dev": true, - "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.1.1" } }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "license": "MIT", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "engines": { "node": ">=0.10.0" } @@ -6820,7 +6470,6 @@ "integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==", "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC", "dependencies": { "debuglog": "^1.0.1", "read-package-json": "^2.0.0", @@ -6838,7 +6487,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -6849,7 +6497,6 @@ "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.1", "json-parse-even-better-errors": "^2.3.0", @@ -6863,7 +6510,6 @@ "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, - "license": "ISC", "dependencies": { "debuglog": "^1.0.1", "dezalgo": "^1.0.0", @@ -6876,7 +6522,6 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -6889,7 +6534,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6906,7 +6550,6 @@ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -6924,19 +6567,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -6957,7 +6592,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6967,7 +6601,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -6987,15 +6620,13 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -7005,7 +6636,6 @@ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, - "license": "MIT", "dependencies": { "lowercase-keys": "^2.0.0" }, @@ -7018,7 +6648,6 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7029,7 +6658,6 @@ "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -7044,12 +6672,11 @@ } }, "node_modules/rollup": { - "version": "4.40.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", - "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", - "license": "MIT", + "version": "4.50.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz", + "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -7059,26 +6686,27 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.1", - "@rollup/rollup-android-arm64": "4.40.1", - "@rollup/rollup-darwin-arm64": "4.40.1", - "@rollup/rollup-darwin-x64": "4.40.1", - "@rollup/rollup-freebsd-arm64": "4.40.1", - "@rollup/rollup-freebsd-x64": "4.40.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", - "@rollup/rollup-linux-arm-musleabihf": "4.40.1", - "@rollup/rollup-linux-arm64-gnu": "4.40.1", - "@rollup/rollup-linux-arm64-musl": "4.40.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-gnu": "4.40.1", - "@rollup/rollup-linux-riscv64-musl": "4.40.1", - "@rollup/rollup-linux-s390x-gnu": "4.40.1", - "@rollup/rollup-linux-x64-gnu": "4.40.1", - "@rollup/rollup-linux-x64-musl": "4.40.1", - "@rollup/rollup-win32-arm64-msvc": "4.40.1", - "@rollup/rollup-win32-ia32-msvc": "4.40.1", - "@rollup/rollup-win32-x64-msvc": "4.40.1", + "@rollup/rollup-android-arm-eabi": "4.50.0", + "@rollup/rollup-android-arm64": "4.50.0", + "@rollup/rollup-darwin-arm64": "4.50.0", + "@rollup/rollup-darwin-x64": "4.50.0", + "@rollup/rollup-freebsd-arm64": "4.50.0", + "@rollup/rollup-freebsd-x64": "4.50.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", + "@rollup/rollup-linux-arm-musleabihf": "4.50.0", + "@rollup/rollup-linux-arm64-gnu": "4.50.0", + "@rollup/rollup-linux-arm64-musl": "4.50.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", + "@rollup/rollup-linux-ppc64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-musl": "4.50.0", + "@rollup/rollup-linux-s390x-gnu": "4.50.0", + "@rollup/rollup-linux-x64-gnu": "4.50.0", + "@rollup/rollup-linux-x64-musl": "4.50.0", + "@rollup/rollup-openharmony-arm64": "4.50.0", + "@rollup/rollup-win32-arm64-msvc": "4.50.0", + "@rollup/rollup-win32-ia32-msvc": "4.50.0", + "@rollup/rollup-win32-x64-msvc": "4.50.0", "fsevents": "~2.3.2" } }, @@ -7087,7 +6715,6 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -7118,7 +6745,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -7128,7 +6754,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -7136,12 +6761,17 @@ "npm": ">=2.0.0" } }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7174,15 +6804,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -7199,7 +6827,6 @@ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7216,33 +6843,31 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "license": "MIT" }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -7250,7 +6875,6 @@ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/send": { @@ -7258,7 +6882,6 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", @@ -7281,7 +6904,6 @@ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "type-fest": "^0.13.1" @@ -7298,7 +6920,6 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "dev": true, - "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -7314,7 +6935,6 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7332,7 +6952,6 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -7348,7 +6967,6 @@ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -7362,15 +6980,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7383,7 +6999,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -7393,7 +7008,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -7413,7 +7027,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -7430,7 +7043,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7449,7 +7061,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -7469,7 +7080,6 @@ "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", "dev": true, - "license": "ISC", "engines": { "node": "*" } @@ -7478,7 +7088,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7494,7 +7103,6 @@ "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", "dev": true, - "license": "MIT", "dependencies": { "array-find-index": "^1.0.2", "spdx-expression-parse": "^3.0.0", @@ -7506,7 +7114,6 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -7516,40 +7123,35 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" + "dev": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true }, "node_modules/spdx-ranges": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", - "dev": true, - "license": "(MIT AND CC-BY-3.0)" + "dev": true }, "node_modules/spdx-satisfies": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz", "integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==", "dev": true, - "license": "MIT", "dependencies": { "spdx-compare": "^1.0.0", "spdx-expression-parse": "^3.0.0", @@ -7561,32 +7163,41 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, - "license": "BSD-3-Clause", "optional": true }, "node_modules/ssim.js": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/ssim.js/-/ssim.js-3.5.0.tgz", "integrity": "sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7601,7 +7212,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -7629,7 +7239,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -7640,7 +7249,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7662,7 +7270,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -7681,7 +7288,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -7699,7 +7305,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7712,7 +7317,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -7722,7 +7326,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -7735,7 +7338,6 @@ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "debug": "^4.1.0" }, @@ -7748,7 +7350,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7764,7 +7365,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7773,12 +7373,11 @@ } }, "node_modules/svelte": { - "version": "5.37.3", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.37.3.tgz", - "integrity": "sha512-7t/ejshehHd+95z3Z7ebS7wsqHDQxi/8nBTuTRwpMgNegfRBfuitCSKTUDKIBOExqfT2+DhQ2VLG8Xn+cBXoaQ==", - "license": "MIT", + "version": "5.38.6", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.6.tgz", + "integrity": "sha512-ltBPlkvqk3bgCK7/N323atUpP3O3Y+DrGV4dcULrsSn4fZaaNnOmdplNznwfdWclAgvSr5rxjtzn/zJhRm6TKg==", "dependencies": { - "@ampproject/remapping": "^2.3.0", + "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", @@ -7801,7 +7400,6 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "license": "MIT", "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" @@ -7818,7 +7416,6 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -7831,7 +7428,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } @@ -7845,7 +7441,6 @@ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "license": "MIT", "bin": { "tree-kill": "cli.js" } @@ -7855,17 +7450,15 @@ "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/ts-api-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18.12" }, @@ -7878,7 +7471,6 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -7891,7 +7483,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.0" }, @@ -7900,18 +7491,16 @@ } }, "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } @@ -7921,7 +7510,6 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7934,7 +7522,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, - "license": "(MIT OR CC0-1.0)", "optional": true, "engines": { "node": ">=10" @@ -7948,7 +7535,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, - "license": "MIT", "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -7963,7 +7549,6 @@ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -7978,7 +7563,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -7998,7 +7582,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -8020,7 +7603,6 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -8037,11 +7619,10 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8055,7 +7636,6 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -8074,7 +7654,6 @@ "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "dev": true, - "license": "MIT", "dependencies": { "@fastify/busboy": "^2.0.0" }, @@ -8086,22 +7665,19 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/universal-user-agent": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -8111,15 +7687,14 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -8134,7 +7709,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -8151,7 +7725,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -8160,15 +7733,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -8179,15 +7750,14 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", + "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -8262,12 +7832,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, @@ -8278,17 +7842,16 @@ } }, "node_modules/vue": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", - "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", - "license": "MIT", + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-sfc": "3.5.13", - "@vue/runtime-dom": "3.5.13", - "@vue/server-renderer": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" }, "peerDependencies": { "typescript": "*" @@ -8308,7 +7871,6 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8324,7 +7886,6 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -8344,7 +7905,6 @@ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -8372,7 +7932,6 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -8387,16 +7946,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, @@ -8412,7 +7971,6 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8422,7 +7980,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8439,15 +7996,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -8469,7 +8024,6 @@ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", "dev": true, - "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -8483,34 +8037,15 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0" } }, - "node_modules/xterm": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", - "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", - "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", - "license": "MIT" - }, - "node_modules/xterm-addon-fit": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", - "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", - "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", - "license": "MIT", - "peerDependencies": { - "xterm": "^5.0.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } @@ -8518,14 +8053,12 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", - "license": "ISC", "bin": { "yaml": "bin.mjs" }, @@ -8538,7 +8071,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -8557,7 +8089,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "license": "ISC", "engines": { "node": ">=10" } @@ -8567,7 +8098,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -8578,7 +8108,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -8589,15 +8118,13 @@ "node_modules/zimmerframe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", - "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", - "license": "MIT" + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==" }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -8607,7 +8134,6 @@ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", "dev": true, - "license": "ISC", "peerDependencies": { "zod": "^3.24.1" } @@ -8616,10 +8142,10 @@ "version": "0.0.0" }, "packages/playwright": { - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8633,11 +8159,11 @@ }, "packages/playwright-browser-chromium": { "name": "@playwright/browser-chromium", - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" @@ -8645,11 +8171,11 @@ }, "packages/playwright-browser-firefox": { "name": "@playwright/browser-firefox", - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" @@ -8657,22 +8183,22 @@ }, "packages/playwright-browser-webkit": { "name": "@playwright/browser-webkit", - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" } }, "packages/playwright-chromium": { - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8686,14 +8212,14 @@ "version": "0.0.0", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "engines": { "node": ">=18" } }, "packages/playwright-core": { - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -8704,12 +8230,12 @@ }, "packages/playwright-ct-core": { "name": "@playwright/experimental-ct-core", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0-next", - "playwright-core": "1.55.0-next", - "vite": "^6.3.4" + "playwright": "1.56.0", + "playwright-core": "1.56.0", + "vite": "^6.3.6" }, "engines": { "node": ">=18" @@ -8717,10 +8243,10 @@ }, "packages/playwright-ct-react": { "name": "@playwright/experimental-ct-react", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.55.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8732,10 +8258,10 @@ }, "packages/playwright-ct-react17": { "name": "@playwright/experimental-ct-react17", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.55.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-react": "^4.2.1" }, "bin": { @@ -8747,10 +8273,10 @@ }, "packages/playwright-ct-svelte": { "name": "@playwright/experimental-ct-svelte", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.55.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@sveltejs/vite-plugin-svelte": "^5.1.0" }, "bin": { @@ -8763,50 +8289,12 @@ "node": ">=18" } }, - "packages/playwright-ct-svelte/node_modules/@sveltejs/vite-plugin-svelte": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", - "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", - "license": "MIT", - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", - "debug": "^4.4.1", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.17", - "vitefu": "^1.0.6" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22" - }, - "peerDependencies": { - "svelte": "^5.0.0", - "vite": "^6.0.0" - } - }, - "packages/playwright-ct-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", - "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.7" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "svelte": "^5.0.0", - "vite": "^6.0.0" - } - }, "packages/playwright-ct-vue": { "name": "@playwright/experimental-ct-vue", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "@playwright/experimental-ct-core": "1.55.0-next", + "@playwright/experimental-ct-core": "1.56.0", "@vitejs/plugin-vue": "^5.2.0" }, "bin": { @@ -8817,11 +8305,11 @@ } }, "packages/playwright-firefox": { - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8830,57 +8318,12 @@ "node": ">=18" } }, - "packages/playwright-mcp": { - "name": "@playwright/mcp", - "version": "1.52.0-next", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.6.1", - "commander": "^13.1.0", - "playwright": "1.52.0-next", - "zod-to-json-schema": "^3.24.4" - }, - "bin": { - "mcp": "cli.js" - }, - "devDependencies": { - "@types/node": "^22.13.10" - }, - "engines": { - "node": ">=18" - } - }, - "packages/playwright-mdd": { - "name": "@playwright/mdd", - "version": "0.0.1", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "commander": "^13.1.0", - "debug": "^4.4.1", - "dotenv": "^16.5.0", - "mime": "^4.0.7", - "openai": "^5.7.0", - "playwright-core": "1.55.0-next", - "zod-to-json-schema": "^3.24.4" - }, - "bin": { - "playwright-mdd": "cli.js" - }, - "devDependencies": { - "@types/debug": "^4.1.7" - }, - "engines": { - "node": ">=18" - } - }, "packages/playwright-test": { "name": "@playwright/test", - "version": "1.55.0-next", + "version": "1.56.0", "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0-next" + "playwright": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8889,45 +8332,12 @@ "node": ">=18" } }, - "packages/playwright-test-mcp": { - "name": "@playwright/test-runner-mcp", - "version": "0.0.1", - "license": "Apache-2.0", - "dependencies": { - "commander": "^13.1.0", - "playwright": "1.55.0-next", - "playwright-core": "1.55.0-next" - }, - "bin": { - "mcp-server-playwright-test": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "packages/playwright-tools": { - "name": "@playwright/experimental-tools", - "version": "0.0.0", - "extraneous": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.52.0-next" - }, - "devDependencies": { - "@anthropic-ai/sdk": "^0.33.1", - "@modelcontextprotocol/sdk": "^1.6.1", - "openai": "^4.79.1" - }, - "engines": { - "node": ">=18" - } - }, "packages/playwright-webkit": { - "version": "1.55.0-next", + "version": "1.56.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" }, "bin": { "playwright": "cli.js" @@ -8941,7 +8351,6 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -8965,9 +8374,9 @@ "packages/web": { "version": "0.0.0", "dependencies": { - "codemirror": "5.65.18", - "xterm": "^5.1.0", - "xterm-addon-fit": "^0.7.0" + "@xterm/addon-fit": "^0.10.0", + "@xterm/xterm": "^5.5.0", + "codemirror": "5.65.18" } } } diff --git a/package.json b/package.json index 0677532561a88..706751a1a468c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "playwright-internal", "private": true, - "version": "1.55.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -30,6 +30,9 @@ "ttest": "node ./tests/playwright-test/stable-test-runner/node_modules/@playwright/test/cli test --config=tests/playwright-test/playwright.config.ts", "ct": "playwright test tests/components/test-all.spec.js --reporter=list", "test": "playwright test --config=tests/library/playwright.config.ts", + "test-mcp": "playwright test --config=tests/mcp/playwright.config.ts", + "ctest-mcp": "playwright test --config=tests/mcp/playwright.config.ts --project=chrome", + "etest-mcp": "playwright test --config=tests/mcp/playwright.config.extension.ts", "eslint": "eslint --cache", "tsc": "tsc -p . && tsc -p packages/html-reporter/", "doc": "node utils/doclint/cli.js", @@ -53,52 +56,53 @@ "@actions/core": "^1.10.0", "@actions/github": "^6.0.0", "@babel/code-frame": "^7.26.2", - "@eslint/compat": "^1.2.6", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.19.0", - "@modelcontextprotocol/sdk": "^1.17.3", + "@eslint/compat": "^1.3.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "^9.34.0", + "@modelcontextprotocol/sdk": "^1.17.5", "@octokit/graphql-schema": "^15.26.0", - "@stylistic/eslint-plugin": "^3.0.1", + "@stylistic/eslint-plugin": "^5.2.3", "@types/codemirror": "^5.60.7", "@types/formidable": "^2.0.4", - "@types/immutable": "^3.8.7", - "@types/node": "^18.19.68", - "@types/react": "^18.0.12", - "@types/react-dom": "^18.0.5", + "@types/node": "18.19.76", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", "@types/ws": "^8.5.3", "@types/xml2js": "^0.4.9", - "@typescript-eslint/eslint-plugin": "^8.26.1", - "@typescript-eslint/parser": "^8.26.1", - "@typescript-eslint/utils": "^8.26.1", + "@typescript-eslint/eslint-plugin": "^8.41.0", + "@typescript-eslint/parser": "^8.41.0", + "@typescript-eslint/utils": "^8.41.0", "@vitejs/plugin-basic-ssl": "^1.1.0", "@vitejs/plugin-react": "^4.2.1", "@zip.js/zip.js": "^2.7.29", "ansi-styles": "^4.3.0", "chokidar": "^3.5.3", - "chromium-bidi": "^7.2.0", + "chromium-bidi": "^9.0.0", "colors": "^1.4.0", "concurrently": "^6.2.1", "cross-env": "^7.0.3", "dotenv": "^16.4.5", - "electron": "^30.1.2", + "electron": "^38.1.0", "esbuild": "^0.25.0", - "eslint": "^9.19.0", - "eslint-plugin-import": "^2.31.0", + "eslint": "^9.34.0", + "eslint-plugin-import": "^2.32.0", "eslint-plugin-notice": "^1.0.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", "formidable": "^2.1.1", "immutable": "^4.3.7", "license-checker": "^25.0.1", "mime": "^3.0.0", "node-stream-zip": "^1.15.0", - "react": "^18.1.0", - "react-dom": "^18.1.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "ssim.js": "^3.5.0", - "typescript": "^5.8.2", - "vite": "^6.3.4", + "typescript": "^5.9.2", + "vite": "^6.3.6", "ws": "^8.17.1", "xml2js": "^0.5.0", - "yaml": "2.6.0" + "yaml": "2.6.0", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.24.6" } } diff --git a/packages/html-reporter/src/chip.tsx b/packages/html-reporter/src/chip.tsx index cdd07777a67f7..456c8157ed6b0 100644 --- a/packages/html-reporter/src/chip.tsx +++ b/packages/html-reporter/src/chip.tsx @@ -23,7 +23,7 @@ import { clsx } from '@web/uiUtils'; import { type AnchorID, useAnchor } from './links'; export const Chip: React.FC<{ - header: JSX.Element | string, + header: React.JSX.Element | string, expanded?: boolean, noInsets?: boolean, setExpanded?: (expanded: boolean) => void, @@ -48,7 +48,7 @@ export const Chip: React.FC<{ }; export const AutoChip: React.FC<{ - header: JSX.Element | string, + header: React.JSX.Element | string, initialExpanded?: boolean, noInsets?: boolean, children?: any, diff --git a/packages/html-reporter/src/colors.css b/packages/html-reporter/src/colors.css index c11249829415c..7c3bef0b9f131 100644 --- a/packages/html-reporter/src/colors.css +++ b/packages/html-reporter/src/colors.css @@ -215,6 +215,7 @@ SOFTWARE. */ --color-header-search-border: #57606a; --color-sidenav-selected-bg: #ffffff; --color-menu-bg-active: rgba(0,0,0,0); + --color-control-transparent-bg-hover: #818b981a; --color-input-disabled-bg: rgba(175,184,193,0.2); --color-timeline-badge-bg: #eaeef2; --color-ansi-black: #24292f; @@ -453,437 +454,441 @@ SOFTWARE. */ --color-scale-coral-9: #510901 } -@media(prefers-color-scheme: dark) { - :root { - --color-canvas-default-transparent: rgba(13,17,23,0); - --color-marketing-icon-primary: #79c0ff; - --color-marketing-icon-secondary: #1f6feb; - --color-diff-blob-addition-num-text: #c9d1d9; - --color-diff-blob-addition-fg: #c9d1d9; - --color-diff-blob-addition-num-bg: rgba(63,185,80,0.3); - --color-diff-blob-addition-line-bg: rgba(46,160,67,0.15); - --color-diff-blob-addition-word-bg: rgba(46,160,67,0.4); - --color-diff-blob-deletion-num-text: #c9d1d9; - --color-diff-blob-deletion-fg: #c9d1d9; - --color-diff-blob-deletion-num-bg: rgba(248,81,73,0.3); - --color-diff-blob-deletion-line-bg: rgba(248,81,73,0.15); - --color-diff-blob-deletion-word-bg: rgba(248,81,73,0.4); - --color-diff-blob-hunk-num-bg: rgba(56,139,253,0.4); - --color-diff-blob-expander-icon: #8b949e; - --color-diff-blob-selected-line-highlight-mix-blend-mode: screen; - --color-diffstat-deletion-border: rgba(240,246,252,0.1); - --color-diffstat-addition-border: rgba(240,246,252,0.1); - --color-diffstat-addition-bg: #3fb950; - --color-search-keyword-hl: rgba(210,153,34,0.4); - --color-prettylights-syntax-comment: #8b949e; - --color-prettylights-syntax-constant: #79c0ff; - --color-prettylights-syntax-entity: #d2a8ff; - --color-prettylights-syntax-storage-modifier-import: #c9d1d9; - --color-prettylights-syntax-entity-tag: #7ee787; - --color-prettylights-syntax-keyword: #ff7b72; - --color-prettylights-syntax-string: #a5d6ff; - --color-prettylights-syntax-variable: #ffa657; - --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; - --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; - --color-prettylights-syntax-invalid-illegal-bg: #8e1519; - --color-prettylights-syntax-carriage-return-text: #f0f6fc; - --color-prettylights-syntax-carriage-return-bg: #b62324; - --color-prettylights-syntax-string-regexp: #7ee787; - --color-prettylights-syntax-markup-list: #f2cc60; - --color-prettylights-syntax-markup-heading: #1f6feb; - --color-prettylights-syntax-markup-italic: #c9d1d9; - --color-prettylights-syntax-markup-bold: #c9d1d9; - --color-prettylights-syntax-markup-deleted-text: #ffdcd7; - --color-prettylights-syntax-markup-deleted-bg: #67060c; - --color-prettylights-syntax-markup-inserted-text: #aff5b4; - --color-prettylights-syntax-markup-inserted-bg: #033a16; - --color-prettylights-syntax-markup-changed-text: #ffdfb6; - --color-prettylights-syntax-markup-changed-bg: #5a1e02; - --color-prettylights-syntax-markup-ignored-text: #c9d1d9; - --color-prettylights-syntax-markup-ignored-bg: #1158c7; - --color-prettylights-syntax-meta-diff-range: #d2a8ff; - --color-prettylights-syntax-brackethighlighter-angle: #8b949e; - --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; - --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; - --color-codemirror-text: #c9d1d9; - --color-codemirror-bg: #0d1117; - --color-codemirror-gutters-bg: #0d1117; - --color-codemirror-guttermarker-text: #0d1117; - --color-codemirror-guttermarker-subtle-text: #484f58; - --color-codemirror-linenumber-text: #8b949e; - --color-codemirror-cursor: #c9d1d9; - --color-codemirror-selection-bg: rgba(56,139,253,0.4); - --color-codemirror-activeline-bg: rgba(110,118,129,0.1); - --color-codemirror-matchingbracket-text: #c9d1d9; - --color-codemirror-lines-bg: #0d1117; - --color-codemirror-syntax-comment: #8b949e; - --color-codemirror-syntax-constant: #79c0ff; - --color-codemirror-syntax-entity: #d2a8ff; - --color-codemirror-syntax-keyword: #ff7b72; - --color-codemirror-syntax-storage: #ff7b72; - --color-codemirror-syntax-string: #a5d6ff; - --color-codemirror-syntax-support: #79c0ff; - --color-codemirror-syntax-variable: #ffa657; - --color-checks-bg: #010409; - --color-checks-run-border-width: 1px; - --color-checks-container-border-width: 1px; - --color-checks-text-primary: #c9d1d9; - --color-checks-text-secondary: #8b949e; - --color-checks-text-link: #58a6ff; - --color-checks-btn-icon: #8b949e; - --color-checks-btn-hover-icon: #c9d1d9; - --color-checks-btn-hover-bg: rgba(110,118,129,0.1); - --color-checks-input-text: #8b949e; - --color-checks-input-placeholder-text: #484f58; - --color-checks-input-focus-text: #c9d1d9; - --color-checks-input-bg: #161b22; - --color-checks-input-shadow: none; - --color-checks-donut-error: #f85149; - --color-checks-donut-pending: #d29922; - --color-checks-donut-success: #2ea043; - --color-checks-donut-neutral: #8b949e; - --color-checks-dropdown-text: #c9d1d9; - --color-checks-dropdown-bg: #161b22; - --color-checks-dropdown-border: #30363d; - --color-checks-dropdown-shadow: rgba(1,4,9,0.3); - --color-checks-dropdown-hover-text: #c9d1d9; - --color-checks-dropdown-hover-bg: rgba(110,118,129,0.1); - --color-checks-dropdown-btn-hover-text: #c9d1d9; - --color-checks-dropdown-btn-hover-bg: rgba(110,118,129,0.1); - --color-checks-scrollbar-thumb-bg: rgba(110,118,129,0.4); - --color-checks-header-label-text: #8b949e; - --color-checks-header-label-open-text: #c9d1d9; - --color-checks-header-border: #21262d; - --color-checks-header-icon: #8b949e; - --color-checks-line-text: #8b949e; - --color-checks-line-num-text: #484f58; - --color-checks-line-timestamp-text: #484f58; - --color-checks-line-hover-bg: rgba(110,118,129,0.1); - --color-checks-line-selected-bg: rgba(56,139,253,0.15); - --color-checks-line-selected-num-text: #58a6ff; - --color-checks-line-dt-fm-text: #f0f6fc; - --color-checks-line-dt-fm-bg: #9e6a03; - --color-checks-gate-bg: rgba(187,128,9,0.15); - --color-checks-gate-text: #8b949e; - --color-checks-gate-waiting-text: #d29922; - --color-checks-step-header-open-bg: #161b22; - --color-checks-step-error-text: #f85149; - --color-checks-step-warning-text: #d29922; - --color-checks-logline-text: #8b949e; - --color-checks-logline-num-text: #484f58; - --color-checks-logline-debug-text: #a371f7; - --color-checks-logline-error-text: #8b949e; - --color-checks-logline-error-num-text: #484f58; - --color-checks-logline-error-bg: rgba(248,81,73,0.15); - --color-checks-logline-warning-text: #8b949e; - --color-checks-logline-warning-num-text: #d29922; - --color-checks-logline-warning-bg: rgba(187,128,9,0.15); - --color-checks-logline-command-text: #58a6ff; - --color-checks-logline-section-text: #3fb950; - --color-checks-ansi-black: #0d1117; - --color-checks-ansi-black-bright: #161b22; - --color-checks-ansi-white: #b1bac4; - --color-checks-ansi-white-bright: #b1bac4; - --color-checks-ansi-gray: #6e7681; - --color-checks-ansi-red: #ff7b72; - --color-checks-ansi-red-bright: #ffa198; - --color-checks-ansi-green: #3fb950; - --color-checks-ansi-green-bright: #56d364; - --color-checks-ansi-yellow: #d29922; - --color-checks-ansi-yellow-bright: #e3b341; - --color-checks-ansi-blue: #58a6ff; - --color-checks-ansi-blue-bright: #79c0ff; - --color-checks-ansi-magenta: #bc8cff; - --color-checks-ansi-magenta-bright: #d2a8ff; - --color-checks-ansi-cyan: #76e3ea; - --color-checks-ansi-cyan-bright: #b3f0ff; - --color-project-header-bg: #0d1117; - --color-project-sidebar-bg: #161b22; - --color-project-gradient-in: #161b22; - --color-project-gradient-out: rgba(22,27,34,0); - --color-mktg-success: rgba(41,147,61,1); - --color-mktg-info: rgba(42,123,243,1); - --color-mktg-bg-shade-gradient-top: rgba(1,4,9,0.065); - --color-mktg-bg-shade-gradient-bottom: rgba(1,4,9,0); - --color-mktg-btn-bg-top: hsla(228,82%,66%,1); - --color-mktg-btn-bg-bottom: #4969ed; - --color-mktg-btn-bg-overlay-top: hsla(228,74%,59%,1); - --color-mktg-btn-bg-overlay-bottom: #3355e0; - --color-mktg-btn-text: #f0f6fc; - --color-mktg-btn-primary-bg-top: hsla(137,56%,46%,1); - --color-mktg-btn-primary-bg-bottom: #2ea44f; - --color-mktg-btn-primary-bg-overlay-top: hsla(134,60%,38%,1); - --color-mktg-btn-primary-bg-overlay-bottom: #22863a; - --color-mktg-btn-primary-text: #f0f6fc; - --color-mktg-btn-enterprise-bg-top: hsla(249,100%,72%,1); - --color-mktg-btn-enterprise-bg-bottom: #6f57ff; - --color-mktg-btn-enterprise-bg-overlay-top: hsla(248,65%,63%,1); - --color-mktg-btn-enterprise-bg-overlay-bottom: #614eda; - --color-mktg-btn-enterprise-text: #f0f6fc; - --color-mktg-btn-outline-text: #f0f6fc; - --color-mktg-btn-outline-border: rgba(240,246,252,0.3); - --color-mktg-btn-outline-hover-text: #f0f6fc; - --color-mktg-btn-outline-hover-border: rgba(240,246,252,0.5); - --color-mktg-btn-outline-focus-border: #f0f6fc; - --color-mktg-btn-outline-focus-border-inset: rgba(240,246,252,0.5); - --color-mktg-btn-dark-text: #f0f6fc; - --color-mktg-btn-dark-border: rgba(240,246,252,0.3); - --color-mktg-btn-dark-hover-text: #f0f6fc; - --color-mktg-btn-dark-hover-border: rgba(240,246,252,0.5); - --color-mktg-btn-dark-focus-border: #f0f6fc; - --color-mktg-btn-dark-focus-border-inset: rgba(240,246,252,0.5); - --color-avatar-bg: rgba(240,246,252,0.1); - --color-avatar-border: rgba(240,246,252,0.1); - --color-avatar-stack-fade: #30363d; - --color-avatar-stack-fade-more: #21262d; - --color-avatar-child-shadow: -2px -2px 0 #0d1117; - --color-topic-tag-border: rgba(0,0,0,0); - --color-select-menu-backdrop-border: #484f58; - --color-select-menu-tap-highlight: rgba(48,54,61,0.5); - --color-select-menu-tap-focus-bg: #0c2d6b; - --color-overlay-shadow: 0 0 0 1px #30363d, 0 16px 32px rgba(1,4,9,0.85); - --color-header-text: rgba(240,246,252,0.7); - --color-header-bg: #161b22; - --color-header-logo: #f0f6fc; - --color-header-search-bg: #0d1117; - --color-header-search-border: #30363d; - --color-sidenav-selected-bg: #21262d; - --color-menu-bg-active: #161b22; - --color-input-disabled-bg: rgba(110,118,129,0); - --color-timeline-badge-bg: #21262d; - --color-ansi-black: #484f58; - --color-ansi-black-bright: #6e7681; - --color-ansi-white: #b1bac4; - --color-ansi-white-bright: #f0f6fc; - --color-ansi-gray: #6e7681; - --color-ansi-red: #ff7b72; - --color-ansi-red-bright: #ffa198; - --color-ansi-green: #3fb950; - --color-ansi-green-bright: #56d364; - --color-ansi-yellow: #d29922; - --color-ansi-yellow-bright: #e3b341; - --color-ansi-blue: #58a6ff; - --color-ansi-blue-bright: #79c0ff; - --color-ansi-magenta: #bc8cff; - --color-ansi-magenta-bright: #d2a8ff; - --color-ansi-cyan: #39c5cf; - --color-ansi-cyan-bright: #56d4dd; - --color-btn-text: #c9d1d9; - --color-btn-bg: #21262d; - --color-btn-border: rgba(240,246,252,0.1); - --color-btn-shadow: 0 0 transparent; - --color-btn-inset-shadow: 0 0 transparent; - --color-btn-hover-bg: #30363d; - --color-btn-hover-border: #8b949e; - --color-btn-active-bg: hsla(212,12%,18%,1); - --color-btn-active-border: #6e7681; - --color-btn-selected-bg: #161b22; - --color-btn-focus-bg: #21262d; - --color-btn-focus-border: #8b949e; - --color-btn-focus-shadow: 0 0 0 3px rgba(139,148,158,0.3); - --color-btn-shadow-active: inset 0 0.15em 0.3em rgba(1,4,9,0.15); - --color-btn-shadow-input-focus: 0 0 0 0.2em rgba(31,111,235,0.3); - --color-btn-counter-bg: #30363d; - --color-btn-primary-text: #ffffff; - --color-btn-primary-bg: #238636; - --color-btn-primary-border: rgba(240,246,252,0.1); - --color-btn-primary-shadow: 0 0 transparent; - --color-btn-primary-inset-shadow: 0 0 transparent; - --color-btn-primary-hover-bg: #2ea043; - --color-btn-primary-hover-border: rgba(240,246,252,0.1); - --color-btn-primary-selected-bg: #238636; - --color-btn-primary-selected-shadow: 0 0 transparent; - --color-btn-primary-disabled-text: rgba(240,246,252,0.5); - --color-btn-primary-disabled-bg: rgba(35,134,54,0.6); - --color-btn-primary-disabled-border: rgba(240,246,252,0.1); - --color-btn-primary-focus-bg: #238636; - --color-btn-primary-focus-border: rgba(240,246,252,0.1); - --color-btn-primary-focus-shadow: 0 0 0 3px rgba(46,164,79,0.4); - --color-btn-primary-icon: #f0f6fc; - --color-btn-primary-counter-bg: rgba(240,246,252,0.2); - --color-btn-outline-text: #58a6ff; - --color-btn-outline-hover-text: #58a6ff; - --color-btn-outline-hover-bg: #30363d; - --color-btn-outline-hover-border: rgba(240,246,252,0.1); - --color-btn-outline-hover-shadow: 0 1px 0 rgba(1,4,9,0.1); - --color-btn-outline-hover-inset-shadow: inset 0 1px 0 rgba(240,246,252,0.03); - --color-btn-outline-hover-counter-bg: rgba(240,246,252,0.2); - --color-btn-outline-selected-text: #f0f6fc; - --color-btn-outline-selected-bg: #0d419d; - --color-btn-outline-selected-border: rgba(240,246,252,0.1); - --color-btn-outline-selected-shadow: 0 0 transparent; - --color-btn-outline-disabled-text: rgba(88,166,255,0.5); - --color-btn-outline-disabled-bg: #0d1117; - --color-btn-outline-disabled-counter-bg: rgba(31,111,235,0.05); - --color-btn-outline-focus-border: rgba(240,246,252,0.1); - --color-btn-outline-focus-shadow: 0 0 0 3px rgba(17,88,199,0.4); - --color-btn-outline-counter-bg: rgba(31,111,235,0.1); - --color-btn-danger-text: #f85149; - --color-btn-danger-hover-text: #f0f6fc; - --color-btn-danger-hover-bg: #da3633; - --color-btn-danger-hover-border: #f85149; - --color-btn-danger-hover-shadow: 0 0 transparent; - --color-btn-danger-hover-inset-shadow: 0 0 transparent; - --color-btn-danger-hover-icon: #f0f6fc; - --color-btn-danger-hover-counter-bg: rgba(255,255,255,0.2); - --color-btn-danger-selected-text: #ffffff; - --color-btn-danger-selected-bg: #b62324; - --color-btn-danger-selected-border: #ff7b72; - --color-btn-danger-selected-shadow: 0 0 transparent; - --color-btn-danger-disabled-text: rgba(248,81,73,0.5); - --color-btn-danger-disabled-bg: #0d1117; - --color-btn-danger-disabled-counter-bg: rgba(218,54,51,0.05); - --color-btn-danger-focus-border: #f85149; - --color-btn-danger-focus-shadow: 0 0 0 3px rgba(248,81,73,0.4); - --color-btn-danger-counter-bg: rgba(218,54,51,0.1); - --color-btn-danger-icon: #f85149; - --color-underlinenav-icon: #484f58; - --color-underlinenav-border-hover: rgba(110,118,129,0.4); - --color-fg-default: #c9d1d9; - --color-fg-muted: #8b949e; - --color-fg-subtle: #484f58; - --color-fg-on-emphasis: #f0f6fc; - --color-canvas-default: #0d1117; - --color-canvas-overlay: #161b22; - --color-canvas-inset: #010409; - --color-canvas-subtle: #161b22; - --color-border-default: #30363d; - --color-border-muted: #21262d; - --color-border-subtle: rgba(240,246,252,0.1); - --color-shadow-small: 0 0 transparent; - --color-shadow-medium: 0 3px 6px #010409; - --color-shadow-large: 0 8px 24px #010409; - --color-shadow-extra-large: 0 12px 48px #010409; - --color-neutral-emphasis-plus: #6e7681; - --color-neutral-emphasis: #6e7681; - --color-neutral-muted: rgba(110,118,129,0.4); - --color-neutral-subtle: rgba(110,118,129,0.1); - --color-accent-fg: #58a6ff; - --color-accent-emphasis: #1f6feb; - --color-accent-muted: rgba(56,139,253,0.4); - --color-accent-subtle: rgba(56,139,253,0.15); - --color-success-fg: #3fb950; - --color-success-emphasis: #238636; - --color-success-muted: rgba(46,160,67,0.4); - --color-success-subtle: rgba(46,160,67,0.15); - --color-attention-fg: #d29922; - --color-attention-emphasis: #9e6a03; - --color-attention-muted: rgba(187,128,9,0.4); - --color-attention-subtle: rgba(187,128,9,0.15); - --color-severe-fg: #db6d28; - --color-severe-emphasis: #bd561d; - --color-severe-muted: rgba(219,109,40,0.4); - --color-severe-subtle: rgba(219,109,40,0.15); - --color-danger-fg: #f85149; - --color-danger-emphasis: #da3633; - --color-danger-muted: rgba(248,81,73,0.4); - --color-danger-subtle: rgba(248,81,73,0.15); - --color-done-fg: #a371f7; - --color-done-emphasis: #8957e5; - --color-done-muted: rgba(163,113,247,0.4); - --color-done-subtle: rgba(163,113,247,0.15); - --color-sponsors-fg: #db61a2; - --color-sponsors-emphasis: #bf4b8a; - --color-sponsors-muted: rgba(219,97,162,0.4); - --color-sponsors-subtle: rgba(219,97,162,0.15); - --color-primer-canvas-backdrop: rgba(1,4,9,0.8); - --color-primer-canvas-sticky: rgba(13,17,23,0.95); - --color-primer-border-active: #F78166; - --color-primer-border-contrast: rgba(240,246,252,0.2); - --color-primer-shadow-highlight: 0 0 transparent; - --color-primer-shadow-inset: 0 0 transparent; - --color-primer-shadow-focus: 0 0 0 3px #0c2d6b; - --color-scale-black: #010409; - --color-scale-white: #f0f6fc; - --color-scale-gray-0: #f0f6fc; - --color-scale-gray-1: #c9d1d9; - --color-scale-gray-2: #b1bac4; - --color-scale-gray-3: #8b949e; - --color-scale-gray-4: #6e7681; - --color-scale-gray-5: #484f58; - --color-scale-gray-6: #30363d; - --color-scale-gray-7: #21262d; - --color-scale-gray-8: #161b22; - --color-scale-gray-9: #0d1117; - --color-scale-blue-0: #cae8ff; - --color-scale-blue-1: #a5d6ff; - --color-scale-blue-2: #79c0ff; - --color-scale-blue-3: #58a6ff; - --color-scale-blue-4: #388bfd; - --color-scale-blue-5: #1f6feb; - --color-scale-blue-6: #1158c7; - --color-scale-blue-7: #0d419d; - --color-scale-blue-8: #0c2d6b; - --color-scale-blue-9: #051d4d; - --color-scale-green-0: #aff5b4; - --color-scale-green-1: #7ee787; - --color-scale-green-2: #56d364; - --color-scale-green-3: #3fb950; - --color-scale-green-4: #2ea043; - --color-scale-green-5: #238636; - --color-scale-green-6: #196c2e; - --color-scale-green-7: #0f5323; - --color-scale-green-8: #033a16; - --color-scale-green-9: #04260f; - --color-scale-yellow-0: #f8e3a1; - --color-scale-yellow-1: #f2cc60; - --color-scale-yellow-2: #e3b341; - --color-scale-yellow-3: #d29922; - --color-scale-yellow-4: #bb8009; - --color-scale-yellow-5: #9e6a03; - --color-scale-yellow-6: #845306; - --color-scale-yellow-7: #693e00; - --color-scale-yellow-8: #4b2900; - --color-scale-yellow-9: #341a00; - --color-scale-orange-0: #ffdfb6; - --color-scale-orange-1: #ffc680; - --color-scale-orange-2: #ffa657; - --color-scale-orange-3: #f0883e; - --color-scale-orange-4: #db6d28; - --color-scale-orange-5: #bd561d; - --color-scale-orange-6: #9b4215; - --color-scale-orange-7: #762d0a; - --color-scale-orange-8: #5a1e02; - --color-scale-orange-9: #3d1300; - --color-scale-red-0: #ffdcd7; - --color-scale-red-1: #ffc1ba; - --color-scale-red-2: #ffa198; - --color-scale-red-3: #ff7b72; - --color-scale-red-4: #f85149; - --color-scale-red-5: #da3633; - --color-scale-red-6: #b62324; - --color-scale-red-7: #8e1519; - --color-scale-red-8: #67060c; - --color-scale-red-9: #490202; - --color-scale-purple-0: #eddeff; - --color-scale-purple-1: #e2c5ff; - --color-scale-purple-2: #d2a8ff; - --color-scale-purple-3: #bc8cff; - --color-scale-purple-4: #a371f7; - --color-scale-purple-5: #8957e5; - --color-scale-purple-6: #6e40c9; - --color-scale-purple-7: #553098; - --color-scale-purple-8: #3c1e70; - --color-scale-purple-9: #271052; - --color-scale-pink-0: #ffdaec; - --color-scale-pink-1: #ffbedd; - --color-scale-pink-2: #ff9bce; - --color-scale-pink-3: #f778ba; - --color-scale-pink-4: #db61a2; - --color-scale-pink-5: #bf4b8a; - --color-scale-pink-6: #9e3670; - --color-scale-pink-7: #7d2457; - --color-scale-pink-8: #5e103e; - --color-scale-pink-9: #42062a; - --color-scale-coral-0: #FFDDD2; - --color-scale-coral-1: #FFC2B2; - --color-scale-coral-2: #FFA28B; - --color-scale-coral-3: #F78166; - --color-scale-coral-4: #EA6045; - --color-scale-coral-5: #CF462D; - --color-scale-coral-6: #AC3220; - --color-scale-coral-7: #872012; - --color-scale-coral-8: #640D04; - --color-scale-coral-9: #460701 - } +:root.light-mode { + color-scheme: light; +} + +:root.dark-mode { + color-scheme: dark; + --color-canvas-default-transparent: rgba(13,17,23,0); + --color-marketing-icon-primary: #79c0ff; + --color-marketing-icon-secondary: #1f6feb; + --color-diff-blob-addition-num-text: #c9d1d9; + --color-diff-blob-addition-fg: #c9d1d9; + --color-diff-blob-addition-num-bg: rgba(63,185,80,0.3); + --color-diff-blob-addition-line-bg: rgba(46,160,67,0.15); + --color-diff-blob-addition-word-bg: rgba(46,160,67,0.4); + --color-diff-blob-deletion-num-text: #c9d1d9; + --color-diff-blob-deletion-fg: #c9d1d9; + --color-diff-blob-deletion-num-bg: rgba(248,81,73,0.3); + --color-diff-blob-deletion-line-bg: rgba(248,81,73,0.15); + --color-diff-blob-deletion-word-bg: rgba(248,81,73,0.4); + --color-diff-blob-hunk-num-bg: rgba(56,139,253,0.4); + --color-diff-blob-expander-icon: #8b949e; + --color-diff-blob-selected-line-highlight-mix-blend-mode: screen; + --color-diffstat-deletion-border: rgba(240,246,252,0.1); + --color-diffstat-addition-border: rgba(240,246,252,0.1); + --color-diffstat-addition-bg: #3fb950; + --color-search-keyword-hl: rgba(210,153,34,0.4); + --color-prettylights-syntax-comment: #8b949e; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #c9d1d9; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #c9d1d9; + --color-prettylights-syntax-markup-bold: #c9d1d9; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #c9d1d9; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-brackethighlighter-angle: #8b949e; + --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-codemirror-text: #c9d1d9; + --color-codemirror-bg: #0d1117; + --color-codemirror-gutters-bg: #0d1117; + --color-codemirror-guttermarker-text: #0d1117; + --color-codemirror-guttermarker-subtle-text: #484f58; + --color-codemirror-linenumber-text: #8b949e; + --color-codemirror-cursor: #c9d1d9; + --color-codemirror-selection-bg: rgba(56,139,253,0.4); + --color-codemirror-activeline-bg: rgba(110,118,129,0.1); + --color-codemirror-matchingbracket-text: #c9d1d9; + --color-codemirror-lines-bg: #0d1117; + --color-codemirror-syntax-comment: #8b949e; + --color-codemirror-syntax-constant: #79c0ff; + --color-codemirror-syntax-entity: #d2a8ff; + --color-codemirror-syntax-keyword: #ff7b72; + --color-codemirror-syntax-storage: #ff7b72; + --color-codemirror-syntax-string: #a5d6ff; + --color-codemirror-syntax-support: #79c0ff; + --color-codemirror-syntax-variable: #ffa657; + --color-checks-bg: #010409; + --color-checks-run-border-width: 1px; + --color-checks-container-border-width: 1px; + --color-checks-text-primary: #c9d1d9; + --color-checks-text-secondary: #8b949e; + --color-checks-text-link: #58a6ff; + --color-checks-btn-icon: #8b949e; + --color-checks-btn-hover-icon: #c9d1d9; + --color-checks-btn-hover-bg: rgba(110,118,129,0.1); + --color-checks-input-text: #8b949e; + --color-checks-input-placeholder-text: #484f58; + --color-checks-input-focus-text: #c9d1d9; + --color-checks-input-bg: #161b22; + --color-checks-input-shadow: none; + --color-checks-donut-error: #f85149; + --color-checks-donut-pending: #d29922; + --color-checks-donut-success: #2ea043; + --color-checks-donut-neutral: #8b949e; + --color-checks-dropdown-text: #c9d1d9; + --color-checks-dropdown-bg: #161b22; + --color-checks-dropdown-border: #30363d; + --color-checks-dropdown-shadow: rgba(1,4,9,0.3); + --color-checks-dropdown-hover-text: #c9d1d9; + --color-checks-dropdown-hover-bg: rgba(110,118,129,0.1); + --color-checks-dropdown-btn-hover-text: #c9d1d9; + --color-checks-dropdown-btn-hover-bg: rgba(110,118,129,0.1); + --color-checks-scrollbar-thumb-bg: rgba(110,118,129,0.4); + --color-checks-header-label-text: #8b949e; + --color-checks-header-label-open-text: #c9d1d9; + --color-checks-header-border: #21262d; + --color-checks-header-icon: #8b949e; + --color-checks-line-text: #8b949e; + --color-checks-line-num-text: #484f58; + --color-checks-line-timestamp-text: #484f58; + --color-checks-line-hover-bg: rgba(110,118,129,0.1); + --color-checks-line-selected-bg: rgba(56,139,253,0.15); + --color-checks-line-selected-num-text: #58a6ff; + --color-checks-line-dt-fm-text: #f0f6fc; + --color-checks-line-dt-fm-bg: #9e6a03; + --color-checks-gate-bg: rgba(187,128,9,0.15); + --color-checks-gate-text: #8b949e; + --color-checks-gate-waiting-text: #d29922; + --color-checks-step-header-open-bg: #161b22; + --color-checks-step-error-text: #f85149; + --color-checks-step-warning-text: #d29922; + --color-checks-logline-text: #8b949e; + --color-checks-logline-num-text: #484f58; + --color-checks-logline-debug-text: #a371f7; + --color-checks-logline-error-text: #8b949e; + --color-checks-logline-error-num-text: #484f58; + --color-checks-logline-error-bg: rgba(248,81,73,0.15); + --color-checks-logline-warning-text: #8b949e; + --color-checks-logline-warning-num-text: #d29922; + --color-checks-logline-warning-bg: rgba(187,128,9,0.15); + --color-checks-logline-command-text: #58a6ff; + --color-checks-logline-section-text: #3fb950; + --color-checks-ansi-black: #0d1117; + --color-checks-ansi-black-bright: #161b22; + --color-checks-ansi-white: #b1bac4; + --color-checks-ansi-white-bright: #b1bac4; + --color-checks-ansi-gray: #6e7681; + --color-checks-ansi-red: #ff7b72; + --color-checks-ansi-red-bright: #ffa198; + --color-checks-ansi-green: #3fb950; + --color-checks-ansi-green-bright: #56d364; + --color-checks-ansi-yellow: #d29922; + --color-checks-ansi-yellow-bright: #e3b341; + --color-checks-ansi-blue: #58a6ff; + --color-checks-ansi-blue-bright: #79c0ff; + --color-checks-ansi-magenta: #bc8cff; + --color-checks-ansi-magenta-bright: #d2a8ff; + --color-checks-ansi-cyan: #76e3ea; + --color-checks-ansi-cyan-bright: #b3f0ff; + --color-project-header-bg: #0d1117; + --color-project-sidebar-bg: #161b22; + --color-project-gradient-in: #161b22; + --color-project-gradient-out: rgba(22,27,34,0); + --color-mktg-success: rgba(41,147,61,1); + --color-mktg-info: rgba(42,123,243,1); + --color-mktg-bg-shade-gradient-top: rgba(1,4,9,0.065); + --color-mktg-bg-shade-gradient-bottom: rgba(1,4,9,0); + --color-mktg-btn-bg-top: hsla(228,82%,66%,1); + --color-mktg-btn-bg-bottom: #4969ed; + --color-mktg-btn-bg-overlay-top: hsla(228,74%,59%,1); + --color-mktg-btn-bg-overlay-bottom: #3355e0; + --color-mktg-btn-text: #f0f6fc; + --color-mktg-btn-primary-bg-top: hsla(137,56%,46%,1); + --color-mktg-btn-primary-bg-bottom: #2ea44f; + --color-mktg-btn-primary-bg-overlay-top: hsla(134,60%,38%,1); + --color-mktg-btn-primary-bg-overlay-bottom: #22863a; + --color-mktg-btn-primary-text: #f0f6fc; + --color-mktg-btn-enterprise-bg-top: hsla(249,100%,72%,1); + --color-mktg-btn-enterprise-bg-bottom: #6f57ff; + --color-mktg-btn-enterprise-bg-overlay-top: hsla(248,65%,63%,1); + --color-mktg-btn-enterprise-bg-overlay-bottom: #614eda; + --color-mktg-btn-enterprise-text: #f0f6fc; + --color-mktg-btn-outline-text: #f0f6fc; + --color-mktg-btn-outline-border: rgba(240,246,252,0.3); + --color-mktg-btn-outline-hover-text: #f0f6fc; + --color-mktg-btn-outline-hover-border: rgba(240,246,252,0.5); + --color-mktg-btn-outline-focus-border: #f0f6fc; + --color-mktg-btn-outline-focus-border-inset: rgba(240,246,252,0.5); + --color-mktg-btn-dark-text: #f0f6fc; + --color-mktg-btn-dark-border: rgba(240,246,252,0.3); + --color-mktg-btn-dark-hover-text: #f0f6fc; + --color-mktg-btn-dark-hover-border: rgba(240,246,252,0.5); + --color-mktg-btn-dark-focus-border: #f0f6fc; + --color-mktg-btn-dark-focus-border-inset: rgba(240,246,252,0.5); + --color-avatar-bg: rgba(240,246,252,0.1); + --color-avatar-border: rgba(240,246,252,0.1); + --color-avatar-stack-fade: #30363d; + --color-avatar-stack-fade-more: #21262d; + --color-avatar-child-shadow: -2px -2px 0 #0d1117; + --color-topic-tag-border: rgba(0,0,0,0); + --color-select-menu-backdrop-border: #484f58; + --color-select-menu-tap-highlight: rgba(48,54,61,0.5); + --color-select-menu-tap-focus-bg: #0c2d6b; + --color-overlay-shadow: 0 0 0 1px #30363d, 0 16px 32px rgba(1,4,9,0.85); + --color-header-text: rgba(240,246,252,0.7); + --color-header-bg: #161b22; + --color-header-logo: #f0f6fc; + --color-header-search-bg: #0d1117; + --color-header-search-border: #30363d; + --color-sidenav-selected-bg: #21262d; + --color-menu-bg-active: #161b22; + --color-control-transparent-bg-hover: #656c7633; + --color-input-disabled-bg: rgba(110,118,129,0); + --color-timeline-badge-bg: #21262d; + --color-ansi-black: #484f58; + --color-ansi-black-bright: #6e7681; + --color-ansi-white: #b1bac4; + --color-ansi-white-bright: #f0f6fc; + --color-ansi-gray: #6e7681; + --color-ansi-red: #ff7b72; + --color-ansi-red-bright: #ffa198; + --color-ansi-green: #3fb950; + --color-ansi-green-bright: #56d364; + --color-ansi-yellow: #d29922; + --color-ansi-yellow-bright: #e3b341; + --color-ansi-blue: #58a6ff; + --color-ansi-blue-bright: #79c0ff; + --color-ansi-magenta: #bc8cff; + --color-ansi-magenta-bright: #d2a8ff; + --color-ansi-cyan: #39c5cf; + --color-ansi-cyan-bright: #56d4dd; + --color-btn-text: #c9d1d9; + --color-btn-bg: #21262d; + --color-btn-border: rgba(240,246,252,0.1); + --color-btn-shadow: 0 0 transparent; + --color-btn-inset-shadow: 0 0 transparent; + --color-btn-hover-bg: #30363d; + --color-btn-hover-border: #8b949e; + --color-btn-active-bg: hsla(212,12%,18%,1); + --color-btn-active-border: #6e7681; + --color-btn-selected-bg: #161b22; + --color-btn-focus-bg: #21262d; + --color-btn-focus-border: #8b949e; + --color-btn-focus-shadow: 0 0 0 3px rgba(139,148,158,0.3); + --color-btn-shadow-active: inset 0 0.15em 0.3em rgba(1,4,9,0.15); + --color-btn-shadow-input-focus: 0 0 0 0.2em rgba(31,111,235,0.3); + --color-btn-counter-bg: #30363d; + --color-btn-primary-text: #ffffff; + --color-btn-primary-bg: #238636; + --color-btn-primary-border: rgba(240,246,252,0.1); + --color-btn-primary-shadow: 0 0 transparent; + --color-btn-primary-inset-shadow: 0 0 transparent; + --color-btn-primary-hover-bg: #2ea043; + --color-btn-primary-hover-border: rgba(240,246,252,0.1); + --color-btn-primary-selected-bg: #238636; + --color-btn-primary-selected-shadow: 0 0 transparent; + --color-btn-primary-disabled-text: rgba(240,246,252,0.5); + --color-btn-primary-disabled-bg: rgba(35,134,54,0.6); + --color-btn-primary-disabled-border: rgba(240,246,252,0.1); + --color-btn-primary-focus-bg: #238636; + --color-btn-primary-focus-border: rgba(240,246,252,0.1); + --color-btn-primary-focus-shadow: 0 0 0 3px rgba(46,164,79,0.4); + --color-btn-primary-icon: #f0f6fc; + --color-btn-primary-counter-bg: rgba(240,246,252,0.2); + --color-btn-outline-text: #58a6ff; + --color-btn-outline-hover-text: #58a6ff; + --color-btn-outline-hover-bg: #30363d; + --color-btn-outline-hover-border: rgba(240,246,252,0.1); + --color-btn-outline-hover-shadow: 0 1px 0 rgba(1,4,9,0.1); + --color-btn-outline-hover-inset-shadow: inset 0 1px 0 rgba(240,246,252,0.03); + --color-btn-outline-hover-counter-bg: rgba(240,246,252,0.2); + --color-btn-outline-selected-text: #f0f6fc; + --color-btn-outline-selected-bg: #0d419d; + --color-btn-outline-selected-border: rgba(240,246,252,0.1); + --color-btn-outline-selected-shadow: 0 0 transparent; + --color-btn-outline-disabled-text: rgba(88,166,255,0.5); + --color-btn-outline-disabled-bg: #0d1117; + --color-btn-outline-disabled-counter-bg: rgba(31,111,235,0.05); + --color-btn-outline-focus-border: rgba(240,246,252,0.1); + --color-btn-outline-focus-shadow: 0 0 0 3px rgba(17,88,199,0.4); + --color-btn-outline-counter-bg: rgba(31,111,235,0.1); + --color-btn-danger-text: #f85149; + --color-btn-danger-hover-text: #f0f6fc; + --color-btn-danger-hover-bg: #da3633; + --color-btn-danger-hover-border: #f85149; + --color-btn-danger-hover-shadow: 0 0 transparent; + --color-btn-danger-hover-inset-shadow: 0 0 transparent; + --color-btn-danger-hover-icon: #f0f6fc; + --color-btn-danger-hover-counter-bg: rgba(255,255,255,0.2); + --color-btn-danger-selected-text: #ffffff; + --color-btn-danger-selected-bg: #b62324; + --color-btn-danger-selected-border: #ff7b72; + --color-btn-danger-selected-shadow: 0 0 transparent; + --color-btn-danger-disabled-text: rgba(248,81,73,0.5); + --color-btn-danger-disabled-bg: #0d1117; + --color-btn-danger-disabled-counter-bg: rgba(218,54,51,0.05); + --color-btn-danger-focus-border: #f85149; + --color-btn-danger-focus-shadow: 0 0 0 3px rgba(248,81,73,0.4); + --color-btn-danger-counter-bg: rgba(218,54,51,0.1); + --color-btn-danger-icon: #f85149; + --color-underlinenav-icon: #484f58; + --color-underlinenav-border-hover: rgba(110,118,129,0.4); + --color-fg-default: #c9d1d9; + --color-fg-muted: #8b949e; + --color-fg-subtle: #484f58; + --color-fg-on-emphasis: #f0f6fc; + --color-canvas-default: #0d1117; + --color-canvas-overlay: #161b22; + --color-canvas-inset: #010409; + --color-canvas-subtle: #161b22; + --color-border-default: #30363d; + --color-border-muted: #21262d; + --color-border-subtle: rgba(240,246,252,0.1); + --color-shadow-small: 0 0 transparent; + --color-shadow-medium: 0 3px 6px #010409; + --color-shadow-large: 0 8px 24px #010409; + --color-shadow-extra-large: 0 12px 48px #010409; + --color-neutral-emphasis-plus: #6e7681; + --color-neutral-emphasis: #6e7681; + --color-neutral-muted: rgba(110,118,129,0.4); + --color-neutral-subtle: rgba(110,118,129,0.1); + --color-accent-fg: #58a6ff; + --color-accent-emphasis: #1f6feb; + --color-accent-muted: rgba(56,139,253,0.4); + --color-accent-subtle: rgba(56,139,253,0.15); + --color-success-fg: #3fb950; + --color-success-emphasis: #238636; + --color-success-muted: rgba(46,160,67,0.4); + --color-success-subtle: rgba(46,160,67,0.15); + --color-attention-fg: #d29922; + --color-attention-emphasis: #9e6a03; + --color-attention-muted: rgba(187,128,9,0.4); + --color-attention-subtle: rgba(187,128,9,0.15); + --color-severe-fg: #db6d28; + --color-severe-emphasis: #bd561d; + --color-severe-muted: rgba(219,109,40,0.4); + --color-severe-subtle: rgba(219,109,40,0.15); + --color-danger-fg: #f85149; + --color-danger-emphasis: #da3633; + --color-danger-muted: rgba(248,81,73,0.4); + --color-danger-subtle: rgba(248,81,73,0.15); + --color-done-fg: #a371f7; + --color-done-emphasis: #8957e5; + --color-done-muted: rgba(163,113,247,0.4); + --color-done-subtle: rgba(163,113,247,0.15); + --color-sponsors-fg: #db61a2; + --color-sponsors-emphasis: #bf4b8a; + --color-sponsors-muted: rgba(219,97,162,0.4); + --color-sponsors-subtle: rgba(219,97,162,0.15); + --color-primer-canvas-backdrop: rgba(1,4,9,0.8); + --color-primer-canvas-sticky: rgba(13,17,23,0.95); + --color-primer-border-active: #F78166; + --color-primer-border-contrast: rgba(240,246,252,0.2); + --color-primer-shadow-highlight: 0 0 transparent; + --color-primer-shadow-inset: 0 0 transparent; + --color-primer-shadow-focus: 0 0 0 3px #0c2d6b; + --color-scale-black: #010409; + --color-scale-white: #f0f6fc; + --color-scale-gray-0: #f0f6fc; + --color-scale-gray-1: #c9d1d9; + --color-scale-gray-2: #b1bac4; + --color-scale-gray-3: #8b949e; + --color-scale-gray-4: #6e7681; + --color-scale-gray-5: #484f58; + --color-scale-gray-6: #30363d; + --color-scale-gray-7: #21262d; + --color-scale-gray-8: #161b22; + --color-scale-gray-9: #0d1117; + --color-scale-blue-0: #cae8ff; + --color-scale-blue-1: #a5d6ff; + --color-scale-blue-2: #79c0ff; + --color-scale-blue-3: #58a6ff; + --color-scale-blue-4: #388bfd; + --color-scale-blue-5: #1f6feb; + --color-scale-blue-6: #1158c7; + --color-scale-blue-7: #0d419d; + --color-scale-blue-8: #0c2d6b; + --color-scale-blue-9: #051d4d; + --color-scale-green-0: #aff5b4; + --color-scale-green-1: #7ee787; + --color-scale-green-2: #56d364; + --color-scale-green-3: #3fb950; + --color-scale-green-4: #2ea043; + --color-scale-green-5: #238636; + --color-scale-green-6: #196c2e; + --color-scale-green-7: #0f5323; + --color-scale-green-8: #033a16; + --color-scale-green-9: #04260f; + --color-scale-yellow-0: #f8e3a1; + --color-scale-yellow-1: #f2cc60; + --color-scale-yellow-2: #e3b341; + --color-scale-yellow-3: #d29922; + --color-scale-yellow-4: #bb8009; + --color-scale-yellow-5: #9e6a03; + --color-scale-yellow-6: #845306; + --color-scale-yellow-7: #693e00; + --color-scale-yellow-8: #4b2900; + --color-scale-yellow-9: #341a00; + --color-scale-orange-0: #ffdfb6; + --color-scale-orange-1: #ffc680; + --color-scale-orange-2: #ffa657; + --color-scale-orange-3: #f0883e; + --color-scale-orange-4: #db6d28; + --color-scale-orange-5: #bd561d; + --color-scale-orange-6: #9b4215; + --color-scale-orange-7: #762d0a; + --color-scale-orange-8: #5a1e02; + --color-scale-orange-9: #3d1300; + --color-scale-red-0: #ffdcd7; + --color-scale-red-1: #ffc1ba; + --color-scale-red-2: #ffa198; + --color-scale-red-3: #ff7b72; + --color-scale-red-4: #f85149; + --color-scale-red-5: #da3633; + --color-scale-red-6: #b62324; + --color-scale-red-7: #8e1519; + --color-scale-red-8: #67060c; + --color-scale-red-9: #490202; + --color-scale-purple-0: #eddeff; + --color-scale-purple-1: #e2c5ff; + --color-scale-purple-2: #d2a8ff; + --color-scale-purple-3: #bc8cff; + --color-scale-purple-4: #a371f7; + --color-scale-purple-5: #8957e5; + --color-scale-purple-6: #6e40c9; + --color-scale-purple-7: #553098; + --color-scale-purple-8: #3c1e70; + --color-scale-purple-9: #271052; + --color-scale-pink-0: #ffdaec; + --color-scale-pink-1: #ffbedd; + --color-scale-pink-2: #ff9bce; + --color-scale-pink-3: #f778ba; + --color-scale-pink-4: #db61a2; + --color-scale-pink-5: #bf4b8a; + --color-scale-pink-6: #9e3670; + --color-scale-pink-7: #7d2457; + --color-scale-pink-8: #5e103e; + --color-scale-pink-9: #42062a; + --color-scale-coral-0: #FFDDD2; + --color-scale-coral-1: #FFC2B2; + --color-scale-coral-2: #FFA28B; + --color-scale-coral-3: #F78166; + --color-scale-coral-4: #EA6045; + --color-scale-coral-5: #CF462D; + --color-scale-coral-6: #AC3220; + --color-scale-coral-7: #872012; + --color-scale-coral-8: #640D04; + --color-scale-coral-9: #460701 } diff --git a/packages/html-reporter/src/common.css b/packages/html-reporter/src/common.css index dc3323d29ffb9..c34e279923c10 100644 --- a/packages/html-reporter/src/common.css +++ b/packages/html-reporter/src/common.css @@ -296,6 +296,22 @@ article, aside, details, figcaption, figure, footer, header, main, menu, nav, se background-color: var(--color-btn-hover-bg); } +input[type="checkbox"] { + outline: var(--color-focus-border); + height: 24px; +} + +dialog { + background-color: var(--color-canvas-subtle); + border: 1px solid var(--color-border-default); + border-radius: 6px; + padding: 6px; +} + +.subnav-item .octicon.octicon-settings { + margin-right: 0; +} + @media only screen and (max-width: 600px) { .subnav-item, .form-control { border-radius: 0 !important; diff --git a/packages/html-reporter/src/headerView.tsx b/packages/html-reporter/src/headerView.tsx index d385c12992069..471818876e0f8 100644 --- a/packages/html-reporter/src/headerView.tsx +++ b/packages/html-reporter/src/headerView.tsx @@ -24,6 +24,9 @@ import { Link, navigate, SearchParamsContext } from './links'; import { statusIcon } from './statusIcon'; import { filterWithQuery } from './filter'; import { linkifyText } from '@web/renderUtils'; +import { Dialog } from '@web/shared/dialog'; +import { useDarkModeSetting } from '@web/theme'; +import { useSetting } from '@web/uiUtils'; export const HeaderView: React.FC<{ title: string | undefined, @@ -90,6 +93,7 @@ const StatsNavView: React.FC<{ + ; }; @@ -112,3 +116,53 @@ const NavLink: React.FC<{ {count} ; }; + +const SettingsButton: React.FC = () => { + const settingsRef = React.useRef(null); + const [settingsOpen, setSettingsOpen] = React.useState(false); + const [darkMode, setDarkMode] = useDarkModeSetting(); + const [mergeFiles, setMergeFiles] = useSetting('mergeFiles', false); + + return <> +
{ + setSettingsOpen(!settingsOpen); + e.preventDefault(); + }} + onMouseDown={preventDefault}> + {icons.settings()} +
+ setSettingsOpen(false)} + anchor={settingsRef} + dataTestId='settings-dialog' + > + + + + ; +}; + +const preventDefault = (e: any) => { + e.stopPropagation(); + e.preventDefault(); +}; + +const stopPropagation = (e: any) => { + e.stopPropagation(); + e.stopImmediatePropagation(); +}; diff --git a/packages/html-reporter/src/icons.tsx b/packages/html-reporter/src/icons.tsx index a7368c5f4a30a..85ce05c3fccf1 100644 --- a/packages/html-reporter/src/icons.tsx +++ b/packages/html-reporter/src/icons.tsx @@ -103,3 +103,9 @@ export const copy = () => { ; }; + +export const settings = () => { + return ; +}; diff --git a/packages/html-reporter/src/index.tsx b/packages/html-reporter/src/index.tsx index 89a44fe02661f..df1f9b2ab7de5 100644 --- a/packages/html-reporter/src/index.tsx +++ b/packages/html-reporter/src/index.tsx @@ -28,6 +28,8 @@ const zipjs = zipImport as typeof zip; import logo from '@web/assets/playwright-logo.svg'; import { SearchParamsProvider } from './links'; +import { applyTheme } from '@web/theme'; + const link = document.createElement('link'); link.rel = 'shortcut icon'; link.href = logo; @@ -49,6 +51,7 @@ const ReportLoader: React.FC = () => { }; window.onload = () => { + applyTheme(); ReactDOM.createRoot(document.querySelector('#root')!).render(); }; diff --git a/packages/html-reporter/src/labels.css b/packages/html-reporter/src/labels.css index dbb38feaa4be5..ad9bdc3a36369 100644 --- a/packages/html-reporter/src/labels.css +++ b/packages/html-reporter/src/labels.css @@ -35,7 +35,7 @@ color: var(--color-fg-default); } -@media(prefers-color-scheme: light) { +:root.light-mode { .label-color-0 { background-color: var(--color-scale-blue-0); color: var(--color-scale-blue-6); @@ -68,7 +68,7 @@ } } -@media(prefers-color-scheme: dark) { +:root.dark-mode { .label-color-0 { background-color: var(--color-scale-blue-9); color: var(--color-scale-blue-2); diff --git a/packages/html-reporter/src/labels.tsx b/packages/html-reporter/src/labels.tsx index 8e1e6b5b6600d..253518eaa958d 100644 --- a/packages/html-reporter/src/labels.tsx +++ b/packages/html-reporter/src/labels.tsx @@ -44,7 +44,10 @@ export const ProjectAndTagLabelsView: React.FC<{ useLinks?: boolean, style?: React.CSSProperties, }> = ({ projectNames, activeProjectName, otherLabels, useLinks, style }) => { - return (projectNames.length > 0 || otherLabels.length > 0) && + // We can have an empty project name if we have no projects specified in the config + const hasProjectNames = projectNames.length > 0 && !!activeProjectName; + + return (hasProjectNames || otherLabels.length > 0) && {!!useLinks ? : } ; diff --git a/packages/html-reporter/src/metadataView.tsx b/packages/html-reporter/src/metadataView.tsx index 545eb14513a8a..11077b9a04345 100644 --- a/packages/html-reporter/src/metadataView.tsx +++ b/packages/html-reporter/src/metadataView.tsx @@ -65,21 +65,23 @@ const InnerMetadataView: React.FC<{ metadata: Metadata }> = params => { return
{commitInfo.ci && !commitInfo.gitCommit && } {commitInfo.gitCommit && } - {otherEntries.length > 0 && (commitInfo.gitCommit || commitInfo.ci) &&
} -
- {otherEntries.map(([propertyName, value]) => { - const valueString = typeof value !== 'object' || value === null || value === undefined ? String(value) : JSON.stringify(value); - const trimmedValue = valueString.length > 1000 ? valueString.slice(0, 1000) + '\u2026' : valueString; - return ( -
- - {propertyName} - : {linkifyText(trimmedValue)} - -
- ); - })} -
+ {otherEntries.length > 0 && <> + {(commitInfo.gitCommit || commitInfo.ci) &&
} +
+ {otherEntries.map(([propertyName, value]) => { + const valueString = typeof value !== 'object' || value === null || value === undefined ? String(value) : JSON.stringify(value); + const trimmedValue = valueString.length > 1000 ? valueString.slice(0, 1000) + '\u2026' : valueString; + return ( +
+ + {propertyName} + : {linkifyText(trimmedValue)} + +
+ ); + })} +
+ }
; }; diff --git a/packages/html-reporter/src/reportView.tsx b/packages/html-reporter/src/reportView.tsx index c68e130a5b533..c21ded5c7d669 100644 --- a/packages/html-reporter/src/reportView.tsx +++ b/packages/html-reporter/src/reportView.tsx @@ -26,6 +26,7 @@ import './reportView.css'; import { TestCaseView } from './testCaseView'; import { TestFilesHeader, TestFilesView } from './testFilesView'; import './theme.css'; +import { useSetting } from '@web/uiUtils'; declare global { interface Window { @@ -49,10 +50,11 @@ export const ReportView: React.FC<{ const [expandedFiles, setExpandedFiles] = React.useState>(new Map()); const [filterText, setFilterText] = React.useState(searchParams.get('q') || ''); const [metadataVisible, setMetadataVisible] = React.useState(false); + const [mergeFiles] = useSetting('mergeFiles', false); const testId = searchParams.get('testId'); const q = searchParams.get('q')?.toString() || ''; const filterParam = q ? '&q=' + q : ''; - const reportTitle = report?.json()?.title; + const reportTitle = report?.json()?.options.title; const testIdToFileIdMap = React.useMemo(() => { const map = new Map(); @@ -66,15 +68,8 @@ export const ReportView: React.FC<{ const filter = React.useMemo(() => Filter.parse(filterText), [filterText]); const filteredStats = React.useMemo(() => filter.empty() ? undefined : computeStats(report?.json().files || [], filter), [report, filter]); const testModel = React.useMemo(() => { - const result: TestModelSummary = { files: [], tests: [] }; - for (const file of report?.json().files || []) { - const tests = file.tests.filter(t => filter.matches(t)); - if (tests.length) - result.files.push({ ...file, tests }); - result.tests.push(...tests); - } - return result; - }, [report, filter]); + return mergeFiles ? createMergedFilesModel(report, filter) : createFilesModel(report, filter); + }, [report, filter, mergeFiles]); const { prev, next } = React.useMemo(() => { const index = testModel.tests.findIndex(t => t.testId === testId); @@ -133,7 +128,7 @@ export const ReportView: React.FC<{ setMetadataVisible(visible => !visible)}/> ; } + const { projectNames, metadata, options } = report.json(); return
filter.matches(t)); + if (tests.length) + result.files.push({ ...file, tests }); + result.tests.push(...tests); + } + return result; +} + +function createMergedFilesModel(report: LoadedReport | undefined, filter: Filter): TestModelSummary { + const groups: TestFileSummary[] = []; + const groupMap = new Map(); + + for (const file of report?.json().files || []) { + const tests = file.tests.filter(t => filter.matches(t)); + for (const test of tests) { + const describe = test.path[0] ?? ''; + let group = groupMap.get(describe); + if (!group) { + group = { + fileId: describe, + fileName: describe, + tests: [], + stats: { total: 0, expected: 0, unexpected: 0, flaky: 0, skipped: 0, ok: true } + }; + groupMap.set(describe, group); + groups.push(group); + } + const testCopy = { ...test, path: test.path.slice(1) }; + group.tests.push(testCopy); + } + } + + groups.sort((a, b) => a.fileName.localeCompare(b.fileName)); + + const result: TestModelSummary = { files: groups, tests: [] }; + for (const group of groups) + result.tests.push(...group.tests); + return result; +} diff --git a/packages/html-reporter/src/statusIcon.tsx b/packages/html-reporter/src/statusIcon.tsx index 39837955aecb7..778321276fecc 100644 --- a/packages/html-reporter/src/statusIcon.tsx +++ b/packages/html-reporter/src/statusIcon.tsx @@ -18,7 +18,7 @@ import * as icons from './icons'; import './colors.css'; import './common.css'; -export function statusIcon(status: 'failed' | 'timedOut' | 'skipped' | 'passed' | 'expected' | 'unexpected' | 'flaky' | 'interrupted'): JSX.Element { +export function statusIcon(status: 'failed' | 'timedOut' | 'skipped' | 'passed' | 'expected' | 'unexpected' | 'flaky' | 'interrupted'): React.JSX.Element { switch (status) { case 'failed': case 'unexpected': diff --git a/packages/html-reporter/src/tabbedPane.css b/packages/html-reporter/src/tabbedPane.css index 7a128da217f7e..042ab9f7608d5 100644 --- a/packages/html-reporter/src/tabbedPane.css +++ b/packages/html-reporter/src/tabbedPane.css @@ -59,12 +59,16 @@ overflow: hidden; text-overflow: ellipsis; display: inline-block; + height: 30px; + padding: 0 8px; + border-radius: 6px; } -.tabbed-pane-tab-element.selected { - border-bottom-color: #666; +.tabbed-pane-tab-label:hover { + background-color: var(--color-control-transparent-bg-hover); } -.tabbed-pane-tab-element:hover { - color: #333; +.tabbed-pane-tab-element.selected { + border-bottom-color: #666; + -webkit-text-stroke: 0.5px currentColor; } diff --git a/packages/html-reporter/src/tabbedPane.tsx b/packages/html-reporter/src/tabbedPane.tsx index af15b6b4fa381..a1e533348b873 100644 --- a/packages/html-reporter/src/tabbedPane.tsx +++ b/packages/html-reporter/src/tabbedPane.tsx @@ -15,12 +15,13 @@ */ import { clsx } from '@web/uiUtils'; +import './colors.css'; import './tabbedPane.css'; import * as React from 'react'; export interface TabbedPaneTab { id: string; - title: string | JSX.Element; + title: string | React.JSX.Element; count?: number; render: () => React.ReactElement; } diff --git a/packages/html-reporter/src/testCaseView.css b/packages/html-reporter/src/testCaseView.css index 9c8dd8f22eda1..496f1bdb2f409 100644 --- a/packages/html-reporter/src/testCaseView.css +++ b/packages/html-reporter/src/testCaseView.css @@ -41,6 +41,10 @@ padding: 0 8px 8px; } +.selected .test-case-run-duration { + -webkit-text-stroke: 0; +} + .test-case-run-duration { color: var(--color-fg-muted); padding-left: 8px; diff --git a/packages/html-reporter/src/testCaseView.tsx b/packages/html-reporter/src/testCaseView.tsx index c068a030b1537..75a8e1c742ea3 100644 --- a/packages/html-reporter/src/testCaseView.tsx +++ b/packages/html-reporter/src/testCaseView.tsx @@ -15,7 +15,7 @@ */ import type { TestAnnotation } from '@playwright/test'; -import type { TestCase, TestCaseSummary } from './types'; +import type { HTMLReportOptions, TestCase, TestCaseSummary } from './types'; import * as React from 'react'; import { TabbedPane } from './tabbedPane'; import { AutoChip } from './chip'; @@ -39,7 +39,8 @@ export const TestCaseView: React.FC<{ next: TestCaseSummary | undefined, prev: TestCaseSummary | undefined, run: number, -}> = ({ projectNames, test, testRunMetadata, run, next, prev }) => { + options?: HTMLReportOptions, +}> = ({ projectNames, test, testRunMetadata, run, next, prev, options }) => { const [selectedResultIndex, setSelectedResultIndex] = React.useState(run); const searchParams = React.useContext(SearchParamsContext); @@ -83,7 +84,7 @@ export const TestCaseView: React.FC<{ {!!visibleAnnotations.length && {visibleAnnotations.map((annotation, index) => )} } - + ; }, })) || []} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} /> diff --git a/packages/html-reporter/src/testFileView.css b/packages/html-reporter/src/testFileView.css index 006dda1042968..f081c696b9c3d 100644 --- a/packages/html-reporter/src/testFileView.css +++ b/packages/html-reporter/src/testFileView.css @@ -84,4 +84,17 @@ .test-file-header-br { flex-basis: 100%; height: 0; +} + +.test-file-no-files { + margin-top: 12px; + + color: var(--color-fg-muted); + background-color: unset; + + font-weight: unset; + + border: 1px solid var(--color-border-default); + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; } \ No newline at end of file diff --git a/packages/html-reporter/src/testFileView.tsx b/packages/html-reporter/src/testFileView.tsx index fa88fbb65be58..34e349b1e9486 100644 --- a/packages/html-reporter/src/testFileView.tsx +++ b/packages/html-reporter/src/testFileView.tsx @@ -71,7 +71,7 @@ export const TestFileView: React.FC; }; -function imageDiffBadge(test: TestCaseSummary): JSX.Element | undefined { +function imageDiffBadge(test: TestCaseSummary): React.JSX.Element | undefined { for (const result of test.results) { for (const attachment of result.attachments) { if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/)) @@ -80,7 +80,7 @@ function imageDiffBadge(test: TestCaseSummary): JSX.Element | undefined { } } -function videoBadge(test: TestCaseSummary): JSX.Element | undefined { +function videoBadge(test: TestCaseSummary): React.JSX.Element | undefined { const resultWithVideo = test.results.find(result => result.attachments.some(attachment => attachment.name === 'video')); return resultWithVideo ? {video()} : undefined; } diff --git a/packages/html-reporter/src/testFilesView.tsx b/packages/html-reporter/src/testFilesView.tsx index efffa58a955f1..7ecd07ba39520 100644 --- a/packages/html-reporter/src/testFilesView.tsx +++ b/packages/html-reporter/src/testFilesView.tsx @@ -18,6 +18,7 @@ import type { FilteredStats, HTMLReport, TestFileSummary } from './types'; import * as React from 'react'; import { TestFileView } from './testFileView'; import './testFileView.css'; +import './chip.css'; import { msToString } from './utils'; import { AutoChip } from './chip'; import { CodeSnippet } from './testErrorView'; @@ -27,39 +28,41 @@ import { HeaderView } from './headerView'; import { clsx } from '@web/uiUtils'; export const TestFilesView: React.FC<{ - tests: TestFileSummary[], + files: TestFileSummary[], expandedFiles: Map, setExpandedFiles: (value: Map) => void, projectNames: string[], -}> = ({ tests, expandedFiles, setExpandedFiles, projectNames }) => { +}> = ({ files, expandedFiles, setExpandedFiles, projectNames }) => { const filteredFiles = React.useMemo(() => { const result: { file: TestFileSummary, defaultExpanded: boolean }[] = []; let visibleTests = 0; - for (const file of tests) { + for (const file of files) { visibleTests += file.tests.length; result.push({ file, defaultExpanded: visibleTests < 200 }); } return result; - }, [tests]); + }, [files]); return <> - {filteredFiles.map(({ file, defaultExpanded }) => { - return { - const value = expandedFiles.get(fileId); - if (value === undefined) - return defaultExpanded; - return !!value; - }} - setFileExpanded={(fileId, expanded) => { - const newExpanded = new Map(expandedFiles); - newExpanded.set(fileId, expanded); - setExpandedFiles(newExpanded); - }}> - ; - })} + {filteredFiles.length > 0 ? + filteredFiles.map(({ file, defaultExpanded }) => { + return { + const value = expandedFiles.get(fileId); + if (value === undefined) + return defaultExpanded; + return !!value; + }} + setFileExpanded={(fileId, expanded) => { + const newExpanded = new Map(expandedFiles); + newExpanded.set(fileId, expanded); + setExpandedFiles(newExpanded); + }}> + ; + }) + :
No tests found
} ; }; @@ -93,7 +96,7 @@ export const TestFilesHeader: React.FC<{ ; return <> - + {!isMetadataInTopLine && metadataToggleButton} {metadataVisible && } {!!report.errors.length && diff --git a/packages/html-reporter/src/testResultView.css b/packages/html-reporter/src/testResultView.css index 81fd61453d4ed..7291d2a1c5548 100644 --- a/packages/html-reporter/src/testResultView.css +++ b/packages/html-reporter/src/testResultView.css @@ -45,13 +45,13 @@ padding: 2px 8px; } -@media(prefers-color-scheme: light) { +:root.light-mode { .test-result-counter { background: var(--color-scale-gray-5); } } -@media(prefers-color-scheme: dark) { +:root.dark-mode { .test-result-counter { background: var(--color-scale-gray-3); } diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index 8fa4ab017d4b4..1b5d7c6c87489 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -14,7 +14,7 @@ limitations under the License. */ -import type { TestAttachment, TestCase, TestResult, TestStep } from './types'; +import type { HTMLReportOptions, TestAttachment, TestCase, TestResult, TestStep } from './types'; import * as React from 'react'; import { TreeItem } from './treeItem'; import { msToString } from './utils'; @@ -74,7 +74,8 @@ export const TestResultView: React.FC<{ test: TestCase, result: TestResult, testRunMetadata: MetadataWithCommitInfo | undefined, -}> = ({ test, result, testRunMetadata }) => { + options?: HTMLReportOptions, +}> = ({ test, result, testRunMetadata, options }) => { const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors, errorContext } = React.useMemo(() => { const attachments = result.attachments.filter(a => !a.name.startsWith('_')); const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/'))); @@ -91,6 +92,9 @@ export const TestResultView: React.FC<{ }, [result]); const prompt = useAsyncMemo(async () => { + if (options?.noCopyPrompt) + return undefined; + const stdoutAttachment = result.attachments.find(a => a.name === 'stdout'); const stderrAttachment = result.attachments.find(a => a.name === 'stderr'); const stdout = stdoutAttachment?.body && stdoutAttachment.contentType === 'text/plain' ? stdoutAttachment.body : undefined; diff --git a/packages/html-reporter/src/treeItem.tsx b/packages/html-reporter/src/treeItem.tsx index 546510afc3fad..389e558650071 100644 --- a/packages/html-reporter/src/treeItem.tsx +++ b/packages/html-reporter/src/treeItem.tsx @@ -20,8 +20,8 @@ import * as icons from './icons'; import { clsx } from '@web/uiUtils'; export const TreeItem: React.FunctionComponent<{ - title: JSX.Element, - loadChildren?: () => JSX.Element[], + title: React.JSX.Element, + loadChildren?: () => React.JSX.Element[], onClick?: () => void, expandByDefault?: boolean, depth: number, diff --git a/packages/html-reporter/src/types.d.ts b/packages/html-reporter/src/types.d.ts index 1acf73d780a2f..270a8cb9838c5 100644 --- a/packages/html-reporter/src/types.d.ts +++ b/packages/html-reporter/src/types.d.ts @@ -36,15 +36,21 @@ export type Location = { column: number; }; +export type HTMLReportOptions = { + title?: string; + noCopyPrompt?: boolean; + noSnippets?: boolean; +}; + export type HTMLReport = { metadata: Metadata; - title: string | undefined; files: TestFileSummary[]; stats: Stats; projectNames: string[]; startTime: number; duration: number; errors: string[]; // Top-level errors that are not attributed to any test. + options: HTMLReportOptions; }; export type TestFile = { diff --git a/packages/injected/src/ariaSnapshot.ts b/packages/injected/src/ariaSnapshot.ts index 09c3bef843a7d..a1e1afd4256af 100644 --- a/packages/injected/src/ariaSnapshot.ts +++ b/packages/injected/src/ariaSnapshot.ts @@ -16,11 +16,11 @@ import { escapeRegExp, longestCommonSubstring, normalizeWhiteSpace } from '@isomorphic/stringUtils'; -import { box, getElementComputedStyle, isElementVisible } from './domUtils'; +import { computeBox, getElementComputedStyle, isElementVisible } from './domUtils'; import * as roleUtils from './roleUtils'; import { yamlEscapeKeyIfNeeded, yamlEscapeValueIfNeeded } from './yaml'; -import type { AriaProps, AriaRegex, AriaRole, AriaTemplateNode, AriaTemplateRoleNode, AriaTemplateTextNode } from '@isomorphic/ariaSnapshot'; +import type { AriaProps, AriaRegex, AriaTextValue, AriaRole, AriaTemplateNode } from '@isomorphic/ariaSnapshot'; import type { Box } from './domUtils'; export type AriaNode = AriaProps & { @@ -92,7 +92,7 @@ export function generateAriaTree(rootElement: Element, publicOptions: AriaTreeOp const visited = new Set(); const snapshot: AriaSnapshot = { - root: { role: 'fragment', name: '', children: [], element: rootElement, props: {}, box: box(rootElement), receivesPointerEvents: true }, + root: { role: 'fragment', name: '', children: [], element: rootElement, props: {}, box: computeBox(rootElement), receivesPointerEvents: true }, elements: new Map(), refs: new Map(), }; @@ -188,6 +188,11 @@ export function generateAriaTree(rootElement: Element, publicOptions: AriaTreeOp const href = element.getAttribute('href')!; ariaNode.props['url'] = href; } + + if (ariaNode.role === 'textbox' && element.hasAttribute('placeholder') && element.getAttribute('placeholder') !== ariaNode.name) { + const placeholder = element.getAttribute('placeholder')!; + ariaNode.props['placeholder'] = placeholder; + } } roleUtils.beginAriaCaches(); @@ -226,7 +231,7 @@ function toAriaNode(element: Element, options: InternalOptions): AriaNode | null children: [], props: {}, element, - box: box(element), + box: computeBox(element), receivesPointerEvents: true, active }; @@ -242,13 +247,17 @@ function toAriaNode(element: Element, options: InternalOptions): AriaNode | null const name = normalizeWhiteSpace(roleUtils.getElementAccessibleName(element, false) || ''); const receivesPointerEvents = roleUtils.receivesPointerEvents(element); + const box = computeBox(element); + if (role === 'generic' && box.inline && element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE) + return null; + const result: AriaNode = { role, name, children: [], props: {}, element, - box: box(element), + box, receivesPointerEvents, active }; @@ -293,7 +302,7 @@ function normalizeGenericRoles(node: AriaNode) { } // Only remove generic that encloses one element, logical grouping still makes sense, even if it is not ref-able. - const removeSelf = node.role === 'generic' && result.length <= 1 && result.every(c => typeof c !== 'string' && !!c.ref); + const removeSelf = node.role === 'generic' && !node.name && result.length <= 1 && result.every(c => typeof c !== 'string' && !!c.ref); if (removeSelf) return result; node.children = result; @@ -333,7 +342,7 @@ function normalizeStringChildren(rootA11yNode: AriaNode) { visit(rootA11yNode); } -function matchesText(text: string, template: AriaRegex | string | undefined): boolean { +function matchesStringOrRegex(text: string, template: AriaRegex | string | undefined): boolean { if (!template) return true; if (!text) @@ -343,12 +352,39 @@ function matchesText(text: string, template: AriaRegex | string | undefined): bo return !!text.match(new RegExp(template.pattern)); } -function matchesTextNode(text: string, template: AriaTemplateTextNode) { - return matchesText(text, template.text); +function matchesTextValue(text: string, template: AriaTextValue | undefined) { + if (!template?.normalized) + return true; + if (!text) + return false; + if (text === template.normalized) + return true; + // Accept pattern as value. + if (text === template.raw) + return true; + + const regex = cachedRegex(template); + if (regex) + return !!text.match(regex); + return false; } -function matchesName(text: string, template: AriaTemplateRoleNode) { - return matchesText(text, template.name); +const cachedRegexSymbol = Symbol('cachedRegex'); + +function cachedRegex(template: AriaTextValue): RegExp | null { + if ((template as any)[cachedRegexSymbol] !== undefined) + return (template as any)[cachedRegexSymbol]; + + const { raw } = template; + const canBeRegex = raw.startsWith('/') && raw.endsWith('/') && raw.length > 1; + let regex: RegExp | null; + try { + regex = canBeRegex ? new RegExp(raw.slice(1, -1)) : null; + } catch (e) { + regex = null; + } + (template as any)[cachedRegexSymbol] = regex; + return regex; } export type MatcherReceived = { @@ -376,7 +412,7 @@ export function getAllElementsMatchingExpectAriaTemplate(rootElement: Element, t function matchesNode(node: AriaNode | string, template: AriaTemplateNode, isDeepEqual: boolean): boolean { if (typeof node === 'string' && template.kind === 'text') - return matchesTextNode(node, template); + return matchesTextValue(node, template.text); if (node === null || typeof node !== 'object' || template.kind !== 'role') return false; @@ -395,9 +431,9 @@ function matchesNode(node: AriaNode | string, template: AriaTemplateNode, isDeep return false; if (template.selected !== undefined && template.selected !== node.selected) return false; - if (!matchesName(node.name, template)) + if (!matchesStringOrRegex(node.name, template.name)) return false; - if (!matchesText(node.props.url, template.props?.url)) + if (!matchesTextValue(node.props.url, template.props?.url)) return false; // Proceed based on the container mode. @@ -464,7 +500,7 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, publicOptions: AriaTr const lines: string[] = []; const includeText = options.renderStringsAsRegex ? textContributesInfo : () => true; const renderString = options.renderStringsAsRegex ? convertToBestGuessRegex : (str: string) => str; - const visit = (ariaNode: AriaNode | string, parentAriaNode: AriaNode | null, indent: string) => { + const visit = (ariaNode: AriaNode | string, parentAriaNode: AriaNode | null, indent: string, renderCursorPointer: boolean) => { if (typeof ariaNode === 'string') { if (parentAriaNode && !includeText(parentAriaNode, ariaNode)) return; @@ -502,10 +538,13 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, publicOptions: AriaTr if (ariaNode.selected === true) key += ` [selected]`; + let inCursorPointer = false; if (ariaNode.ref) { key += ` [ref=${ariaNode.ref}]`; - if (options.renderCursorPointer && hasPointerCursor(ariaNode)) + if (renderCursorPointer && hasPointerCursor(ariaNode)) { + inCursorPointer = true; key += ' [cursor=pointer]'; + } } const escapedKey = indent + '- ' + yamlEscapeKeyIfNeeded(key); @@ -523,7 +562,7 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, publicOptions: AriaTr for (const [name, value] of Object.entries(ariaNode.props)) lines.push(indent + ' - /' + name + ': ' + yamlEscapeValueIfNeeded(value)); for (const child of ariaNode.children || []) - visit(child, ariaNode, indent + ' '); + visit(child, ariaNode, indent + ' ', renderCursorPointer && !inCursorPointer); } }; @@ -531,9 +570,9 @@ export function renderAriaTree(ariaSnapshot: AriaSnapshot, publicOptions: AriaTr if (ariaNode.role === 'fragment') { // Render fragment. for (const child of ariaNode.children || []) - visit(child, ariaNode, ''); + visit(child, ariaNode, '', !!options.renderCursorPointer); } else { - visit(ariaNode, null, ''); + visit(ariaNode, null, '', !!options.renderCursorPointer); } return lines.join('\n'); } diff --git a/packages/injected/src/bindingsController.ts b/packages/injected/src/bindingsController.ts index ec0ff89eee7cd..a9a8d42c7bf09 100644 --- a/packages/injected/src/bindingsController.ts +++ b/packages/injected/src/bindingsController.ts @@ -32,12 +32,10 @@ type BindingData = { }; export class BindingsController { - // eslint-disable-next-line no-restricted-globals private _global: typeof globalThis; private _globalBindingName: string; private _bindings = new Map(); - // eslint-disable-next-line no-restricted-globals constructor(global: typeof globalThis, globalBindingName: string) { this._global = global; this._globalBindingName = globalBindingName; diff --git a/packages/injected/src/clock.ts b/packages/injected/src/clock.ts index 673a2da6facf1..753996a36e9ff 100644 --- a/packages/injected/src/clock.ts +++ b/packages/injected/src/clock.ts @@ -430,7 +430,6 @@ function mirrorDateProperties(target: any, source: Builtins['Date']): Builtins[' } function createDate(clock: ClockController, NativeDate: Builtins['Date']): Builtins['Date'] { - // eslint-disable-next-line no-restricted-globals function ClockDate(this: typeof ClockDate, year: number, month: number, date: number, hour: number, minute: number, second: number, ms: number): Date | string { // the Date constructor called as a function, ref Ecma-262 Edition 5.1, section 15.9.2. // This remains so in the 10th edition of 2019 as well. @@ -497,7 +496,6 @@ function createIntl(clock: ClockController, NativeIntl: Builtins['Intl']): Built ClockIntl.DateTimeFormat = function(...args: any[]) { const realFormatter = new NativeIntl.DateTimeFormat(...args); - // eslint-disable-next-line no-restricted-globals const formatter: Intl.DateTimeFormat = { formatRange: realFormatter.formatRange.bind(realFormatter), formatRangeToParts: realFormatter.formatRangeToParts.bind(realFormatter), diff --git a/packages/injected/src/consoleApi.ts b/packages/injected/src/consoleApi.ts index 2f2da420b280f..2406c2e894061 100644 --- a/packages/injected/src/consoleApi.ts +++ b/packages/injected/src/consoleApi.ts @@ -21,6 +21,7 @@ import { escapeForTextSelector } from '@isomorphic/stringUtils'; import type { InjectedScript } from './injectedScript'; import type { Language } from '@isomorphic/locatorGenerators'; import type { ByRoleOptions } from '@isomorphic/locatorUtils'; +import type { AriaTreeOptions } from './ariaSnapshot'; const selectorSymbol = Symbol('selector'); @@ -91,8 +92,8 @@ export class ConsoleAPI { inspect: (selector: string) => this._inspect(selector), selector: (element: Element) => this._selector(element), generateLocator: (element: Element, language?: Language) => this._generateLocator(element, language), - ariaSnapshot: (element?: Element) => { - return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, { mode: 'expect' }); + ariaSnapshot: (element?: Element, options?: AriaTreeOptions) => { + return this._injectedScript.ariaSnapshot(element || this._injectedScript.document.body, options || { mode: 'expect' }); }, resume: () => this._resume(), ...new Locator(this._injectedScript, ''), diff --git a/packages/injected/src/domUtils.ts b/packages/injected/src/domUtils.ts index e11d6476949cb..eb2b6a25f5a0f 100644 --- a/packages/injected/src/domUtils.ts +++ b/packages/injected/src/domUtils.ts @@ -76,7 +76,12 @@ export function closestCrossShadow(element: Element | undefined, css: string, sc } export function getElementComputedStyle(element: Element, pseudo?: string): CSSStyleDeclaration | undefined { - return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : undefined; + const cache = pseudo === '::before' ? cacheStyleBefore : pseudo === '::after' ? cacheStyleAfter : cacheStyle; + if (cache && cache.has(element)) + return cache.get(element); + const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : undefined; + cache?.set(element, style); + return style; } export function isElementStyleVisibilityVisible(element: Element, style?: CSSStyleDeclaration): boolean { @@ -105,33 +110,34 @@ export function isElementStyleVisibilityVisible(element: Element, style?: CSSSty export type Box = { visible: boolean; + inline: boolean; rect?: DOMRect; style?: CSSStyleDeclaration; }; -export function box(element: Element): Box { +export function computeBox(element: Element): Box { // Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises. const style = getElementComputedStyle(element); if (!style) - return { visible: true }; + return { visible: true, inline: false }; if (style.display === 'contents') { // display:contents is not rendered itself, but its child nodes are. for (let child = element.firstChild; child; child = child.nextSibling) { if (child.nodeType === 1 /* Node.ELEMENT_NODE */ && isElementVisible(child as Element)) - return { visible: true, style }; + return { visible: true, inline: false, style }; if (child.nodeType === 3 /* Node.TEXT_NODE */ && isVisibleTextNode(child as Text)) - return { visible: true, style }; + return { visible: true, inline: true, style }; } - return { visible: false, style }; + return { visible: false, inline: false, style }; } if (!isElementStyleVisibilityVisible(element, style)) - return { style, visible: false }; + return { style, visible: false, inline: false }; const rect = element.getBoundingClientRect(); - return { rect, style, visible: rect.width > 0 && rect.height > 0 }; + return { rect, style, visible: rect.width > 0 && rect.height > 0, inline: style.display === 'inline' }; } export function isElementVisible(element: Element): boolean { - return box(element).visible; + return computeBox(element).visible; } export function isVisibleTextNode(node: Text) { @@ -143,6 +149,9 @@ export function isVisibleTextNode(node: Text) { } export function elementSafeTagName(element: Element) { + const tagName = element.tagName; + if (typeof tagName === 'string') // Fast path. + return tagName.toUpperCase(); // Named inputs, e.g. , will be exposed as fields on the parent
// and override its properties. if (element instanceof HTMLFormElement) @@ -150,3 +159,23 @@ export function elementSafeTagName(element: Element) { // Elements from the svg namespace do not have uppercase tagName right away. return element.tagName.toUpperCase(); } + +let cacheStyle: Map | undefined; +let cacheStyleBefore: Map | undefined; +let cacheStyleAfter: Map | undefined; +let cachesCounter = 0; + +export function beginDOMCaches() { + ++cachesCounter; + cacheStyle ??= new Map(); + cacheStyleBefore ??= new Map(); + cacheStyleAfter ??= new Map(); +} + +export function endDOMCaches() { + if (!--cachesCounter) { + cacheStyle = undefined; + cacheStyleBefore = undefined; + cacheStyleAfter = undefined; + } +} diff --git a/packages/injected/src/highlight.css b/packages/injected/src/highlight.css index 3acfd3fb1c776..c82132b0ed70a 100644 --- a/packages/injected/src/highlight.css +++ b/packages/injected/src/highlight.css @@ -51,11 +51,6 @@ x-pw-tooltip-line { cursor: pointer; } -x-pw-tooltip-line.selectable:hover { - background-color: hsl(0, 0%, 95%); - overflow: hidden; -} - x-pw-tooltip-footer { display: flex; max-width: 600px; @@ -72,12 +67,15 @@ x-pw-dialog { display: flex; flex-direction: column; position: absolute; - width: 400px; - height: 150px; z-index: 10; font-size: 13px; } +x-pw-dialog:not(.autosize) { + width: 400px; + height: 150px; +} + x-pw-dialog-body { display: flex; flex-direction: column; @@ -207,6 +205,11 @@ x-pw-tool-item.record > x-div { clip-path: url(#icon-circle-large-filled); } +x-pw-tool-item.record.toggled > x-div { + /* codicon: stop-circle */ + clip-path: url(#icon-stop-circle); +} + x-pw-tool-item.pick-locator > x-div { /* codicon: inspect */ clip-path: url(#icon-inspect); @@ -315,3 +318,25 @@ x-locator-editor.does-not-match { width: 100% !important; height: 100% !important; } + +x-pw-action-list { + flex: auto; + display: flex; + flex-direction: column; + user-select: none; +} + +x-pw-action-item { + padding: 6px 10px; + cursor: pointer; + overflow: hidden; +} + +x-pw-action-item:hover { + background-color: hsl(0, 0%, 95%); +} + +x-pw-action-item:last-child { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; +} diff --git a/packages/injected/src/highlight.ts b/packages/injected/src/highlight.ts index 3a442a51b5f36..3df5633a19eb1 100644 --- a/packages/injected/src/highlight.ts +++ b/packages/injected/src/highlight.ts @@ -65,12 +65,6 @@ export class Highlight { this._glassPaneElement.style.pointerEvents = 'none'; this._glassPaneElement.style.display = 'flex'; this._glassPaneElement.style.backgroundColor = 'transparent'; - for (const eventName of ['click', 'auxclick', 'dragstart', 'input', 'keydown', 'keyup', 'pointerdown', 'pointerup', 'mousedown', 'mouseup', 'mouseleave', 'focus', 'scroll']) { - this._glassPaneElement.addEventListener(eventName, e => { - e.stopPropagation(); - e.stopImmediatePropagation(); - }); - } this._actionPointElement = document.createElement('x-pw-action-point'); this._actionPointElement.setAttribute('hidden', 'true'); this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? 'open' : 'closed' }); @@ -204,20 +198,38 @@ export class Highlight { return this._renderedEntries[0]?.box; } + firstTooltipBox(): DOMRect | undefined { + const entry = this._renderedEntries[0]; + if (!entry || !entry.tooltipElement || entry.tooltipLeft === undefined || entry.tooltipTop === undefined) + return; + return { + x: entry.tooltipLeft, + y: entry.tooltipTop, + left: entry.tooltipLeft, + top: entry.tooltipTop, + width: entry.tooltipElement.offsetWidth, + height: entry.tooltipElement.offsetHeight, + bottom: entry.tooltipTop + entry.tooltipElement.offsetHeight, + right: entry.tooltipLeft + entry.tooltipElement.offsetWidth, + toJSON: () => {}, + }; + } + + // Note: there is a copy of this method in dialog.tsx. Please fix bugs in both places. tooltipPosition(box: DOMRect, tooltipElement: HTMLElement) { const tooltipWidth = tooltipElement.offsetWidth; const tooltipHeight = tooltipElement.offsetHeight; const totalWidth = this._glassPaneElement.offsetWidth; const totalHeight = this._glassPaneElement.offsetHeight; - let anchorLeft = box.left; + let anchorLeft = Math.max(5, box.left); if (anchorLeft + tooltipWidth > totalWidth - 5) anchorLeft = totalWidth - tooltipWidth - 5; - let anchorTop = box.bottom + 5; + let anchorTop = Math.max(0, box.bottom) + 5; if (anchorTop + tooltipHeight > totalHeight - 5) { // If can't fit below, either position above... - if (box.top > tooltipHeight + 5) { - anchorTop = box.top - tooltipHeight - 5; + if (Math.max(0, box.top) > tooltipHeight + 5) { + anchorTop = Math.max(0, box.top) - tooltipHeight - 5; } else { // Or on top in case of large element anchorTop = totalHeight - 5 - tooltipHeight; @@ -251,4 +263,16 @@ export class Highlight { appendChild(element: Element) { this._glassPaneShadow.appendChild(element); } + + onGlassPaneClick(handler: (event: MouseEvent) => void) { + this._glassPaneElement.style.pointerEvents = 'auto'; + this._glassPaneElement.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'; + this._glassPaneElement.addEventListener('click', handler); + } + + offGlassPaneClick(handler: (event: MouseEvent) => void) { + this._glassPaneElement.style.pointerEvents = 'none'; + this._glassPaneElement.style.backgroundColor = 'transparent'; + this._glassPaneElement.removeEventListener('click', handler); + } } diff --git a/packages/injected/src/injectedScript.ts b/packages/injected/src/injectedScript.ts index 29446799cb80c..76969f4ed676e 100644 --- a/packages/injected/src/injectedScript.ts +++ b/packages/injected/src/injectedScript.ts @@ -20,12 +20,12 @@ import { parseAttributeSelector, parseSelector, stringifySelector, visitAllSelec import { cacheNormalizedWhitespaces, normalizeWhiteSpace, trimStringWithEllipsis } from '@isomorphic/stringUtils'; import { generateAriaTree, getAllElementsMatchingExpectAriaTemplate, matchesExpectAriaTemplate, renderAriaTree } from './ariaSnapshot'; -import { enclosingShadowRootOrDocument, isElementVisible, isInsideScope, parentElementOrShadowHost, setGlobalOptions } from './domUtils'; +import { beginDOMCaches, enclosingShadowRootOrDocument, endDOMCaches, isElementVisible, isInsideScope, parentElementOrShadowHost, setGlobalOptions } from './domUtils'; import { Highlight } from './highlight'; import { kLayoutSelectorNames, layoutSelectorScore } from './layoutSelectorUtils'; import { createReactEngine } from './reactSelectorEngine'; import { createRoleEngine } from './roleSelectorEngine'; -import { getAriaDisabled, getAriaRole, getCheckedAllowMixed, getCheckedWithoutMixed, getElementAccessibleDescription, getElementAccessibleErrorMessage, getElementAccessibleName, getReadonly } from './roleUtils'; +import { beginAriaCaches, endAriaCaches, getAriaDisabled, getAriaRole, getCheckedAllowMixed, getCheckedWithoutMixed, getElementAccessibleDescription, getElementAccessibleErrorMessage, getElementAccessibleName, getReadonly } from './roleUtils'; import { SelectorEvaluatorImpl, sortInDOMOrder } from './selectorEvaluator'; import { generateSelector } from './selectorGenerator'; import { elementMatchesText, elementText, getElementLabels } from './selectorUtils'; @@ -51,7 +51,7 @@ export type FrameExpectParams = Omit; -export type ElementStateQueryResult = { matches: boolean, received?: string | 'error:notconnected' }; +export type ElementStateQueryResult = { matches: boolean, received?: string | 'error:notconnected', isRadio?: boolean }; export type HitTargetInterceptionResult = { stop: () => 'done' | { hitTargetDescription: string }; @@ -72,6 +72,7 @@ export type InjectedScriptOptions = { testIdAttributeName: string; stableRafCount: number; browserName: string; + isUtilityWorld?: boolean; customEngines: { name: string, source: string }[]; }; @@ -80,6 +81,7 @@ export class InjectedScript { readonly _evaluator: SelectorEvaluatorImpl; private _stableRafCount: number; private _browserName: string; + private _isUtilityWorld: boolean; readonly onGlobalListenersRemoved: Set<() => void>; private _hitTargetInterceptor: undefined | ((event: MouseEvent | PointerEvent | TouchEvent) => void); private _highlight: Highlight | undefined; @@ -87,7 +89,6 @@ export class InjectedScript { private _sdkLanguage: Language; private _testIdAttributeNameForStrictErrorAndConsoleCodegen: string = 'data-testid'; private _markedElements?: { callId: string, elements: Set }; - // eslint-disable-next-line no-restricted-globals readonly window: Window & typeof globalThis; readonly document: Document; readonly consoleApi: ConsoleAPI; @@ -120,7 +121,6 @@ export class InjectedScript { private _mouseHitTargetInterceptorEvents: Set; private _allHitTargetInterceptorEvents: Set; - // eslint-disable-next-line no-restricted-globals constructor(window: Window & typeof globalThis, options: InjectedScriptOptions) { this.window = window; this.document = window.document; @@ -235,6 +235,7 @@ export class InjectedScript { this._stableRafCount = options.stableRafCount; this._browserName = options.browserName; + this._isUtilityWorld = !!options.isUtilityWorld; setGlobalOptions({ browserNameForWorkarounds: options.browserName }); this._setupGlobalListenersRemovalDetection(); @@ -737,9 +738,11 @@ export class InjectedScript { const checked = getCheckedWithoutMixed(element); if (checked === 'error') throw this.createStacklessError('Not a checkbox or radio button'); + const isRadio = element.nodeName === 'INPUT' && (element as HTMLInputElement).type === 'radio'; return { matches: need === checked, received: checked ? 'checked' : 'unchecked', + isRadio, }; } @@ -1210,14 +1213,25 @@ export class InjectedScript { } strictModeViolationError(selector: ParsedSelector, matches: Element[]): Error { - const infos = matches.slice(0, 10).map(m => ({ - preview: this.previewNode(m), - selector: this.generateSelectorSimple(m), - })); - const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`); - if (infos.length < matches.length) - lines.push('\n ...'); - return this.createStacklessError(`strict mode violation: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join('')}\n`); + this._evaluator.begin(); + beginAriaCaches(); + beginDOMCaches(); + try { + // Firefox is slow to access DOM bindings in the utility world, making it very expensive to generate a lot of selectors. + const maxElements = this._isUtilityWorld && this._browserName === 'firefox' ? 2 : 10; + const infos = matches.slice(0, maxElements).map(m => ({ + preview: this.previewNode(m), + selector: this.generateSelectorSimple(m), + })); + const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`); + if (infos.length < matches.length) + lines.push('\n ...'); + return this.createStacklessError(`strict mode violation: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join('')}\n`); + } finally { + endDOMCaches(); + endAriaCaches(); + this._evaluator.end(); + } } createStacklessError(message: string): Error { diff --git a/packages/injected/src/reactSelectorEngine.ts b/packages/injected/src/reactSelectorEngine.ts index a9185057c7cdc..eb27da6947b7e 100644 --- a/packages/injected/src/reactSelectorEngine.ts +++ b/packages/injected/src/reactSelectorEngine.ts @@ -108,11 +108,7 @@ function getChildren(reactElement: ReactVNode): ReactVNode[] { } function getProps(reactElement: ReactVNode) { - const props = - // React 16+ - reactElement.memoizedProps || - // React 15 - reactElement._currentElement?.props; + const props = /* React 16+ */ reactElement.memoizedProps || /* React 15 */ reactElement._currentElement?.props; if (!props || typeof props === 'string') return props; const result = { ...props }; @@ -130,12 +126,8 @@ function buildComponentsTree(reactElement: ReactVNode): ComponentNode { props: getProps(reactElement), }; - const rootElement = - // React 16+ - // @see https://github.com/baruchvlz/resq/blob/5c15a5e04d3f7174087248f5a158c3d6dcc1ec72/src/utils.js#L29 - reactElement.stateNode || - // React 15 - reactElement._hostNode || reactElement._renderedComponent?._hostNode; + // @see https://github.com/baruchvlz/resq/blob/5c15a5e04d3f7174087248f5a158c3d6dcc1ec72/src/utils.js#L29 + const rootElement = /* React 16+ */ reactElement.stateNode || /* React 15 */ reactElement._hostNode || /* React 15 */ reactElement._renderedComponent?._hostNode; if (rootElement instanceof Element) { treeNode.rootElements.push(rootElement); } else { diff --git a/packages/injected/src/recorder/clipPaths.ts b/packages/injected/src/recorder/clipPaths.ts index 1ac490843d292..45b55a7df2933 100644 --- a/packages/injected/src/recorder/clipPaths.ts +++ b/packages/injected/src/recorder/clipPaths.ts @@ -27,5 +27,5 @@ import type { SvgJson } from './recorder'; // eslint-disable-next-line key-spacing, object-curly-spacing, comma-spacing, quotes -const svgJson: SvgJson = {"tagName":"svg","children":[{"tagName":"defs","children":[{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-gripper"},"children":[{"tagName":"path","attrs":{"d":"M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-circle-large-filled"},"children":[{"tagName":"path","attrs":{"d":"M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-inspect"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-whole-word"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M0 11H1V13H15V11H16V14H15H1H0V11Z"}},{"tagName":"path","attrs":{"d":"M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z"}},{"tagName":"path","attrs":{"d":"M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-eye"},"children":[{"tagName":"path","attrs":{"d":"M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-symbol-constant"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M4 6h8v1H4V6zm8 3H4v1h8V9z"}},{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-check"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-close"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-pass"},"children":[{"tagName":"path","attrs":{"d":"M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z"}},{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-gist"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z"}}]}]}]}; +const svgJson: SvgJson = {"tagName":"svg","children":[{"tagName":"defs","children":[{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-gripper"},"children":[{"tagName":"path","attrs":{"d":"M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-circle-large-filled"},"children":[{"tagName":"path","attrs":{"d":"M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-stop-circle"},"children":[{"tagName":"path","attrs":{"d":"M6 6h4v4H6z"}},{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-inspect"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-whole-word"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M0 11H1V13H15V11H16V14H15H1H0V11Z"}},{"tagName":"path","attrs":{"d":"M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z"}},{"tagName":"path","attrs":{"d":"M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-eye"},"children":[{"tagName":"path","attrs":{"d":"M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-symbol-constant"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M4 6h8v1H4V6zm8 3H4v1h8V9z"}},{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-check"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-close"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-pass"},"children":[{"tagName":"path","attrs":{"d":"M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z"}},{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z"}}]},{"tagName":"clipPath","attrs":{"width":"16","height":"16","viewBox":"0 0 16 16","fill":"currentColor","id":"icon-gist"},"children":[{"tagName":"path","attrs":{"fill-rule":"evenodd","clip-rule":"evenodd","d":"M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z"}}]}]}]}; export default svgJson; diff --git a/packages/injected/src/recorder/icons/stop-circle.svg b/packages/injected/src/recorder/icons/stop-circle.svg new file mode 100644 index 0000000000000..4f39984fa23d2 --- /dev/null +++ b/packages/injected/src/recorder/icons/stop-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/injected/src/recorder/recorder.ts b/packages/injected/src/recorder/recorder.ts index 3be1809fdb4a3..60162b760a917 100644 --- a/packages/injected/src/recorder/recorder.ts +++ b/packages/injected/src/recorder/recorder.ts @@ -194,10 +194,12 @@ class RecordActionTool implements RecorderTool { private _expectProgrammaticKeyUp = false; private _pendingClickAction: { action: actions.ClickAction, timeout: number } | undefined; private _observer: MutationObserver | null = null; + private _dialog: Dialog; constructor(recorder: Recorder) { this._recorder = recorder; this._performingActions = new Set(); + this._dialog = new Dialog(recorder); } cursor() { @@ -229,16 +231,23 @@ class RecordActionTool implements RecorderTool { this._hoveredElement = null; this._activeModel = null; this._expectProgrammaticKeyUp = false; + this._dialog.close(); } onClick(event: MouseEvent) { + if (this._dialog.isShowing()) { + if (event.button === 2 && event.type === 'auxclick') { + // Note: in some browsers, e.g. firefox mac, + // auxclick event arrives after contextmenu and should be consumed. + consumeEvent(event); + } + return; + } + // in webkit, sliding a range element may trigger a click event with a different target if the mouse is released outside the element bounding box. // So we check the hovered element instead, and if it is a range input, we skip click handling if (isRangeInput(this._hoveredElement)) return; - // Right clicks are handled by 'contextmenu' event if its auxclick - if (event.button === 2 && event.type === 'auxclick') - return; if (this._shouldIgnoreMouseEvent(event)) return; if (this._actionInProgress(event)) @@ -246,6 +255,11 @@ class RecordActionTool implements RecorderTool { if (this._consumedDueToNoModel(event, this._hoveredModel)) return; + if (event.button === 2 && event.type === 'auxclick') { + this._showActionListDialog(this._hoveredModel!, event); + return; + } + const checkbox = asCheckbox(this._recorder.deepEventTarget(event)); if (checkbox && event.detail === 1) { // Interestingly, inputElement.checked is reversed inside this event handler. @@ -277,6 +291,8 @@ class RecordActionTool implements RecorderTool { } onDblClick(event: MouseEvent) { + if (this._dialog.isShowing()) + return; if (isRangeInput(this._hoveredElement)) return; if (this._shouldIgnoreMouseEvent(event)) @@ -313,40 +329,40 @@ class RecordActionTool implements RecorderTool { } onContextMenu(event: MouseEvent) { - // the 'contextmenu' event is triggered by a right-click or equivalent action, - // and it prevents the click event from firing for that action, so we always - // convert 'contextmenu' into a right-click. + if (this._dialog.isShowing()) { + // Note: in some browsers, e.g. chromium windows, + // contextmenu event arrives after auxclick and should be consumed. + consumeEvent(event); + return; + } if (this._shouldIgnoreMouseEvent(event)) return; if (this._actionInProgress(event)) return; if (this._consumedDueToNoModel(event, this._hoveredModel)) return; - - this._performAction({ - name: 'click', - selector: this._hoveredModel!.selector, - position: positionForEvent(event), - signals: [], - button: 'right', - modifiers: 0, - clickCount: 0 - }); + this._showActionListDialog(this._hoveredModel!, event); } onPointerDown(event: PointerEvent) { + if (this._dialog.isShowing()) + return; if (this._shouldIgnoreMouseEvent(event)) return; this._consumeWhenAboutToPerform(event); } onPointerUp(event: PointerEvent) { + if (this._dialog.isShowing()) + return; if (this._shouldIgnoreMouseEvent(event)) return; this._consumeWhenAboutToPerform(event); } onMouseDown(event: MouseEvent) { + if (this._dialog.isShowing()) + return; if (this._shouldIgnoreMouseEvent(event)) return; this._consumeWhenAboutToPerform(event); @@ -354,12 +370,16 @@ class RecordActionTool implements RecorderTool { } onMouseUp(event: MouseEvent) { + if (this._dialog.isShowing()) + return; if (this._shouldIgnoreMouseEvent(event)) return; this._consumeWhenAboutToPerform(event); } onMouseMove(event: MouseEvent) { + if (this._dialog.isShowing()) + return; const target = this._recorder.deepEventTarget(event); if (this._hoveredElement === target) return; @@ -368,6 +388,8 @@ class RecordActionTool implements RecorderTool { } onMouseLeave(event: MouseEvent) { + if (this._dialog.isShowing()) + return; const window = this._recorder.injectedScript.window; // Leaving iframe. if (window.top !== window && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) { @@ -377,10 +399,14 @@ class RecordActionTool implements RecorderTool { } onFocus(event: Event) { + if (this._dialog.isShowing()) + return; this._onFocus(true); } onInput(event: Event) { + if (this._dialog.isShowing()) + return; const target = this._recorder.deepEventTarget(event); if (target.nodeName === 'INPUT' && (target as HTMLInputElement).type.toLowerCase() === 'file') { @@ -433,6 +459,8 @@ class RecordActionTool implements RecorderTool { } onKeyDown(event: KeyboardEvent) { + if (this._dialog.isShowing()) + return; if (!this._shouldGenerateKeyPressFor(event)) return; if (this._actionInProgress(event)) { @@ -464,6 +492,8 @@ class RecordActionTool implements RecorderTool { } onKeyUp(event: KeyboardEvent) { + if (this._dialog.isShowing()) + return; if (!this._shouldGenerateKeyPressFor(event)) return; @@ -476,9 +506,91 @@ class RecordActionTool implements RecorderTool { } onScroll(event: Event) { + if (this._dialog.isShowing()) + return; this._resetHoveredModel(); } + private _showActionListDialog(model: HighlightModelWithSelector, event: MouseEvent) { + consumeEvent(event); + const actionPosition = positionForEvent(event); + const actions: { title: string, cb: () => void }[] = [ + { + title: 'Click', + cb: () => this._performAction({ + name: 'click', + selector: model.selector, + position: actionPosition, + signals: [], + button: 'left', + modifiers: 0, + clickCount: 0, + }), + }, + { + title: 'Right click', + cb: () => this._performAction({ + name: 'click', + selector: model.selector, + position: actionPosition, + signals: [], + button: 'right', + modifiers: 0, + clickCount: 0, + }), + }, + { + title: 'Double click', + cb: () => this._performAction({ + name: 'click', + selector: model.selector, + position: actionPosition, + signals: [], + button: 'left', + modifiers: 0, + clickCount: 2, + }), + }, + { + title: 'Hover', + cb: () => this._performAction({ + name: 'hover', + selector: model.selector, + position: actionPosition, + signals: [], + }), + }, + { + title: 'Pick locator', + cb: () => this._recorder.elementPicked(model.selector, model), + }, + ]; + + const listElement = this._recorder.document.createElement('x-pw-action-list'); + listElement.setAttribute('role', 'list'); + listElement.setAttribute('aria-label', 'Choose action'); + for (const action of actions) { + const actionElement = this._recorder.document.createElement('x-pw-action-item'); + actionElement.setAttribute('role', 'listitem'); + actionElement.textContent = action.title; + actionElement.setAttribute('aria-label', action.title); + actionElement.addEventListener('click', () => { + this._dialog.close(); + action.cb(); + }); + listElement.appendChild(actionElement); + } + + const dialogElement = this._dialog.show({ + label: 'Choose action', + body: listElement, + autosize: true, + }); + const anchorBox = this._recorder.highlight.firstTooltipBox() || model.elements[0].getBoundingClientRect(); + const dialogPosition = this._recorder.highlight.tooltipPosition(anchorBox, dialogElement); + this._dialog.moveTo(dialogPosition.anchorTop, dialogPosition.anchorLeft); + } + private _resetHoveredModel() { this._hoveredModel = null; this._hoveredElement = null; @@ -516,7 +628,7 @@ class RecordActionTool implements RecorderTool { for (const action of this._performingActions) { if (isKeyEvent && action.name === 'press' && event.key === action.key) return true; - if (isMouseOrPointerEvent && (action.name === 'click' || action.name === 'check' || action.name === 'uncheck')) + if (isMouseOrPointerEvent && (action.name === 'click' || action.name === 'hover' || action.name === 'check' || action.name === 'uncheck')) return true; } @@ -1166,7 +1278,9 @@ class Overlay { } setUIState(state: UIState) { - this._recordToggle.classList.toggle('toggled', state.mode === 'recording' || state.mode === 'assertingText' || state.mode === 'assertingVisibility' || state.mode === 'assertingValue' || state.mode === 'assertingSnapshot' || state.mode === 'recording-inspecting'); + const isRecording = state.mode === 'recording' || state.mode === 'assertingText' || state.mode === 'assertingVisibility' || state.mode === 'assertingValue' || state.mode === 'assertingSnapshot' || state.mode === 'recording-inspecting'; + this._recordToggle.classList.toggle('toggled', isRecording); + this._recordToggle.title = isRecording ? 'Stop Recording' : 'Start Recording'; this._pickLocatorToggle.classList.toggle('toggled', state.mode === 'inspecting' || state.mode === 'recording-inspecting'); this._assertVisibilityToggle.classList.toggle('toggled', state.mode === 'assertingVisibility'); this._assertVisibilityToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting'); @@ -1430,8 +1544,10 @@ export class Recorder { private _onContextMenu(event: MouseEvent) { if (!event.isTrusted) return; - if (this._ignoreOverlayEvent(event)) - return; + // Note: in chromium windows, context menu event always includes overlay, + // even for right-click on the page. Therefore, we do not check the overlay + // as in any other events. This is fine, because we do not need context menu + // to work in our overlay anyway. this._currentTool.onContextMenu?.(event); } @@ -1615,6 +1731,7 @@ class Dialog { private _recorder: Recorder; private _dialogElement: HTMLElement | null = null; private _keyboardListener: ((event: KeyboardEvent) => void) | undefined; + private _onGlassPaneClickHandler: ((event: MouseEvent) => void) | undefined; constructor(recorder: Recorder) { this._recorder = recorder; @@ -1627,14 +1744,15 @@ class Dialog { show(options: { label: string; body: Element; - onCommit: () => void; + onCommit?: () => void; onCancel?: () => void; + autosize?: boolean; }) { const acceptButton = this._recorder.document.createElement('x-pw-tool-item'); acceptButton.title = 'Accept'; acceptButton.classList.add('accept'); acceptButton.appendChild(this._recorder.document.createElement('x-div')); - acceptButton.addEventListener('click', () => options.onCommit()); + acceptButton.addEventListener('click', () => options.onCommit?.()); const cancelButton = this._recorder.document.createElement('x-pw-tool-item'); cancelButton.title = 'Close'; @@ -1646,26 +1764,34 @@ class Dialog { }); this._dialogElement = this._recorder.document.createElement('x-pw-dialog'); + if (options.autosize) + this._dialogElement.classList.add('autosize'); + this._keyboardListener = (event: KeyboardEvent) => { if (event.key === 'Escape') { this.close(); options.onCancel?.(); return; } - if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) { + if (options.onCommit && event.key === 'Enter' && (event.ctrlKey || event.metaKey)) { if (this._dialogElement) options.onCommit(); return; } }; - this._recorder.document.addEventListener('keydown', this._keyboardListener, true); + this._onGlassPaneClickHandler = (event: MouseEvent) => { + this.close(); + options.onCancel?.(); + }; + const toolbarElement = this._recorder.document.createElement('x-pw-tools-list'); const labelElement = this._recorder.document.createElement('label'); labelElement.textContent = options.label; toolbarElement.appendChild(labelElement); toolbarElement.appendChild(this._recorder.document.createElement('x-spacer')); - toolbarElement.appendChild(acceptButton); + if (options.onCommit) + toolbarElement.appendChild(acceptButton); toolbarElement.appendChild(cancelButton); this._dialogElement.appendChild(toolbarElement); @@ -1673,6 +1799,8 @@ class Dialog { bodyElement.appendChild(options.body); this._dialogElement.appendChild(bodyElement); this._recorder.highlight.appendChild(this._dialogElement); + this._recorder.highlight.onGlassPaneClick(this._onGlassPaneClickHandler); + this._recorder.document.addEventListener('keydown', this._keyboardListener, true); return this._dialogElement; } @@ -1687,6 +1815,7 @@ class Dialog { if (!this._dialogElement) return; this._dialogElement.remove(); + this._recorder.highlight.offGlassPaneClick(this._onGlassPaneClickHandler!); this._recorder.document.removeEventListener('keydown', this._keyboardListener!); this._dialogElement = null; } diff --git a/packages/injected/src/roleUtils.ts b/packages/injected/src/roleUtils.ts index 3f0b5c14d601c..582c266f49a74 100644 --- a/packages/injected/src/roleUtils.ts +++ b/packages/injected/src/roleUtils.ts @@ -16,7 +16,7 @@ import * as css from '@isomorphic/cssTokenizer'; -import { closestCrossShadow, elementSafeTagName, enclosingShadowRootOrDocument, getElementComputedStyle, isElementStyleVisibilityVisible, isVisibleTextNode, parentElementOrShadowHost } from './domUtils'; +import { beginDOMCaches, closestCrossShadow, elementSafeTagName, enclosingShadowRootOrDocument, endDOMCaches, getElementComputedStyle, isElementStyleVisibilityVisible, isVisibleTextNode, parentElementOrShadowHost } from './domUtils'; import type { AriaRole } from '@isomorphic/ariaSnapshot'; @@ -284,10 +284,10 @@ export function isElementHiddenForAria(element: Element): boolean { const isOptionInsideSelect = element.nodeName === 'OPTION' && !!element.closest('select'); if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style)) return true; - return belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element); + return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element); } -function belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element: Element): boolean { +function belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element: Element): boolean { let hidden = cacheIsHidden?.get(element); if (hidden === undefined) { hidden = false; @@ -298,17 +298,17 @@ function belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(element: Element): if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot) hidden = true; - // display:none and aria-hidden=true and inert are considered hidden for aria. + // display:none and aria-hidden=true are considered hidden for aria. if (!hidden) { const style = getElementComputedStyle(element); - hidden = !style || style.display === 'none' || getAriaBoolean(element.getAttribute('aria-hidden')) === true || element.getAttribute('inert') !== null; + hidden = !style || style.display === 'none' || getAriaBoolean(element.getAttribute('aria-hidden')) === true; } // Check recursively. if (!hidden) { const parent = parentElementOrShadowHost(element); if (parent) - hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlottedOrInert(parent); + hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent); } cacheIsHidden?.set(element, hidden); } @@ -367,9 +367,14 @@ export function getCSSContent(element: Element, pseudo?: '::before' | '::after') const style = getElementComputedStyle(element, pseudo); let content: string | undefined; - if (style && style.display !== 'none' && style.visibility !== 'hidden') { - // Note: all browsers ignore display:none and visibility:hidden pseudos. - content = parseCSSContentPropertyAsString(element, style.content, !!pseudo); + if (style) { + const contentValue = style.content; + if (contentValue && contentValue !== 'none' && contentValue !== 'normal') { + if (style.display !== 'none' && style.visibility !== 'hidden') { + // Note: all browsers ignore display:none and visibility:hidden pseudos. + content = parseCSSContentPropertyAsString(element, contentValue, !!pseudo); + } + } } if (pseudo && content !== undefined) { @@ -1150,6 +1155,7 @@ let cachePointerEvents: Map | undefined; let cachesCounter = 0; export function beginAriaCaches() { + beginDOMCaches(); ++cachesCounter; cacheAccessibleName ??= new Map(); cacheAccessibleNameHidden ??= new Map(); @@ -1176,6 +1182,7 @@ export function endAriaCaches() { cachePseudoContentAfter = undefined; cachePointerEvents = undefined; } + endDOMCaches(); } const inputTypeToRole: Record = { diff --git a/packages/injected/src/selectorGenerator.ts b/packages/injected/src/selectorGenerator.ts index a7dad6644fb0b..202472aa4f15f 100644 --- a/packages/injected/src/selectorGenerator.ts +++ b/packages/injected/src/selectorGenerator.ts @@ -16,7 +16,7 @@ import { escapeForAttributeSelector, escapeForTextSelector, escapeRegExp, quoteCSSAttributeValue } from '@isomorphic/stringUtils'; -import { closestCrossShadow, isElementVisible, isInsideScope, parentElementOrShadowHost } from './domUtils'; +import { beginDOMCaches, closestCrossShadow, endDOMCaches, isElementVisible, isInsideScope, parentElementOrShadowHost } from './domUtils'; import { beginAriaCaches, endAriaCaches, getAriaRole, getElementAccessibleName } from './roleUtils'; import { elementText, getElementLabels } from './selectorUtils'; @@ -78,6 +78,7 @@ export function generateSelector(injectedScript: InjectedScript, targetElement: injectedScript._evaluator.begin(); const cache: Cache = { allowText: new Map(), disallowText: new Map() }; beginAriaCaches(); + beginDOMCaches(); try { let selectors: string[] = []; if (options.forTextExpect) { @@ -135,17 +136,13 @@ export function generateSelector(injectedScript: InjectedScript, targetElement: elements: injectedScript.querySelectorAll(parsedSelector, options.root ?? targetElement.ownerDocument) }; } finally { + endDOMCaches(); endAriaCaches(); injectedScript._evaluator.end(); } } -function filterRegexTokens(textCandidates: SelectorToken[][]): SelectorToken[][] { - // Filter out regex-based selectors for better performance. - return textCandidates.filter(c => c[0].selector[0] !== '/'); -} - -type InternalOptions = GenerateSelectorOptions & { noText?: boolean, noCSSId?: boolean }; +type InternalOptions = GenerateSelectorOptions & { noText?: boolean, noCSSId?: boolean, isRecursive?: boolean }; function generateSelectorFor(cache: Cache, injectedScript: InjectedScript, targetElement: Element, options: InternalOptions): SelectorToken[] | null { if (options.root && !isInsideScope(options.root, targetElement)) @@ -156,77 +153,80 @@ function generateSelectorFor(cache: Cache, injectedScript: InjectedScript, targe if (targetElement.ownerDocument.documentElement === targetElement) return [{ engine: 'css', selector: 'html', score: 1 }]; - const calculate = (element: Element, allowText: boolean): SelectorToken[] | null => { - const allowNthMatch = element === targetElement; + let result: SelectorToken[] | null = null; + const updateResult = (candidate: SelectorToken[]) => { + if (!result || combineScores(candidate) < combineScores(result)) + result = candidate; + }; - let textCandidates = allowText ? buildTextCandidates(injectedScript, element, element === targetElement) : []; - if (element !== targetElement) { - // Do not use regex for parent elements (for performance). - textCandidates = filterRegexTokens(textCandidates); + const candidates: { candidate: SelectorToken[], isTextCandidate: boolean }[] = []; + if (!options.noText) { + for (const candidate of buildTextCandidates(injectedScript, targetElement, !options.isRecursive)) + candidates.push({ candidate, isTextCandidate: true }); + } + for (const token of buildNoTextCandidates(injectedScript, targetElement, options)) { + if (options.omitInternalEngines && token.engine.startsWith('internal:')) + continue; + candidates.push({ candidate: [token], isTextCandidate: false }); + } + candidates.sort((a, b) => combineScores(a.candidate) - combineScores(b.candidate)); + + for (const { candidate, isTextCandidate } of candidates) { + const elements = injectedScript.querySelectorAll(injectedScript.parseSelector(joinTokens(candidate)), options.root ?? targetElement.ownerDocument); + if (!elements.includes(targetElement)) { + // Somehow this selector just does not match the target. Oh well. + continue; } - const noTextCandidates = buildNoTextCandidates(injectedScript, element, options) - .filter(token => !options.omitInternalEngines || !token.engine.startsWith('internal:')) - .map(token => [token]); - - // First check all text and non-text candidates for the element. - let result = chooseFirstSelector(injectedScript, options.root ?? targetElement.ownerDocument, element, [...textCandidates, ...noTextCandidates], allowNthMatch); - - // Do not use regex for chained selectors (for performance). - textCandidates = filterRegexTokens(textCandidates); - - const checkWithText = (textCandidatesToUse: SelectorToken[][]) => { - // Use the deepest possible text selector - works pretty good and saves on compute time. - const allowParentText = allowText && !textCandidatesToUse.length; - - const candidates = [...textCandidatesToUse, ...noTextCandidates].filter(c => { - if (!result) - return true; - return combineScores(c) < combineScores(result); - }); - - // This is best theoretically possible candidate from the current parent. - // We use the fact that widening the scope to grand-parent makes any selector - // even less likely to match. - let bestPossibleInParent: SelectorToken[] | null = candidates[0]; - if (!bestPossibleInParent) - return; - - for (let parent = parentElementOrShadowHost(element); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) { - const parentTokens = calculateCached(parent, allowParentText); - if (!parentTokens) - continue; - // Even the best selector won't be too good - skip this parent. - if (result && combineScores([...parentTokens, ...bestPossibleInParent]) >= combineScores(result)) - continue; - // Update the best candidate that finds "element" in the "parent". - bestPossibleInParent = chooseFirstSelector(injectedScript, parent, element, candidates, allowNthMatch); - if (!bestPossibleInParent) - return; - const combined = [...parentTokens, ...bestPossibleInParent]; - if (!result || combineScores(combined) < combineScores(result)) - result = combined; - } - }; - checkWithText(textCandidates); - // Allow skipping text on the target element, and using text on one of the parents. - if (element === targetElement && textCandidates.length) - checkWithText([]); + if (elements.length === 1) { + // Perfect strict match. All other candidates are strictly worse because they are sorted by score. + updateResult(candidate); + break; + } - return result; - }; + const index = elements.indexOf(targetElement); + if (index > 5) { + // Do not generate locators with nth=6 or worse. + continue; + } + updateResult([...candidate, { engine: 'nth', selector: String(index), score: kNthScore }]); - const calculateCached = (element: Element, allowText: boolean): SelectorToken[] | null => { - const map = allowText ? cache.allowText : cache.disallowText; - let value = map.get(element); - if (value === undefined) { - value = calculate(element, allowText); - map.set(element, value); + if (options.isRecursive) { + // Limit nesting to two levels: parent >>> target. + continue; } - return value; - }; - return calculate(targetElement, !options.noText); + // Now try nested selectors: (best selector for parent) >>> (this candidate selector). + for (let parent = parentElementOrShadowHost(targetElement); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) { + const filtered = elements.filter(e => isInsideScope(parent, e) && e !== parent); + const newIndex = filtered.indexOf(targetElement); + if (filtered.length > 5 || newIndex === -1 || (newIndex === index && filtered.length > 1)) { + // Filtering to this parent is not an improvement - do not generate selector for parent. + continue; + } + + const inParent = filtered.length === 1 ? candidate : [...candidate, { engine: 'nth', selector: String(newIndex), score: kNthScore }]; + const idealSelectorForParent = { engine: '', selector: '', score: 1 }; // Best theoretical score we could achieve for the parent. + if (result && combineScores([idealSelectorForParent, ...inParent]) >= combineScores(result)) { + // It is impossible to generate a better scoring selector through this parent. + continue; + } + + // Do not allow text in parent selector when using text in the target selector. + const noText = !!options.noText || isTextCandidate; + const cacheMap = noText ? cache.disallowText : cache.allowText; + let parentTokens = cacheMap.get(parent); + if (parentTokens === undefined) { + parentTokens = generateSelectorFor(cache, injectedScript, parent, { ...options, isRecursive: true, noText }) || cssFallback(injectedScript, parent, options); + cacheMap.set(parent, parentTokens); + } + if (!parentTokens) + continue; + + updateResult([...parentTokens, ...inParent]); + } + } + return result; } function buildNoTextCandidates(injectedScript: InjectedScript, element: Element, options: InternalOptions): SelectorToken[] { @@ -333,7 +333,8 @@ function buildTextCandidates(injectedScript: InjectedScript, element: Element, i const cssToken: SelectorToken = { engine: 'css', selector: escapeNodeName(element), score: kCSSTagNameScore }; for (const alternative of textAlternatives) candidates.push([cssToken, { engine: 'internal:has-text', selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]); - if (text.length <= 80) { + if (isTargetNode && text.length <= 80) { + // Do not use regex for parent elements (for performance). const re = new RegExp('^' + escapeRegExp(text) + '$'); candidates.push([cssToken, { engine: 'internal:has-text', selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]); } @@ -351,7 +352,8 @@ function buildTextCandidates(injectedScript: InjectedScript, element: Element, i const roleToken = { engine: 'internal:role', selector: `${ariaRole}`, score: kRoleWithoutNameScore }; for (const alternative of textAlternatives) candidates.push([roleToken, { engine: 'internal:has-text', selector: escapeForTextSelector(alternative.text, false), score: kTextScore - alternative.scoreBonus }]); - if (text.length <= 80) { + if (isTargetNode && text.length <= 80) { + // Do not use regex for parent elements (for performance). const re = new RegExp('^' + escapeRegExp(text) + '$'); candidates.push([roleToken, { engine: 'internal:has-text', selector: escapeForTextSelector(re, false), score: kTextScoreRegex }]); } @@ -473,30 +475,6 @@ function combineScores(tokens: SelectorToken[]): number { return score; } -function chooseFirstSelector(injectedScript: InjectedScript, scope: Element | Document, targetElement: Element, selectors: SelectorToken[][], allowNthMatch: boolean): SelectorToken[] | null { - const joined = selectors.map(tokens => ({ tokens, score: combineScores(tokens) })); - joined.sort((a, b) => a.score - b.score); - - let bestWithIndex: SelectorToken[] | null = null; - for (const { tokens } of joined) { - const parsedSelector = injectedScript.parseSelector(joinTokens(tokens)); - const result = injectedScript.querySelectorAll(parsedSelector, scope); - if (result[0] === targetElement && result.length === 1) { - // We are the only match - found the best selector. - return tokens; - } - - // Otherwise, perhaps we can use nth=? - const index = result.indexOf(targetElement); - if (!allowNthMatch || bestWithIndex || index === -1 || result.length > 5) - continue; - - const nth: SelectorToken = { engine: 'nth', selector: String(index), score: kNthScore }; - bestWithIndex = [...tokens, nth]; - } - return bestWithIndex; -} - function isGuidLike(id: string): boolean { let lastCharacterType: 'lower' | 'upper' | 'digit' | 'other' | undefined; let transitionCount = 0; diff --git a/packages/injected/src/utilityScript.ts b/packages/injected/src/utilityScript.ts index 4bacbcdff8df6..e61ad96d45b5f 100644 --- a/packages/injected/src/utilityScript.ts +++ b/packages/injected/src/utilityScript.ts @@ -27,20 +27,16 @@ export type Builtins = { requestIdleCallback: Window['requestIdleCallback'], cancelIdleCallback: Window['cancelIdleCallback'], performance: Window['performance'], - // eslint-disable-next-line no-restricted-globals Intl: typeof window['Intl'], - // eslint-disable-next-line no-restricted-globals Date: typeof window['Date'], }; export class UtilityScript { - // eslint-disable-next-line no-restricted-globals readonly global: typeof globalThis; // Builtins protect injected code from clock emulation. readonly builtins: Builtins; readonly isUnderTest: boolean; - // eslint-disable-next-line no-restricted-globals constructor(global: typeof globalThis, isUnderTest: boolean) { this.global = global; this.isUnderTest = isUnderTest; diff --git a/packages/injected/src/webSocketMock.ts b/packages/injected/src/webSocketMock.ts index f45885a4e8abc..2ae2188e85607 100644 --- a/packages/injected/src/webSocketMock.ts +++ b/packages/injected/src/webSocketMock.ts @@ -33,7 +33,6 @@ export type ClosePageRequest = { type: 'closePage', id: string, code: number | u export type CloseServerRequest = { type: 'closeServer', id: string, code: number | undefined, reason: string | undefined, wasClean: boolean }; export type APIRequest = ConnectRequest | PassthroughRequest | EnsureOpenedRequest | SendToPageRequest | SendToServerRequest | ClosePageRequest | CloseServerRequest; -// eslint-disable-next-line no-restricted-globals type GlobalThis = typeof globalThis; export function inject(globalThis: GlobalThis) { diff --git a/packages/playwright-browser-chromium/package.json b/packages/playwright-browser-chromium/package.json index 32b2f075a720c..60383d54471dc 100644 --- a/packages/playwright-browser-chromium/package.json +++ b/packages/playwright-browser-chromium/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-chromium", - "version": "1.55.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs Chromium", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-browser-firefox/package.json b/packages/playwright-browser-firefox/package.json index df7b8fea635f6..1aa4a0b0d0210 100644 --- a/packages/playwright-browser-firefox/package.json +++ b/packages/playwright-browser-firefox/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-firefox", - "version": "1.55.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs Firefox", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-browser-webkit/package.json b/packages/playwright-browser-webkit/package.json index bb35606e27006..8b4f4eee4823b 100644 --- a/packages/playwright-browser-webkit/package.json +++ b/packages/playwright-browser-webkit/package.json @@ -1,6 +1,6 @@ { "name": "@playwright/browser-webkit", - "version": "1.55.0-next", + "version": "1.56.0", "description": "Playwright package that automatically installs WebKit", "repository": { "type": "git", @@ -27,6 +27,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-chromium/package.json b/packages/playwright-chromium/package.json index 5290fcfbd247a..e210506db1bb9 100644 --- a/packages/playwright-chromium/package.json +++ b/packages/playwright-chromium/package.json @@ -1,6 +1,6 @@ { "name": "playwright-chromium", - "version": "1.55.0-next", + "version": "1.56.0", "description": "A high-level API to automate Chromium", "repository": { "type": "git", @@ -30,6 +30,6 @@ "install": "node install.js" }, "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-client/package.json b/packages/playwright-client/package.json index 854df0d17903f..d27f5995a2d97 100644 --- a/packages/playwright-client/package.json +++ b/packages/playwright-client/package.json @@ -30,6 +30,6 @@ "watch": "npm run esbuild -- --watch" }, "dependencies": { - "playwright-core": "1.55.0-next" + "playwright-core": "1.56.0" } } diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 9d759a2bc2103..d4e4f52c74f85 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -2284,6 +2284,12 @@ export interface Page { runBeforeUnload?: boolean; }): Promise; + /** + * Returns up to (currently) 200 last console messages from this page. See + * [page.on('console')](https://playwright.dev/docs/api/class-page#page-event-console) for more details. + */ + consoleMessages(): Promise>; + /** * Gets the full HTML contents of the page, including the doctype. */ @@ -3598,6 +3604,12 @@ export interface Page { */ opener(): Promise; + /** + * Returns up to (currently) 200 last page errors from this page. See + * [page.on('pageerror')](https://playwright.dev/docs/api/class-page#page-event-page-error) for more details. + */ + pageErrors(): Promise>; + /** * Pauses script execution. Playwright will stop executing the script and wait for the user to either press the * 'Resume' button in the page overlay or to call `playwright.resume()` in the DevTools console. @@ -3914,6 +3926,21 @@ export interface Page { */ requestGC(): Promise; + /** + * Returns up to (currently) 100 last network request from this page. See + * [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request) for more details. + * + * Returned requests should be accessed immediately, otherwise they might be collected to prevent unbounded memory + * growth as new requests come in. Once collected, retrieving most information about the request is impossible. + * + * Note that requests reported through the + * [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request) request are not collected, so + * there is a trade off between efficient memory usage with + * [page.requests()](https://playwright.dev/docs/api/class-page#page-requests) and the amount of available information + * reported through [page.on('request')](https://playwright.dev/docs/api/class-page#page-event-request). + */ + requests(): Promise>; + /** * Routing provides the capability to modify network requests that are made by a page. * @@ -8181,14 +8208,7 @@ export interface BrowserContext { behavior?: 'wait'|'ignoreErrors'|'default' }): Promise; /** - * **NOTE** Only works with Chromium browser's persistent context. - * - * Emitted when new background page is created in the context. - * - * ```js - * const backgroundPage = await context.waitForEvent('backgroundpage'); - * ``` - * + * This event is not emitted. */ on(event: 'backgroundpage', listener: (page: Page) => any): this; @@ -8380,14 +8400,7 @@ export interface BrowserContext { once(event: 'weberror', listener: (webError: WebError) => any): this; /** - * **NOTE** Only works with Chromium browser's persistent context. - * - * Emitted when new background page is created in the context. - * - * ```js - * const backgroundPage = await context.waitForEvent('backgroundpage'); - * ``` - * + * This event is not emitted. */ addListener(event: 'backgroundpage', listener: (page: Page) => any): this; @@ -8634,14 +8647,7 @@ export interface BrowserContext { off(event: 'weberror', listener: (webError: WebError) => any): this; /** - * **NOTE** Only works with Chromium browser's persistent context. - * - * Emitted when new background page is created in the context. - * - * ```js - * const backgroundPage = await context.waitForEvent('backgroundpage'); - * ``` - * + * This event is not emitted. */ prependListener(event: 'backgroundpage', listener: (page: Page) => any): this; @@ -8840,9 +8846,8 @@ export interface BrowserContext { }>): Promise; /** - * **NOTE** Background pages are only supported on Chromium-based browsers. - * - * All existing background pages in the context. + * Returns an empty list. + * @deprecated Background pages have been removed from Chromium together with Manifest V2 extensions. */ backgroundPages(): Array; @@ -9359,14 +9364,7 @@ export interface BrowserContext { }): Promise; /** - * **NOTE** Only works with Chromium browser's persistent context. - * - * Emitted when new background page is created in the context. - * - * ```js - * const backgroundPage = await context.waitForEvent('backgroundpage'); - * ``` - * + * This event is not emitted. */ waitForEvent(event: 'backgroundpage', optionsOrPredicate?: { predicate?: (page: Page) => boolean | Promise, timeout?: number } | ((page: Page) => boolean | Promise)): Promise; @@ -14918,10 +14916,7 @@ export interface BrowserType { */ downloadsPath?: string; - /** - * Specify environment variables that will be visible to the browser. Defaults to `process.env`. - */ - env?: { [key: string]: string|number|boolean; }; + env?: { [key: string]: string|undefined; }; /** * Path to a browser executable to run instead of the bundled one. If @@ -15351,10 +15346,7 @@ export interface BrowserType { */ downloadsPath?: string; - /** - * Specify environment variables that will be visible to the browser. Defaults to `process.env`. - */ - env?: { [key: string]: string|number|boolean; }; + env?: { [key: string]: string|undefined; }; /** * Path to a browser executable to run instead of the bundled one. If @@ -16235,18 +16227,16 @@ export type AndroidKey = 'Home' | 'Back' | 'Call' | 'EndCall' | - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | - 'Star' | 'Pound' | '*' | '#' | + '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | + 'Star' | '*' | 'Pound' | '#' | 'DialUp' | 'DialDown' | 'DialLeft' | 'DialRight' | 'DialCenter' | 'VolumeUp' | 'VolumeDown' | - 'ChannelUp' | 'ChannelDown' | 'Power' | 'Camera' | 'Clear' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | - 'Comma' | ',' | - 'Period' | '.' | + 'Comma' | ',' | 'Period' | '.' | 'AltLeft' | 'AltRight' | 'ShiftLeft' | 'ShiftRight' | 'Tab' | '\t' | @@ -16274,8 +16264,26 @@ export type AndroidKey = 'Notification' | 'Search' | 'RecentApps' | + 'MediaPlayPause' | + 'MediaStop' | + 'MediaNext' | + 'MediaPrevious' | + 'MediaRewind' | + 'MediaFastForward' | + 'MediaPlay' | + 'MediaPause' | + 'MediaClose' | + 'MediaEject' | + 'MediaRecord' | + 'ChannelUp' | 'ChannelDown' | 'AppSwitch' | 'Assist' | + 'MediaAudioTrack' | + 'MediaTopMenu' | + 'MediaSkipForward' | + 'MediaSkipBackward' | + 'MediaStepForward' | + 'MediaStepBackward' | 'Cut' | 'Copy' | 'Paste'; @@ -21766,10 +21774,7 @@ export interface LaunchOptions { */ downloadsPath?: string; - /** - * Specify environment variables that will be visible to the browser. Defaults to `process.env`. - */ - env?: { [key: string]: string|number|boolean; }; + env?: { [key: string]: string|undefined; }; /** * Path to a browser executable to run instead of the bundled one. If diff --git a/packages/playwright-core/bin/install_webkit_wsl.ps1 b/packages/playwright-core/bin/install_webkit_wsl.ps1 new file mode 100644 index 0000000000000..cc16ca9fdd6ff --- /dev/null +++ b/packages/playwright-core/bin/install_webkit_wsl.ps1 @@ -0,0 +1,35 @@ +$ErrorActionPreference = 'Stop' + +# WebKit WSL Installation Script +# See webkit-wsl-transport-server.ts for the complete architecture diagram. +# This script sets up a WSL distribution that will be used to run WebKit. + +$Distribution = "playwright" +$Username = "pwuser" + +$distributions = (wsl --list --quiet) -split "\r?\n" +if ($distributions -contains $Distribution) { + Write-Host "WSL distribution '$Distribution' already exists. Skipping installation." +} else { + Write-Host "Installing new WSL distribution '$Distribution'..." + $VhdSize = "10GB" + wsl --install -d Ubuntu-24.04 --name $Distribution --no-launch --vhd-size $VhdSize + wsl -d $Distribution -u root adduser --gecos GECOS --disabled-password $Username +} + +$pwshDirname = (Resolve-Path -Path $PSScriptRoot).Path; +$playwrightCoreRoot = Resolve-Path (Join-Path $pwshDirname "..") + +$initScript = @" +if [ ! -f "/home/$Username/node/bin/node" ]; then + mkdir -p /home/$Username/node + curl -fsSL https://nodejs.org/dist/v22.17.0/node-v22.17.0-linux-x64.tar.xz -o /home/$Username/node/node-v22.17.0-linux-x64.tar.xz + tar -xJf /home/$Username/node/node-v22.17.0-linux-x64.tar.xz -C /home/$Username/node --strip-components=1 +fi +/home/$Username/node/bin/node cli.js install-deps webkit +cp lib/server/webkit/wsl/webkit-wsl-transport-client.js /home/$Username/ +sudo -u $Username PLAYWRIGHT_SKIP_BROWSER_GC=1 /home/$Username/node/bin/node cli.js install webkit +"@ -replace "\r\n", "`n" + +wsl -d $Distribution --cd $playwrightCoreRoot -u root -- bash -c "$initScript" +Write-Host "Done!" \ No newline at end of file diff --git a/packages/playwright-core/bin/reinstall_chrome_beta_mac.sh b/packages/playwright-core/bin/reinstall_chrome_beta_mac.sh index c563c81de95ff..617e3b5ee5bb7 100755 --- a/packages/playwright-core/bin/reinstall_chrome_beta_mac.sh +++ b/packages/playwright-core/bin/reinstall_chrome_beta_mac.sh @@ -4,7 +4,7 @@ set -x rm -rf "/Applications/Google Chrome Beta.app" cd /tmp -curl --retry 3 -o ./googlechromebeta.dmg -k https://dl.google.com/chrome/mac/universal/beta/googlechromebeta.dmg +curl --retry 3 -o ./googlechromebeta.dmg https://dl.google.com/chrome/mac/universal/beta/googlechromebeta.dmg hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechromebeta.dmg ./googlechromebeta.dmg cp -pR "/Volumes/googlechromebeta.dmg/Google Chrome Beta.app" /Applications hdiutil detach /Volumes/googlechromebeta.dmg diff --git a/packages/playwright-core/bin/reinstall_chrome_stable_mac.sh b/packages/playwright-core/bin/reinstall_chrome_stable_mac.sh index 035fa863f161e..6aa650a55a23f 100755 --- a/packages/playwright-core/bin/reinstall_chrome_stable_mac.sh +++ b/packages/playwright-core/bin/reinstall_chrome_stable_mac.sh @@ -4,7 +4,7 @@ set -x rm -rf "/Applications/Google Chrome.app" cd /tmp -curl --retry 3 -o ./googlechrome.dmg -k https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg +curl --retry 3 -o ./googlechrome.dmg https://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechrome.dmg ./googlechrome.dmg cp -pR "/Volumes/googlechrome.dmg/Google Chrome.app" /Applications hdiutil detach /Volumes/googlechrome.dmg diff --git a/packages/playwright-core/bin/reinstall_msedge_beta_mac.sh b/packages/playwright-core/bin/reinstall_msedge_beta_mac.sh index c03bb022643f2..72ec3e4e5732d 100755 --- a/packages/playwright-core/bin/reinstall_msedge_beta_mac.sh +++ b/packages/playwright-core/bin/reinstall_msedge_beta_mac.sh @@ -3,7 +3,7 @@ set -e set -x cd /tmp -curl --retry 3 -o ./msedge_beta.pkg -k "$1" +curl --retry 3 -o ./msedge_beta.pkg "$1" # Note: there's no way to uninstall previously installed MSEdge. # However, running PKG again seems to update installation. sudo installer -pkg /tmp/msedge_beta.pkg -target / diff --git a/packages/playwright-core/bin/reinstall_msedge_dev_mac.sh b/packages/playwright-core/bin/reinstall_msedge_dev_mac.sh index 9b664dadab4c6..3376e8696cde7 100755 --- a/packages/playwright-core/bin/reinstall_msedge_dev_mac.sh +++ b/packages/playwright-core/bin/reinstall_msedge_dev_mac.sh @@ -3,7 +3,7 @@ set -e set -x cd /tmp -curl --retry 3 -o ./msedge_dev.pkg -k "$1" +curl --retry 3 -o ./msedge_dev.pkg "$1" # Note: there's no way to uninstall previously installed MSEdge. # However, running PKG again seems to update installation. sudo installer -pkg /tmp/msedge_dev.pkg -target / diff --git a/packages/playwright-core/bin/reinstall_msedge_stable_mac.sh b/packages/playwright-core/bin/reinstall_msedge_stable_mac.sh index 7a72703c3e5e4..afcd2f531b5e4 100755 --- a/packages/playwright-core/bin/reinstall_msedge_stable_mac.sh +++ b/packages/playwright-core/bin/reinstall_msedge_stable_mac.sh @@ -3,7 +3,7 @@ set -e set -x cd /tmp -curl --retry 3 -o ./msedge_stable.pkg -k "$1" +curl --retry 3 -o ./msedge_stable.pkg "$1" # Note: there's no way to uninstall previously installed MSEdge. # However, running PKG again seems to update installation. sudo installer -pkg /tmp/msedge_stable.pkg -target / diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 169663e8ee3ba..2aebbd23c80df 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -3,43 +3,43 @@ "browsers": [ { "name": "chromium", - "revision": "1187", + "revision": "1194", "installByDefault": true, - "browserVersion": "140.0.7339.16" + "browserVersion": "141.0.7390.37" }, { "name": "chromium-headless-shell", - "revision": "1187", + "revision": "1194", "installByDefault": true, - "browserVersion": "140.0.7339.16" + "browserVersion": "141.0.7390.37" }, { "name": "chromium-tip-of-tree", - "revision": "1357", + "revision": "1371", "installByDefault": false, - "browserVersion": "141.0.7342.0" + "browserVersion": "142.0.7430.0" }, { "name": "chromium-tip-of-tree-headless-shell", - "revision": "1357", + "revision": "1371", "installByDefault": false, - "browserVersion": "141.0.7342.0" + "browserVersion": "142.0.7430.0" }, { "name": "firefox", - "revision": "1490", + "revision": "1495", "installByDefault": true, - "browserVersion": "141.0" + "browserVersion": "142.0.1" }, { "name": "firefox-beta", - "revision": "1485", + "revision": "1490", "installByDefault": false, - "browserVersion": "142.0b4" + "browserVersion": "143.0b10" }, { "name": "webkit", - "revision": "2203", + "revision": "2215", "installByDefault": true, "revisionOverrides": { "debian11-x64": "2105", diff --git a/packages/playwright-core/bundles/utils/src/utilsBundleImpl.ts b/packages/playwright-core/bundles/utils/src/utilsBundleImpl.ts index 409372e89448e..83a1074f17639 100644 --- a/packages/playwright-core/bundles/utils/src/utilsBundleImpl.ts +++ b/packages/playwright-core/bundles/utils/src/utilsBundleImpl.ts @@ -50,6 +50,7 @@ export const open = openLibrary; export { PNG } from 'pngjs'; export { program } from 'commander'; +export { Option as ProgramOption } from 'commander'; import progressLibrary from 'progress'; export const progress = progressLibrary; diff --git a/packages/playwright-core/package.json b/packages/playwright-core/package.json index 9e62d8396ffc7..0b8fdbb9a59a1 100644 --- a/packages/playwright-core/package.json +++ b/packages/playwright-core/package.json @@ -1,6 +1,6 @@ { "name": "playwright-core", - "version": "1.55.0-next", + "version": "1.56.0", "description": "A high-level API to automate web browsers", "repository": { "type": "git", @@ -33,9 +33,7 @@ "./lib/server/registry/index": "./lib/server/registry/index.js", "./lib/utils": "./lib/utils.js", "./lib/utilsBundle": "./lib/utilsBundle.js", - "./lib/zipBundle": "./lib/zipBundle.js", - "./types/protocol": "./types/protocol.d.ts", - "./types/structs": "./types/structs.d.ts" + "./lib/zipBundle": "./lib/zipBundle.js" }, "bin": { "playwright-core": "cli.js" diff --git a/packages/playwright-core/src/browserServerImpl.ts b/packages/playwright-core/src/browserServerImpl.ts index c789991584afc..0057d26ac94bf 100644 --- a/packages/playwright-core/src/browserServerImpl.ts +++ b/packages/playwright-core/src/browserServerImpl.ts @@ -26,7 +26,7 @@ import * as validatorPrimitives from './protocol/validatorPrimitives'; import { ProgressController } from './server/progress'; import type { BrowserServer, BrowserServerLauncher } from './client/browserType'; -import type { LaunchOptions, LaunchServerOptions, Logger, Env } from './client/types'; +import type { LaunchServerOptions, Logger } from './client/types'; import type { ProtocolLogger } from './server/types'; import type { WebSocketEventEmitter } from './utilsBundle'; import type { Browser } from './server/browser'; @@ -38,7 +38,7 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher { this._browserName = browserName; } - async launchServer(options: LaunchOptions & LaunchServerOptions & { _userDataDir?: string } = {}): Promise { + async launchServer(options: LaunchServerOptions & { _sharedBrowser?: boolean, _userDataDir?: string } = {}): Promise { const playwright = createPlaywright({ sdkLanguage: 'javascript', isServer: true }); // 1. Pre-launch the browser const metadata = { id: '', startTime: 0, endTime: 0, type: 'Internal', method: '', params: {}, log: [], internal: true }; @@ -78,14 +78,10 @@ export class BrowserServerLauncherImpl implements BrowserServerLauncher { throw e; } - return this.launchServerOnExistingBrowser(browser, options); - } - - async launchServerOnExistingBrowser(browser: Browser, options: LaunchServerOptions): Promise { const path = options.wsPath ? (options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}`) : `/${createGuid()}`; // 2. Start the server - const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser, debugController: options._debugController }); + const server = new PlaywrightServer({ mode: options._sharedBrowser ? 'launchServerShared' : 'launchServer', path, maxConnections: Infinity, preLaunchedBrowser: browser }); const wsEndpoint = await server.listen(options.port, options.host); // 3. Return the BrowserServer interface @@ -112,7 +108,7 @@ function toProtocolLogger(logger: Logger | undefined): ProtocolLogger | undefine } : undefined; } -function envObjectToArray(env: Env): { name: string, value: string }[] { +function envObjectToArray(env: NodeJS.ProcessEnv): { name: string, value: string }[] { const result: { name: string, value: string }[] = []; for (const name in env) { if (!Object.is(env[name], undefined)) diff --git a/packages/playwright-core/src/client/browser.ts b/packages/playwright-core/src/client/browser.ts index d112e884436ba..80d2ff2fb9972 100644 --- a/packages/playwright-core/src/client/browser.ts +++ b/packages/playwright-core/src/client/browser.ts @@ -24,7 +24,7 @@ import { mkdirIfNeeded } from './fileUtils'; import type { BrowserType } from './browserType'; import type { Page } from './page'; -import type { BrowserContextOptions, LaunchOptions, LaunchServerOptions, Logger } from './types'; +import type { BrowserContextOptions, LaunchOptions, Logger } from './types'; import type * as api from '../../types/types'; import type * as channels from '@protocol/channels'; @@ -146,17 +146,6 @@ export class Browser extends ChannelOwner implements ap return CDPSession.from((await this._channel.newBrowserCDPSession()).session); } - async _launchServer(options: LaunchServerOptions = {}) { - const serverLauncher = this._browserType._serverLauncher; - const browserImpl = this._connection.toImpl?.(this); - if (!serverLauncher || !browserImpl) - throw new Error('Launching server is not supported'); - return await serverLauncher.launchServerOnExistingBrowser(browserImpl, { - _sharedBrowser: true, - ...options, - }); - } - async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) { this._path = options.path; await this._channel.startTracing({ ...options, page: page ? page._channel : undefined }); diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 7b8015b630cb4..9f97260f97759 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -72,7 +72,6 @@ export class BrowserContext extends ChannelOwner readonly tracing: Tracing; readonly clock: Clock; - readonly _backgroundPages = new Set(); readonly _serviceWorkers = new Set(); private _harRecorders = new Map(); _closingStatus: 'none' | 'closing' | 'closed' = 'none'; @@ -102,11 +101,6 @@ export class BrowserContext extends ChannelOwner this._channel.on('page', ({ page }) => this._onPage(Page.from(page))); this._channel.on('route', ({ route }) => this._onRoute(network.Route.from(route))); this._channel.on('webSocketRoute', ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute))); - this._channel.on('backgroundPage', ({ page }) => { - const backgroundPage = Page.from(page); - this._backgroundPages.add(backgroundPage); - this.emit(Events.BrowserContext.BackgroundPage, backgroundPage); - }); this._channel.on('serviceWorker', ({ worker }) => { const serviceWorker = Worker.from(worker); serviceWorker._context = this; @@ -114,7 +108,7 @@ export class BrowserContext extends ChannelOwner this.emit(Events.BrowserContext.ServiceWorker, serviceWorker); }); this._channel.on('console', event => { - const consoleMessage = new ConsoleMessage(this._platform, event); + const consoleMessage = new ConsoleMessage(this._platform, event, Page.fromNullable(event.page)); this.emit(Events.BrowserContext.Console, consoleMessage); const page = consoleMessage.page(); if (page) @@ -456,7 +450,7 @@ export class BrowserContext extends ChannelOwner } backgroundPages(): Page[] { - return [...this._backgroundPages]; + return []; } serviceWorkers(): Worker[] { diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index c0491a07154bd..af99f15dc7139 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -31,11 +31,9 @@ import type { ConnectOptions, LaunchOptions, LaunchPersistentContextOptions, Lau import type * as api from '../../types/types'; import type * as channels from '@protocol/channels'; import type { ChildProcess } from 'child_process'; -import type { Browser as BrowserImpl } from '../server/browser'; export interface BrowserServerLauncher { - launchServer(options?: LaunchOptions & LaunchServerOptions): Promise; - launchServerOnExistingBrowser(browser: BrowserImpl, options?: LaunchServerOptions): Promise; + launchServer(options?: LaunchServerOptions): Promise; } // This is here just for api generation and checking. diff --git a/packages/playwright-core/src/client/channelOwner.ts b/packages/playwright-core/src/client/channelOwner.ts index 31adda7d17cf9..7bd097b3ff486 100644 --- a/packages/playwright-core/src/client/channelOwner.ts +++ b/packages/playwright-core/src/client/channelOwner.ts @@ -20,7 +20,7 @@ import { methodMetainfo } from '../utils/isomorphic/protocolMetainfo'; import { captureLibraryStackTrace } from './clientStackTrace'; import { stringifyStackFrames } from '../utils/isomorphic/stackTrace'; -import type { ClientInstrumentation, RecoverFromApiErrorHandler } from './clientInstrumentation'; +import type { ClientInstrumentation } from './clientInstrumentation'; import type { Connection } from './connection'; import type { Logger } from './types'; import type { ValidatorContext } from '../protocol/validator'; @@ -199,14 +199,7 @@ export abstract class ChannelOwner Promise; - export interface ClientInstrumentation { addListener(listener: ClientInstrumentationListener): void; removeListener(listener: ClientInstrumentationListener): void; removeAllListeners(): void; onApiCallBegin(apiCall: ApiCallData, channel: { type: string, method: string, params?: Record }): void; - onApiCallRecovery(apiCall: ApiCallData, error: Error, recoveryHandlers: RecoverFromApiErrorHandler[]): void; onApiCallEnd(apiCall: ApiCallData): void; onWillPause(options: { keepTestTimeout: boolean }): void; @@ -52,7 +44,6 @@ export interface ClientInstrumentation { export interface ClientInstrumentationListener { onApiCallBegin?(apiCall: ApiCallData, channel: { type: string, method: string, params?: Record }): void; - onApiCallRecovery?(apiCall: ApiCallData, error: Error, recoveryHandlers: RecoverFromApiErrorHandler[]): void; onApiCallEnd?(apiCall: ApiCallData): void; onWillPause?(options: { keepTestTimeout: boolean }): void; diff --git a/packages/playwright-core/src/client/consoleMessage.ts b/packages/playwright-core/src/client/consoleMessage.ts index 5f8efee8c27ea..b7b5a9720a04b 100644 --- a/packages/playwright-core/src/client/consoleMessage.ts +++ b/packages/playwright-core/src/client/consoleMessage.ts @@ -28,8 +28,8 @@ export class ConsoleMessage implements api.ConsoleMessage { private _page: Page | null; private _event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent; - constructor(platform: Platform, event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent) { - this._page = ('page' in event && event.page) ? Page.from(event.page) : null; + constructor(platform: Platform, event: channels.BrowserContextConsoleEvent | channels.ElectronApplicationConsoleEvent, page: Page | null) { + this._page = page; this._event = event; if (platform.inspectCustom) (this as any)[platform.inspectCustom] = () => this._inspect(); diff --git a/packages/playwright-core/src/client/electron.ts b/packages/playwright-core/src/client/electron.ts index fc33350d0750f..efef83d840cde 100644 --- a/packages/playwright-core/src/client/electron.ts +++ b/packages/playwright-core/src/client/electron.ts @@ -25,7 +25,7 @@ import { Waiter } from './waiter'; import { TimeoutSettings } from './timeoutSettings'; import type { Page } from './page'; -import type { BrowserContextOptions, Env, Headers, WaitForEventOptions } from './types'; +import type { BrowserContextOptions, Headers, WaitForEventOptions } from './types'; import type * as structs from '../../types/structs'; import type * as api from '../../types/types'; import type * as channels from '@protocol/channels'; @@ -34,7 +34,7 @@ import type { BrowserWindow } from 'electron'; import type { Playwright } from './playwright'; type ElectronOptions = Omit & { - env?: Env, + env?: NodeJS.ProcessEnv, extraHTTPHeaders?: Headers, recordHar?: BrowserContextOptions['recordHar'], colorScheme?: 'dark' | 'light' | 'no-preference' | null, @@ -92,7 +92,7 @@ export class ElectronApplication extends ChannelOwner { this.emit(Events.ElectronApplication.Close); }); - this._channel.on('console', event => this.emit(Events.ElectronApplication.Console, new ConsoleMessage(this._platform, event))); + this._channel.on('console', event => this.emit(Events.ElectronApplication.Console, new ConsoleMessage(this._platform, event, null))); this._setEventToSubscriptionMapping(new Map([ [Events.ElectronApplication.Console, 'console'], ])); diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts index a074b26f3d581..41544b8c35b71 100644 --- a/packages/playwright-core/src/client/events.ts +++ b/packages/playwright-core/src/client/events.ts @@ -42,7 +42,7 @@ export const Events = { // Can't use just 'error' due to node.js special treatment of error events. // @see https://nodejs.org/api/events.html#events_error_events WebError: 'weberror', - BackgroundPage: 'backgroundpage', + BackgroundPage: 'backgroundpage', // Deprecated in v1.56, never emitted anymore. ServiceWorker: 'serviceworker', Request: 'request', Response: 'response', diff --git a/packages/playwright-core/src/client/frame.ts b/packages/playwright-core/src/client/frame.ts index 7aaa8353f1165..9470ebc7129d1 100644 --- a/packages/playwright-core/src/client/frame.ts +++ b/packages/playwright-core/src/client/frame.ts @@ -466,7 +466,7 @@ export class Frame extends ChannelOwner implements api.Fr return (await this._channel.title()).value; } - async _expect(expression: string, options: Omit): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { + async _expect(expression: string, options: Omit): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }> { const params: channels.FrameExpectParams = { expression, ...options, isNot: !!options.isNot }; params.expectedValue = serializeArgument(options.expectedValue); const result = (await this._channel.expect(params)); diff --git a/packages/playwright-core/src/client/locator.ts b/packages/playwright-core/src/client/locator.ts index f7afdbb5fe550..fd889c75f36f2 100644 --- a/packages/playwright-core/src/client/locator.ts +++ b/packages/playwright-core/src/client/locator.ts @@ -377,7 +377,7 @@ export class Locator implements api.Locator { await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options, timeout: this._frame._timeout(options) }); } - async _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> { + async _expect(expression: string, options: FrameExpectParams): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean, errorMessage?: string }> { return this._frame._expect(expression, { ...options, selector: this._selector, diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 88c34c6785ed9..727090961ad25 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -91,6 +91,7 @@ export class Request extends ChannelOwner implements ap private _actualHeadersPromise: Promise | undefined; _timing: ResourceTiming; private _fallbackOverrides: SerializedFallbackOverrides = {}; + _hasResponse = false; static from(request: channels.RequestChannel): Request { return (request as any)._object; @@ -117,6 +118,8 @@ export class Request extends ChannelOwner implements ap responseStart: -1, responseEnd: -1, }; + this._hasResponse = this._initializer.hasResponse; + this._channel.on('response', () => this._hasResponse = true); } url(): string { diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 4dbf85ab03c42..739231637fbff 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -22,14 +22,14 @@ import { evaluationScript } from './clientHelper'; import { Coverage } from './coverage'; import { Download } from './download'; import { ElementHandle, determineScreenshotType } from './elementHandle'; -import { TargetClosedError, isTargetClosedError, serializeError } from './errors'; +import { TargetClosedError, isTargetClosedError, parseError, serializeError } from './errors'; import { Events } from './events'; import { FileChooser } from './fileChooser'; import { Frame, verifyLoadState } from './frame'; import { HarRouter } from './harRouter'; import { Keyboard, Mouse, Touchscreen } from './input'; import { JSHandle, assertMaxArguments, parseResult, serializeArgument } from './jsHandle'; -import { Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network'; +import { Request, Response, Route, RouteHandler, WebSocket, WebSocketRoute, WebSocketRouteHandler, validateHeaders } from './network'; import { Video } from './video'; import { Waiter } from './waiter'; import { Worker } from './worker'; @@ -41,13 +41,14 @@ import { trimStringWithEllipsis } from '../utils/isomorphic/stringUtils'; import { urlMatches, urlMatchesEqual } from '../utils/isomorphic/urlMatch'; import { LongStandingScope } from '../utils/isomorphic/manualPromise'; import { isObject, isRegExp, isString } from '../utils/isomorphic/rtti'; +import { ConsoleMessage } from './consoleMessage'; import type { BrowserContext } from './browserContext'; import type { Clock } from './clock'; import type { APIRequestContext } from './fetch'; import type { WaitForNavigationOptions } from './frame'; import type { FrameLocator, Locator, LocatorOptions } from './locator'; -import type { Request, RouteHandlerCallback, WebSocketRouteHandlerCallback } from './network'; +import type { RouteHandlerCallback, WebSocketRouteHandlerCallback } from './network'; import type { FilePayload, Headers, LifecycleEvent, SelectOption, SelectOptionOptions, Size, TimeoutOptions, WaitForEventOptions, WaitForFunctionOptions } from './types'; import type * as structs from '../../types/structs'; import type * as api from '../../types/types'; @@ -237,7 +238,6 @@ export class Page extends ChannelOwner implements api.Page _onClose() { this._closed = true; this._browserContext._pages.delete(this); - this._browserContext._backgroundPages.delete(this); this._disposeHarRouters(); this.emit(Events.Page.Close, this); } @@ -669,6 +669,16 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.fill(selector, value, options); } + async consoleMessages(): Promise { + const { messages } = await this._channel.consoleMessages(); + return messages.map(message => new ConsoleMessage(this._platform, message, this)); + } + + async pageErrors(): Promise { + const { errors } = await this._channel.pageErrors(); + return errors.map(error => parseError(error)); + } + locator(selector: string, options?: LocatorOptions): Locator { return this.mainFrame().locator(selector, options); } @@ -793,6 +803,11 @@ export class Page extends ChannelOwner implements api.Page return await this._mainFrame.waitForFunction(pageFunction, arg, options); } + async requests() { + const { requests } = await this._channel.requests(); + return requests.map(request => Request.from(request)); + } + workers(): Worker[] { return [...this._workers]; } diff --git a/packages/playwright-core/src/client/types.ts b/packages/playwright-core/src/client/types.ts index bcdf728423a89..48a2d71cf8953 100644 --- a/packages/playwright-core/src/client/types.ts +++ b/packages/playwright-core/src/client/types.ts @@ -28,7 +28,6 @@ export interface Logger { export type TimeoutOptions = { timeout?: number }; export type StrictOptions = { strict?: boolean }; export type Headers = { [key: string]: string }; -export type Env = { [key: string]: string | number | boolean | undefined }; export type WaitForEventOptions = Function | TimeoutOptions & { predicate?: Function }; export type WaitForFunctionOptions = TimeoutOptions & { polling?: 'raf' | number }; @@ -88,7 +87,7 @@ export type BrowserContextOptions = Omit { throw new Error('shouldnt be used'); }, - id, - ); - } - if (isExtension) { const connectFilter = url.searchParams.get('connect'); if (connectFilter) { @@ -135,6 +121,16 @@ export class PlaywrightServer { ); } + if (url.searchParams.has('debug-controller')) { + return new PlaywrightConnection( + controllerSemaphore, + ws, + true, + this._playwright, + async () => { throw new Error('shouldnt be used'); }, + id, + ); + } return new PlaywrightConnection( reuseBrowserSemaphore, ws, diff --git a/packages/playwright-core/src/server/accessibility.ts b/packages/playwright-core/src/server/accessibility.ts index b92c5411f5e35..fbf8012c9583b 100644 --- a/packages/playwright-core/src/server/accessibility.ts +++ b/packages/playwright-core/src/server/accessibility.ts @@ -19,11 +19,11 @@ import type * as dom from './dom'; import type * as channels from '@protocol/channels'; export interface AXNode { - isInteresting(insideControl: boolean): boolean; - isLeafNode(): boolean; - isControl(): boolean; - serialize(): channels.AXNode; - children(): Iterable; + isInteresting(insideControl: boolean): boolean; + isLeafNode(): boolean; + isControl(): boolean; + serialize(): channels.AXNode; + children(): Iterable; } export class Accessibility { @@ -33,9 +33,9 @@ export class Accessibility { } async snapshot(options: { - interestingOnly?: boolean; - root?: dom.ElementHandle; - } = {}): Promise { + interestingOnly?: boolean; + root?: dom.ElementHandle; + } = {}): Promise { const { interestingOnly = true, root = null, diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index ce55d0fb1ec9f..28cdd80a9841d 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -69,6 +69,11 @@ export class BidiBrowser extends Browser { ], }); + await browser._browserSession.send('network.addDataCollector', { + dataTypes: [bidi.Network.DataType.Response], + maxEncodedDataSize: 20_000_000, // same default as in CDP: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_network_agent.cc;l=134;drc=4128411589187a396829a827f59a655bed876aa7 + }); + if (options.persistent) { const context = new BidiBrowserContext(browser, undefined, options.persistent); browser._defaultContext = context; @@ -205,6 +210,18 @@ export class BidiBrowserContext extends BrowserContext { userContexts: [this._userContextId()], })); } + if (this._options.timezoneId) { + promises.push(this._browser._browserSession.send('emulation.setTimezoneOverride', { + timezone: this._options.timezoneId, + userContexts: [this._userContextId()], + })); + } + if (this._options.userAgent) { + promises.push(this._browser._browserSession.send('emulation.setUserAgentOverride', { + userAgent: this._options.userAgent, + userContexts: [this._userContextId()], + })); + } await Promise.all(promises); } @@ -306,6 +323,11 @@ export class BidiBrowserContext extends BrowserContext { } async setUserAgent(userAgent: string | undefined): Promise { + this._options.userAgent = userAgent; + await this._browser._browserSession.send('emulation.setUserAgentOverride', { + userAgent: userAgent ?? null, + userContexts: [this._userContextId()], + }); } async doUpdateOffline(): Promise { @@ -343,14 +365,20 @@ export class BidiBrowserContext extends BrowserContext { override async doUpdateDefaultViewport() { if (!this._options.viewport) return; - await this._browser._browserSession.send('browsingContext.setViewport', { - viewport: { - width: this._options.viewport.width, - height: this._options.viewport.height - }, - devicePixelRatio: this._options.deviceScaleFactor || 1, - userContexts: [this._userContextId()], - }); + await Promise.all([ + this._browser._browserSession.send('browsingContext.setViewport', { + viewport: { + width: this._options.viewport.width, + height: this._options.viewport.height + }, + devicePixelRatio: this._options.deviceScaleFactor || 1, + userContexts: [this._userContextId()], + }), + this._browser._browserSession.send('emulation.setScreenOrientationOverride', { + screenOrientation: getScreenOrientation(!!this._options.isMobile, this._options.viewport), + userContexts: [this._userContextId()], + }) + ]); } override async doUpdateDefaultEmulatedMedia() { @@ -420,6 +448,7 @@ function fromBidiSameSite(sameSite: bidi.Network.SameSite): channels.NetworkCook case 'strict': return 'Strict'; case 'lax': return 'Lax'; case 'none': return 'None'; + case 'default': return 'Lax'; } return 'None'; } @@ -467,6 +496,19 @@ function getProxyConfiguration(proxySettings?: types.ProxySettings): bidi.Sessio return proxy; } +export function getScreenOrientation(isMobile: boolean, viewportSize: types.Size) { + const screenOrientation: bidi.Emulation.ScreenOrientation = { + type: 'landscape-primary', + natural: bidi.Emulation.ScreenOrientationNatural.Landscape + }; + if (isMobile) { + screenOrientation.natural = bidi.Emulation.ScreenOrientationNatural.Portrait; + if (viewportSize.width <= viewportSize.height) + screenOrientation.type = 'portrait-primary'; + } + return screenOrientation; +} + export namespace Network { export const enum SameSite { Strict = 'strict', diff --git a/packages/playwright-core/src/server/bidi/bidiChromium.ts b/packages/playwright-core/src/server/bidi/bidiChromium.ts index ac8639a1fc1e9..0af0dfb4d2023 100644 --- a/packages/playwright-core/src/server/bidi/bidiChromium.ts +++ b/packages/playwright-core/src/server/bidi/bidiChromium.ts @@ -26,7 +26,6 @@ import { waitForReadyState } from '../chromium/chromium'; import type { BrowserOptions } from '../browser'; import type { SdkObject } from '../instrumentation'; -import type { Env } from '../utils/processLauncher'; import type { ProtocolError } from '../protocolError'; import type { ConnectionTransport } from '../transport'; import type * as types from '../types'; @@ -77,7 +76,7 @@ export class BidiChromium extends BrowserType { return error; } - override amendEnvironment(env: Env): Env { + override amendEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { return env; } @@ -93,7 +92,7 @@ export class BidiChromium extends BrowserType { return false; } - override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] { + override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) { const chromeArguments = this._innerDefaultArgs(options); chromeArguments.push(`--user-data-dir=${userDataDir}`); chromeArguments.push('--remote-debugging-port=0'); diff --git a/packages/playwright-core/src/server/bidi/bidiConnection.ts b/packages/playwright-core/src/server/bidi/bidiConnection.ts index 0882836c38a36..5dfca87212a9f 100644 --- a/packages/playwright-core/src/server/bidi/bidiConnection.ts +++ b/packages/playwright-core/src/server/bidi/bidiConnection.ts @@ -28,7 +28,8 @@ import type * as bidi from './third_party/bidiProtocol'; // BidiPlaywright uses this special id to issue Browser.close command which we // should ignore. -export const kBrowserCloseMessageId = 0; +export const kBrowserCloseMessageId = Number.MAX_SAFE_INTEGER - 1; +export const kShutdownSessionNewMessageId = kBrowserCloseMessageId - 1; export class BidiConnection { private readonly _transport: ConnectionTransport; @@ -40,6 +41,9 @@ export class BidiConnection { private _closed = false; readonly browserSession: BidiSession; readonly _browsingContextToSession = new Map(); + readonly _realmToBrowsingContext = new Map(); + // TODO: shared/service workers might have multiple owner realms. + readonly _realmToOwnerRealm = new Map(); constructor(transport: ConnectionTransport, onDisconnect: () => void, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) { this._transport = transport; @@ -69,12 +73,35 @@ export class BidiConnection { // Bidi messages do not have a common session identifier, so we // route them based on BrowsingContext. if (object.type === 'event') { + // Track realms to context as well as realm ownership to later resolve + // non-window realms to a browsing context. + if (object.method === 'script.realmCreated') { + if ('context' in object.params) + this._realmToBrowsingContext.set(object.params.realm, object.params.context); + if (object.params.type === 'dedicated-worker') + this._realmToOwnerRealm.set(object.params.realm, object.params.owners[0]); + } else if (object.method === 'script.realmDestroyed') { + this._realmToBrowsingContext.delete(object.params.realm); + this._realmToOwnerRealm.delete(object.params.realm); + } // Route page events to the right session. let context; - if ('context' in object.params) + let realm; + if ('context' in object.params) { context = object.params.context; - else if (object.method === 'log.entryAdded' || object.method === 'script.message') + } else if (object.method === 'log.entryAdded' || object.method === 'script.message') { context = object.params.source?.context; + realm = object.params.source?.realm; + } else if (object.method === 'script.realmCreated' && object.params.type === 'dedicated-worker') { + realm = object.params.owners[0]; + } + if (!context && realm) { + // Find parent realm and its browing context if it is a window realm. + while (this._realmToOwnerRealm.get(realm)) + realm = this._realmToOwnerRealm.get(realm)!; + context = this._realmToBrowsingContext.get(realm); + } + if (context) { const session = this._browsingContextToSession.get(context); if (session) { @@ -209,7 +236,7 @@ export class BidiSession extends EventEmitter { dispatchMessage(message: any) { const object = message as bidi.Message; - if (object.id === kBrowserCloseMessageId) + if (object.id === kBrowserCloseMessageId || object.id === kShutdownSessionNewMessageId) return; if (object.id && this._callbacks.has(object.id)) { const callback = this._callbacks.get(object.id)!; diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index 33a2890bfb853..c7e0a745ebafe 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -57,7 +57,7 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { if (response.type === 'success') return BidiDeserializer.deserialize(response.result); if (response.type === 'exception') - throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails)); + throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text); throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)); } @@ -76,7 +76,7 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { throw new js.JavaScriptErrorInEvaluate('Cannot get handle: ' + JSON.stringify(response.result)); } if (response.type === 'exception') - throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails)); + throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text); throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)); } @@ -95,7 +95,7 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { userActivation: true, }); if (response.type === 'exception') - throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails)); + throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text); if (response.type === 'success') { if (returnByValue) return parseEvaluationResultValue(BidiDeserializer.deserialize(response.result)); @@ -180,7 +180,7 @@ export class BidiExecutionContext implements js.ExecutionContextDelegate { userActivation: true, }); if (response.type === 'exception') - throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text + '\nFull val: ' + JSON.stringify(response.exceptionDetails)); + throw new js.JavaScriptErrorInEvaluate(response.exceptionDetails.text); if (response.type === 'success') return response.result; throw new js.JavaScriptErrorInEvaluate('Unexpected response type: ' + JSON.stringify(response)); diff --git a/packages/playwright-core/src/server/bidi/bidiFirefox.ts b/packages/playwright-core/src/server/bidi/bidiFirefox.ts index 7c96383fe1827..9201a5ffc56af 100644 --- a/packages/playwright-core/src/server/bidi/bidiFirefox.ts +++ b/packages/playwright-core/src/server/bidi/bidiFirefox.ts @@ -20,13 +20,12 @@ import path from 'path'; import { wrapInASCIIBox } from '../utils/ascii'; import { BrowserType, kNoXServerRunningError } from '../browserType'; import { BidiBrowser } from './bidiBrowser'; -import { kBrowserCloseMessageId } from './bidiConnection'; +import { kBrowserCloseMessageId, kShutdownSessionNewMessageId } from './bidiConnection'; import { createProfile } from './third_party/firefoxPrefs'; import { ManualPromise } from '../../utils/isomorphic/manualPromise'; import type { BrowserOptions } from '../browser'; import type { SdkObject } from '../instrumentation'; -import type { Env } from '../utils/processLauncher'; import type { ProtocolError } from '../protocolError'; import type { ConnectionTransport } from '../transport'; import type * as types from '../types'; @@ -57,7 +56,7 @@ export class BidiFirefox extends BrowserType { return error; } - override amendEnvironment(env: Env): Env { + override amendEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { if (!path.isAbsolute(os.homedir())) throw new Error(`Cannot launch Firefox with relative home directory. Did you set ${os.platform() === 'win32' ? 'USERPROFILE' : 'HOME'} to a relative path?`); @@ -77,8 +76,23 @@ export class BidiFirefox extends BrowserType { return env; } - override attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void { + override attemptToGracefullyCloseBrowser(transport: ConnectionTransport) { + this._attemptToGracefullyCloseBrowser(transport).catch(() => {}); + } + + private async _attemptToGracefullyCloseBrowser(transport: ConnectionTransport): Promise { // Note that it's fine to reuse the transport, since our connection ignores kBrowserCloseMessageId. + if (!transport.onmessage) { + // browser.close does not work without an active session. If there is no connection + // created with the transport, make sure to create a new session first. + transport.send({ method: 'session.new', params: { capabilities: {} }, id: kShutdownSessionNewMessageId }); + await new Promise(resolve => { + transport.onmessage = message => { + if (message.id === kShutdownSessionNewMessageId) + resolve(true); + }; + }); + } transport.send({ method: 'browser.close', params: {}, id: kBrowserCloseMessageId }); } @@ -93,11 +107,13 @@ export class BidiFirefox extends BrowserType { }); } - override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] { + override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) { const { args = [], headless } = options; const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile')); if (userDataDirArg) throw this._createUserDataDirArgMisuseError('--profile'); + if (args.find(arg => !arg.startsWith('-'))) + throw new Error('Arguments can not specify page to be opened'); const firefoxArguments = ['--remote-debugging-port=0']; if (headless) firefoxArguments.push('--headless'); diff --git a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts index f4e4a801d6428..b6807e731d457 100644 --- a/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts +++ b/packages/playwright-core/src/server/bidi/bidiNetworkManager.ts @@ -34,6 +34,7 @@ export class BidiNetworkManager { private _userRequestInterceptionEnabled: boolean = false; private _protocolRequestInterceptionEnabled: boolean = false; private _credentials: types.Credentials | undefined; + private _attemptedAuthentications = new Set(); private _intercepId: bidi.Network.Intercept | undefined; constructor(bidiSession: BidiSession, page: Page) { @@ -61,7 +62,7 @@ export class BidiNetworkManager { if (!frame) return; if (redirectedFrom) - this._requests.delete(redirectedFrom._id); + this._deleteRequest(redirectedFrom._id); let route; if (param.intercepts) { // We do not support intercepting redirects. @@ -88,7 +89,9 @@ export class BidiNetworkManager { if (!request) return; const getResponseBody = async () => { - throw new Error(`Response body is not available for requests in Bidi`); + const { bytes } = await this._session.send('network.getData', { request: params.request.request, dataType: bidi.Network.DataType.Response }); + const encoding = bytes.type === 'base64' ? 'base64' : 'utf8'; + return Buffer.from(bytes.value, encoding); }; const timings = params.request.timings; const startTime = timings.requestTime; @@ -131,7 +134,7 @@ export class BidiNetworkManager { if (isRedirected) { response._requestFinished(responseEndTime); } else { - this._requests.delete(request._id); + this._deleteRequest(request._id); response._requestFinished(responseEndTime); } response._setHttpVersion(params.response.protocol); @@ -143,7 +146,7 @@ export class BidiNetworkManager { const request = this._requests.get(params.request.request); if (!request) return; - this._requests.delete(request._id); + this._deleteRequest(request._id); const response = request.request._existingResponse(); if (response) { response.setTransferSize(null); @@ -158,24 +161,37 @@ export class BidiNetworkManager { private _onAuthRequired(params: bidi.Network.AuthRequiredParameters) { const isBasic = params.response.authChallenges?.some(challenge => challenge.scheme.startsWith('Basic')); const credentials = this._page.browserContext._options.httpCredentials; - if (isBasic && credentials) { - this._session.sendMayFail('network.continueWithAuth', { - request: params.request.request, - action: 'provideCredentials', - credentials: { - type: 'password', - username: credentials.username, - password: credentials.password, - } - }); + if (isBasic && credentials && (!credentials.origin || (new URL(params.request.url).origin).toLowerCase() === credentials.origin.toLowerCase())) { + if (this._attemptedAuthentications.has(params.request.request)) { + this._session.sendMayFail('network.continueWithAuth', { + request: params.request.request, + action: 'cancel', + }); + } else { + this._attemptedAuthentications.add(params.request.request); + this._session.sendMayFail('network.continueWithAuth', { + request: params.request.request, + action: 'provideCredentials', + credentials: { + type: 'password', + username: credentials.username, + password: credentials.password, + } + }); + } } else { this._session.sendMayFail('network.continueWithAuth', { request: params.request.request, - action: 'default', + action: 'cancel', }); } } + _deleteRequest(requestId: string) { + this._requests.delete(requestId); + this._attemptedAuthentications.delete(requestId); + } + async setRequestInterception(value: boolean) { this._userRequestInterceptionEnabled = value; await this._updateProtocolRequestInterception(); @@ -226,8 +242,8 @@ class BidiRequest { redirectedFrom._redirectedTo = this; // TODO: missing in the spec? const postDataBuffer = null; - this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined, - payload.request.url, 'other', payload.request.method, postDataBuffer, fromBidiHeaders(payload.request.headers)); + this.request = new network.Request(frame._page.browserContext, frame, null, redirectedFrom ? redirectedFrom.request : null, payload.navigation ?? undefined, payload.request.url, + resourceTypeFromBidi(payload.request.destination, payload.request.initiatorType, payload.initiator?.type), payload.request.method, postDataBuffer, fromBidiHeaders(payload.request.headers)); // "raw" headers are the same as "provisional" headers in Bidi. this.request.setRawRequestHeaders(null); this.request._setBodySize(payload.request.bodySize || 0); @@ -344,3 +360,33 @@ function toBidiSameSite(sameSite?: 'Strict' | 'Lax' | 'None'): bidi.Network.Same return bidi.Network.SameSite.Lax; return bidi.Network.SameSite.None; } + +function resourceTypeFromBidi(requestDestination: string, requestInitiatorType: string | null, eventInitiatorType: string | undefined): string { + switch (requestDestination) { + case 'audio': return 'media'; + case 'audioworklet': return 'script'; + case 'document': return 'document'; + case 'font': return 'font'; + case 'frame': return 'document'; + case 'iframe': return 'document'; + case 'image': return 'image'; + case 'object': return 'object'; + case 'paintworklet': return 'script'; + case 'script': return 'script'; + case 'serviceworker': return 'script'; + case 'sharedworker': return 'script'; + case 'style': return 'stylesheet'; + case 'track': return 'texttrack'; + case 'video': return 'media'; + case 'worker': return 'script'; + case '': + switch (requestInitiatorType) { + case 'fetch': return 'fetch'; + case 'font': return 'font'; + case 'xmlhttprequest': return 'xhr'; + case null: return eventInitiatorType === 'script' ? 'xhr' : 'document'; + default: return 'other'; + } + default: return 'other'; + } +} diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index d9807955a66be..56d4d7c7054d9 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -17,7 +17,9 @@ import { eventsHelper } from '../utils/eventsHelper'; import * as dialog from '../dialog'; import * as dom from '../dom'; -import { Page } from '../page'; +import * as js from '../javascript'; +import { BidiBrowserContext, getScreenOrientation } from './bidiBrowser'; +import { Page, Worker } from '../page'; import { BidiExecutionContext, createHandle } from './bidiExecutionContext'; import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './bidiInput'; import { BidiNetworkManager } from './bidiNetworkManager'; @@ -30,7 +32,6 @@ import type * as frames from '../frames'; import type { InitScript, PageDelegate } from '../page'; import type { Progress } from '../progress'; import type * as types from '../types'; -import type { BidiBrowserContext } from './bidiBrowser'; import type { BidiSession } from './bidiConnection'; import type * as channels from '@protocol/channels'; @@ -45,6 +46,7 @@ export class BidiPage implements PageDelegate { readonly _session: BidiSession; readonly _opener: BidiPage | null; readonly _realmToContext: Map; + private _realmToWorkerContext = new Map(); private _sessionListeners: RegisteredListener[] = []; readonly _browserContext: BidiBrowserContext; readonly _networkManager: BidiNetworkManager; @@ -75,6 +77,8 @@ export class BidiPage implements PageDelegate { eventsHelper.addEventListener(bidiSession, 'browsingContext.historyUpdated', this._onHistoryUpdated.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.domContentLoaded', this._onDomContentLoaded.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.load', this._onLoad.bind(this)), + eventsHelper.addEventListener(bidiSession, 'browsingContext.downloadWillBegin', this._onDownloadWillBegin.bind(this)), + eventsHelper.addEventListener(bidiSession, 'browsingContext.downloadEnd', this._onDownloadEnded.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.userPromptOpened', this._onUserPromptOpened.bind(this)), eventsHelper.addEventListener(bidiSession, 'log.entryAdded', this._onLogEntryAdded.bind(this)), ]; @@ -118,6 +122,13 @@ export class BidiPage implements PageDelegate { } private _onRealmCreated(realmInfo: bidi.Script.RealmInfo) { + if (realmInfo.type === 'dedicated-worker') { + const delegate = new BidiExecutionContext(this._session, realmInfo); + const worker = new Worker(this._page, realmInfo.origin); + this._realmToWorkerContext.set(realmInfo.realm, worker.createExecutionContext(delegate)); + this._page.addWorker(realmInfo.realm, worker); + return; + } if (this._realmToContext.has(realmInfo.realm)) return; if (realmInfo.type !== 'window') @@ -159,11 +170,17 @@ export class BidiPage implements PageDelegate { _onRealmDestroyed(params: bidi.Script.RealmDestroyedParameters): boolean { const context = this._realmToContext.get(params.realm); - if (!context) - return false; - this._realmToContext.delete(params.realm); - context.frame._contextDestroyed(context); - return true; + if (context) { + this._realmToContext.delete(params.realm); + context.frame._contextDestroyed(context); + return true; + } + const existed = this._realmToWorkerContext.delete(params.realm); + if (existed) { + this._page.removeWorker(params.realm); + return true; + } + return false; } // TODO: route the message directly to the browser @@ -217,13 +234,55 @@ export class BidiPage implements PageDelegate { event.defaultValue)); } + private _onDownloadWillBegin(event: bidi.BrowsingContext.DownloadWillBeginParams) { + if (!event.navigation) + return; + + this._page.frameManager.frameAbortedNavigation(event.context, 'Download is starting'); + + let originPage = this._page.initializedOrUndefined(); + // If it's a new window download, report it on the opener page. + if (!originPage && this._opener) + originPage = this._opener._page.initializedOrUndefined(); + if (!originPage) + return; + + this._browserContext._browser._downloadCreated(originPage, event.navigation, event.url, event.suggestedFilename); + } + + private _onDownloadEnded(event: bidi.BrowsingContext.DownloadEndParams) { + if (!event.navigation) + return; + this._browserContext._browser._downloadFinished(event.navigation, event.status === 'canceled' ? 'canceled' : undefined); + } + private _onLogEntryAdded(params: bidi.Log.Entry) { + if (params.type === 'javascript' && params.level === 'error') { + let errorName = ''; + let errorMessage: string | undefined; + if (params.text?.includes(': ')) { + const index = params.text.indexOf(': '); + errorName = params.text.substring(0, index); + errorMessage = params.text.substring(index + 2); + } else { + errorMessage = params.text ?? undefined; + } + const error = new Error(errorMessage); + error.name = errorName; + error.stack = `${params.text}\n${params.stackTrace?.callFrames.map(f => { + const location = `${f.url}:${f.lineNumber + 1}:${f.columnNumber + 1}`; + return f.functionName ? ` at ${f.functionName} (${location})` : ` at ${location}`; + }).join('\n')}`; + this._page.addPageError(error); + return; + } if (params.type !== 'console') return; const entry: bidi.Log.ConsoleLogEntry = params as bidi.Log.ConsoleLogEntry; - const context = this._realmToContext.get(params.source.realm); + const context = this._realmToContext.get(params.source.realm) ?? this._realmToWorkerContext.get(params.source.realm); if (!context) return; + const callFrame = params.stackTrace?.callFrames[0]; const location = callFrame ?? { url: '', lineNumber: 1, columnNumber: 1 }; this._page.addConsoleMessage(entry.method, entry.args.map(arg => createHandle(context, arg)), location, params.text || undefined); @@ -258,14 +317,20 @@ export class BidiPage implements PageDelegate { if (!emulatedSize) return; const viewportSize = emulatedSize.viewport; - await this._session.send('browsingContext.setViewport', { - context: this._session.sessionId, - viewport: { - width: viewportSize.width, - height: viewportSize.height, - }, - devicePixelRatio: options.deviceScaleFactor || 1 - }); + await Promise.all([ + this._session.send('browsingContext.setViewport', { + context: this._session.sessionId, + viewport: { + width: viewportSize.width, + height: viewportSize.height, + }, + devicePixelRatio: options.deviceScaleFactor || 1 + }), + this._session.send('emulation.setScreenOrientationOverride', { + contexts: [this._session.sessionId], + screenOrientation: getScreenOrientation(!!options.isMobile, viewportSize) + }) + ]); } async updateRequestInterception(): Promise { @@ -344,10 +409,17 @@ export class BidiPage implements PageDelegate { } async closePage(runBeforeUnload: boolean): Promise { - await this._session.send('browsingContext.close', { - context: this._session.sessionId, - promptUnload: runBeforeUnload, - }); + if (runBeforeUnload) { + this._session.sendMayFail('browsingContext.close', { + context: this._session.sessionId, + promptUnload: runBeforeUnload, + }); + } else { + await this._session.send('browsingContext.close', { + context: this._session.sessionId, + promptUnload: runBeforeUnload, + }); + } } async setBackgroundColor(color?: { r: number; g: number; b: number; a: number; }): Promise { diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts index 5be629678aeaf..474876ed2c31f 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiCommands.d.ts @@ -117,6 +117,18 @@ export interface Commands { params: Bidi.Emulation.SetGeolocationOverrideParameters; returnType: Bidi.EmptyResult; }; + 'emulation.setScreenOrientationOverride': { + params: Bidi.Emulation.SetScreenOrientationOverrideParameters; + returnType: Bidi.EmptyResult; + }; + 'emulation.setTimezoneOverride': { + params: Bidi.Emulation.SetTimezoneOverrideParameters; + returnType: Bidi.EmptyResult; + }; + 'emulation.setUserAgentOverride': { + params: Bidi.Emulation.SetUserAgentOverrideParameters; + returnType: Bidi.Emulation.SetUserAgentOverrideResult; + }; 'emulation.setLocaleOverride': { params: Bidi.Emulation.SetLocaleOverrideParameters; @@ -166,6 +178,19 @@ export interface Commands { returnType: Bidi.Storage.SetCookieParameters; }; + 'network.addDataCollector': { + params: Bidi.Network.AddDataCollectorParameters; + returnType: Bidi.Network.AddDataCollectorResult; + }; + 'network.removeDataCollector': { + params: Bidi.Network.RemoveDataCollectorParameters; + returnType: Bidi.Network.RemoveDataCollectorResult; + }; + 'network.getData': { + params: Bidi.Network.GetDataParameters; + returnType: Bidi.Network.GetDataResult; + }; + 'network.addIntercept': { params: Bidi.Network.AddInterceptParameters; returnType: Bidi.Network.AddInterceptResult; diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts b/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts index 5d9bedd66261e..cb010b4362e69 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiProtocolCore.ts @@ -18,30 +18,15 @@ // Copied from upstream: https://github.com/GoogleChromeLabs/chromium-bidi/blob/main/src/protocol/generated/webdriver-bidi.ts /** - * THIS FILE IS AUTOGENERATED by cddlconv 0.1.6. + * THIS FILE IS AUTOGENERATED by cddlconv 0.1.7. * Run `node tools/generate-bidi-types.mjs` to regenerate. * @see https://github.com/w3c/webdriver-bidi/blob/master/index.bs */ -export type Event = { - type: 'event'; -} & EventData & - Extensible; export type Command = { id: JsUint; } & CommandData & Extensible; -export type CommandResponse = { - type: 'success'; - id: JsUint; - result: ResultData; -} & Extensible; -export type EventData = - | BrowsingContextEvent - | InputEvent - | LogEvent - | NetworkEvent - | ScriptEvent; export type CommandData = | BrowserCommand | BrowsingContextCommand @@ -52,16 +37,13 @@ export type CommandData = | SessionCommand | StorageCommand | WebExtensionCommand; -export type ResultData = - | BrowsingContextResult - | EmptyResult - | NetworkResult - | ScriptResult - | SessionResult - | StorageResult - | WebExtensionResult; export type EmptyParams = Extensible; export type Message = CommandResponse | ErrorResponse | Event; +export type CommandResponse = { + type: 'success'; + id: JsUint; + result: ResultData; +} & Extensible; export type ErrorResponse = { type: 'error'; id: JsUint | null; @@ -69,7 +51,27 @@ export type ErrorResponse = { message: string; stacktrace?: string; } & Extensible; +export type ResultData = + | BrowserResult + | BrowsingContextResult + | EmulationResult + | InputResult + | NetworkResult + | ScriptResult + | SessionResult + | StorageResult + | WebExtensionResult; export type EmptyResult = Extensible; +export type Event = { + type: 'event'; +} & EventData & + Extensible; +export type EventData = + | BrowsingContextEvent + | InputEvent + | LogEvent + | NetworkEvent + | ScriptEvent; export type Extensible = { [key: string]: any; }; @@ -120,18 +122,12 @@ export type SessionCommand = | Session.Status | Session.Subscribe | Session.Unsubscribe; -export namespace Session { - export type ProxyConfiguration = - | Session.AutodetectProxyConfiguration - | Session.DirectProxyConfiguration - | Session.ManualProxyConfiguration - | Session.PacProxyConfiguration - | Session.SystemProxyConfiguration; -} export type SessionResult = + | Session.EndResult | Session.NewResult | Session.StatusResult - | Session.SubscribeResult; + | Session.SubscribeResult + | Session.UnsubscribeResult; export namespace Session { export type CapabilitiesRequest = { alwaysMatch?: Session.CapabilityRequest; @@ -148,6 +144,14 @@ export namespace Session { unhandledPromptBehavior?: Session.UserPromptHandler; } & Extensible; } +export namespace Session { + export type ProxyConfiguration = + | Session.AutodetectProxyConfiguration + | Session.DirectProxyConfiguration + | Session.ManualProxyConfiguration + | Session.PacProxyConfiguration + | Session.SystemProxyConfiguration; +} export namespace Session { export type AutodetectProxyConfiguration = { proxyType: 'autodetect'; @@ -164,8 +168,8 @@ export namespace Session { httpProxy?: string; sslProxy?: string; } & ({} | Session.SocksProxyConfiguration) & { - noProxy?: [...string[]]; - } & Extensible; + noProxy?: [...string[]]; + } & Extensible; } export namespace Session { export type SocksProxyConfiguration = { @@ -225,10 +229,6 @@ export namespace Session { export namespace Session { export type UnsubscribeByAttributesRequest = { events: [string, ...string[]]; - contexts?: [ - BrowsingContext.BrowsingContext, - ...BrowsingContext.BrowsingContext[], - ]; }; } export namespace Session { @@ -276,6 +276,9 @@ export namespace Session { params: EmptyParams; }; } +export namespace Session { + export type EndResult = EmptyResult; +} export namespace Session { export type Subscribe = { method: 'session.subscribe'; @@ -298,16 +301,25 @@ export namespace Session { | Session.UnsubscribeByAttributesRequest | Session.UnsubscribeByIdRequest; } +export namespace Session { + export type UnsubscribeResult = EmptyResult; +} export type BrowserCommand = | Browser.Close | Browser.CreateUserContext | Browser.GetClientWindows | Browser.GetUserContexts | Browser.RemoveUserContext - | Browser.SetClientWindowState; + | Browser.SetClientWindowState + | Browser.SetDownloadBehavior; export type BrowserResult = + | Browser.CloseResult | Browser.CreateUserContextResult - | Browser.GetUserContextsResult; + | Browser.GetClientWindowsResult + | Browser.GetUserContextsResult + | Browser.RemoveUserContextResult + | Browser.SetClientWindowStateResult + | Browser.SetDownloadBehaviorResult; export namespace Browser { export type ClientWindow = string; } @@ -336,6 +348,9 @@ export namespace Browser { params: EmptyParams; }; } +export namespace Browser { + export type CloseResult = EmptyResult; +} export namespace Browser { export type CreateUserContext = { method: 'browser.createUserContext'; @@ -385,6 +400,9 @@ export namespace Browser { userContext: Browser.UserContext; }; } +export namespace Browser { + export type RemoveUserContextResult = EmptyResult; +} export namespace Browser { export type SetClientWindowState = { method: 'browser.setClientWindowState'; @@ -410,6 +428,40 @@ export namespace Browser { y?: JsInt; }; } +export namespace Browser { + export type SetClientWindowStateResult = Browser.ClientWindowInfo; +} +export namespace Browser { + export type SetDownloadBehavior = { + method: 'browser.setDownloadBehavior'; + params: Browser.SetDownloadBehaviorParameters; + }; +} +export namespace Browser { + export type SetDownloadBehaviorParameters = { + downloadBehavior: Browser.DownloadBehavior | null; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Browser { + export type DownloadBehavior = + | Browser.DownloadBehaviorAllowed + | Browser.DownloadBehaviorDenied; +} +export namespace Browser { + export type DownloadBehaviorAllowed = { + type: 'allowed'; + destinationFolder: string; + }; +} +export namespace Browser { + export type DownloadBehaviorDenied = { + type: 'denied'; + }; +} +export namespace Browser { + export type SetDownloadBehaviorResult = EmptyResult; +} export type BrowsingContextCommand = | BrowsingContext.Activate | BrowsingContext.CaptureScreenshot @@ -423,6 +475,19 @@ export type BrowsingContextCommand = | BrowsingContext.Reload | BrowsingContext.SetViewport | BrowsingContext.TraverseHistory; +export type BrowsingContextResult = + | BrowsingContext.ActivateResult + | BrowsingContext.CaptureScreenshotResult + | BrowsingContext.CloseResult + | BrowsingContext.CreateResult + | BrowsingContext.GetTreeResult + | BrowsingContext.HandleUserPromptResult + | BrowsingContext.LocateNodesResult + | BrowsingContext.NavigateResult + | BrowsingContext.PrintResult + | BrowsingContext.ReloadResult + | BrowsingContext.SetViewportResult + | BrowsingContext.TraverseHistoryResult; export type BrowsingContextEvent = | BrowsingContext.ContextCreated | BrowsingContext.ContextDestroyed @@ -438,14 +503,6 @@ export type BrowsingContextEvent = | BrowsingContext.NavigationStarted | BrowsingContext.UserPromptClosed | BrowsingContext.UserPromptOpened; -export type BrowsingContextResult = - | BrowsingContext.CaptureScreenshotResult - | BrowsingContext.CreateResult - | BrowsingContext.GetTreeResult - | BrowsingContext.LocateNodesResult - | BrowsingContext.NavigateResult - | BrowsingContext.PrintResult - | BrowsingContext.TraverseHistoryResult; export namespace BrowsingContext { export type BrowsingContext = string; } @@ -549,6 +606,15 @@ export namespace BrowsingContext { context: BrowsingContext.BrowsingContext; }; } +export namespace BrowsingContext { + export type ActivateResult = EmptyResult; +} +export namespace BrowsingContext { + export type CaptureScreenshot = { + method: 'browsingContext.captureScreenshot'; + params: BrowsingContext.CaptureScreenshotParameters; + }; +} export namespace BrowsingContext { export type CaptureScreenshotParameters = { context: BrowsingContext.BrowsingContext; @@ -560,12 +626,6 @@ export namespace BrowsingContext { clip?: BrowsingContext.ClipRectangle; }; } -export namespace BrowsingContext { - export type CaptureScreenshot = { - method: 'browsingContext.captureScreenshot'; - params: BrowsingContext.CaptureScreenshotParameters; - }; -} export namespace BrowsingContext { export type ImageFormat = { type: string; @@ -615,6 +675,9 @@ export namespace BrowsingContext { promptUnload?: boolean; }; } +export namespace BrowsingContext { + export type CloseResult = EmptyResult; +} export namespace BrowsingContext { export type Create = { method: 'browsingContext.create'; @@ -673,6 +736,15 @@ export namespace BrowsingContext { userText?: string; }; } +export namespace BrowsingContext { + export type HandleUserPromptResult = EmptyResult; +} +export namespace BrowsingContext { + export type LocateNodes = { + method: 'browsingContext.locateNodes'; + params: BrowsingContext.LocateNodesParameters; + }; +} export namespace BrowsingContext { export type LocateNodesParameters = { context: BrowsingContext.BrowsingContext; @@ -685,12 +757,6 @@ export namespace BrowsingContext { startNodes?: [Script.SharedReference, ...Script.SharedReference[]]; }; } -export namespace BrowsingContext { - export type LocateNodes = { - method: 'browsingContext.locateNodes'; - params: BrowsingContext.LocateNodesParameters; - }; -} export namespace BrowsingContext { export type LocateNodesResult = { nodes: [...Script.NodeRemoteValue[]]; @@ -809,6 +875,9 @@ export namespace BrowsingContext { wait?: BrowsingContext.ReadinessState; }; } +export namespace BrowsingContext { + export type ReloadResult = BrowsingContext.NavigateResult; +} export namespace BrowsingContext { export type SetViewport = { method: 'browsingContext.setViewport'; @@ -832,6 +901,9 @@ export namespace BrowsingContext { height: JsUint; }; } +export namespace BrowsingContext { + export type SetViewportResult = EmptyResult; +} export namespace BrowsingContext { export type TraverseHistory = { method: 'browsingContext.traverseHistory'; @@ -845,7 +917,7 @@ export namespace BrowsingContext { }; } export namespace BrowsingContext { - export type TraverseHistoryResult = Record; + export type TraverseHistoryResult = EmptyResult; } export namespace BrowsingContext { export type ContextCreated = { @@ -981,7 +1053,17 @@ export type EmulationCommand = | Emulation.SetGeolocationOverride | Emulation.SetLocaleOverride | Emulation.SetScreenOrientationOverride - | Emulation.SetTimezoneOverride; + | Emulation.SetScriptingEnabled + | Emulation.SetTimezoneOverride + | Emulation.SetUserAgentOverride; +export type EmulationResult = + | Emulation.SetForcedColorsModeThemeOverrideResult + | Emulation.SetGeolocationOverrideResult + | Emulation.SetLocaleOverrideResult + | Emulation.SetScreenOrientationOverrideResult + | Emulation.SetScriptingEnabledResult + | Emulation.SetTimezoneOverrideResult + | Emulation.SetUserAgentOverrideResult; export namespace Emulation { export type SetForcedColorsModeThemeOverride = { method: 'emulation.setForcedColorsModeThemeOverride'; @@ -1004,6 +1086,9 @@ export namespace Emulation { Dark = 'dark', } } +export namespace Emulation { + export type SetForcedColorsModeThemeOverrideResult = EmptyResult; +} export namespace Emulation { export type SetGeolocationOverride = { method: 'emulation.setGeolocationOverride'; @@ -1013,11 +1098,11 @@ export namespace Emulation { export namespace Emulation { export type SetGeolocationOverrideParameters = ( | { - coordinates: Emulation.GeolocationCoordinates | null; - } + coordinates: Emulation.GeolocationCoordinates | null; + } | { - error: Emulation.GeolocationPositionError; - } + error: Emulation.GeolocationPositionError; + } ) & { contexts?: [ BrowsingContext.BrowsingContext, @@ -1071,6 +1156,9 @@ export namespace Emulation { type: 'positionUnavailable'; }; } +export namespace Emulation { + export type SetGeolocationOverrideResult = EmptyResult; +} export namespace Emulation { export type SetLocaleOverride = { method: 'emulation.setLocaleOverride'; @@ -1087,6 +1175,9 @@ export namespace Emulation { userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; }; } +export namespace Emulation { + export type SetLocaleOverrideResult = EmptyResult; +} export namespace Emulation { export type SetScreenOrientationOverride = { method: 'emulation.setScreenOrientationOverride'; @@ -1122,6 +1213,47 @@ export namespace Emulation { userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; }; } +export namespace Emulation { + export type SetScreenOrientationOverrideResult = EmptyResult; +} +export namespace Emulation { + export type SetUserAgentOverride = { + method: 'emulation.setUserAgentOverride'; + params: Emulation.SetUserAgentOverrideParameters; + }; +} +export namespace Emulation { + export type SetUserAgentOverrideParameters = { + userAgent: string | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export type SetUserAgentOverrideResult = EmptyResult; +} +export namespace Emulation { + export type SetScriptingEnabled = { + method: 'emulation.setScriptingEnabled'; + params: Emulation.SetScriptingEnabledParameters; + }; +} +export namespace Emulation { + export type SetScriptingEnabledParameters = { + enabled: false | null; + contexts?: [ + BrowsingContext.BrowsingContext, + ...BrowsingContext.BrowsingContext[], + ]; + userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; + }; +} +export namespace Emulation { + export type SetScriptingEnabledResult = EmptyResult; +} export namespace Emulation { export type SetTimezoneOverride = { method: 'emulation.setTimezoneOverride'; @@ -1138,6 +1270,9 @@ export namespace Emulation { userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; }; } +export namespace Emulation { + export type SetTimezoneOverrideResult = EmptyResult; +} export type NetworkCommand = | Network.AddDataCollector | Network.AddIntercept @@ -1152,13 +1287,26 @@ export type NetworkCommand = | Network.RemoveIntercept | Network.SetCacheBehavior | Network.SetExtraHeaders; +export type NetworkResult = + | Network.AddDataCollectorResult + | Network.AddInterceptResult + | Network.ContinueRequestResult + | Network.ContinueResponseResult + | Network.ContinueWithAuthResult + | Network.DisownDataResult + | Network.FailRequestResult + | Network.GetDataResult + | Network.ProvideResponseResult + | Network.RemoveDataCollectorResult + | Network.RemoveInterceptResult + | Network.SetCacheBehaviorResult + | Network.SetExtraHeadersResult; export type NetworkEvent = | Network.AuthRequired | Network.BeforeRequestSent | Network.FetchError | Network.ResponseCompleted | Network.ResponseStarted; -export type NetworkResult = Network.AddInterceptResult; export namespace Network { export type AuthChallenge = { scheme: string; @@ -1369,6 +1517,12 @@ export namespace Network { collector: Network.Collector; }; } +export namespace Network { + export type AddIntercept = { + method: 'network.addIntercept'; + params: Network.AddInterceptParameters; + }; +} export namespace Network { export type AddInterceptParameters = { phases: [Network.InterceptPhase, ...Network.InterceptPhase[]]; @@ -1379,12 +1533,6 @@ export namespace Network { urlPatterns?: [...Network.UrlPattern[]]; }; } -export namespace Network { - export type AddIntercept = { - method: 'network.addIntercept'; - params: Network.AddInterceptParameters; - }; -} export namespace Network { export const enum InterceptPhase { BeforeRequestSent = 'beforeRequestSent', @@ -1413,6 +1561,9 @@ export namespace Network { url?: string; }; } +export namespace Network { + export type ContinueRequestResult = EmptyResult; +} export namespace Network { export type ContinueResponse = { method: 'network.continueResponse'; @@ -1429,6 +1580,9 @@ export namespace Network { statusCode?: JsUint; }; } +export namespace Network { + export type ContinueResponseResult = EmptyResult; +} export namespace Network { export type ContinueWithAuth = { method: 'network.continueWithAuth'; @@ -1454,6 +1608,9 @@ export namespace Network { action: 'default' | 'cancel'; }; } +export namespace Network { + export type ContinueWithAuthResult = EmptyResult; +} export namespace Network { export type DisownData = { method: 'network.disownData'; @@ -1467,6 +1624,9 @@ export namespace Network { request: Network.Request; }; } +export namespace Network { + export type DisownDataResult = EmptyResult; +} export namespace Network { export type FailRequest = { method: 'network.failRequest'; @@ -1478,6 +1638,9 @@ export namespace Network { request: Network.Request; }; } +export namespace Network { + export type FailRequestResult = EmptyResult; +} export namespace Network { export type GetData = { method: 'network.getData'; @@ -1516,6 +1679,9 @@ export namespace Network { statusCode?: JsUint; }; } +export namespace Network { + export type ProvideResponseResult = EmptyResult; +} export namespace Network { export type RemoveDataCollector = { method: 'network.removeDataCollector'; @@ -1527,6 +1693,9 @@ export namespace Network { collector: Network.Collector; }; } +export namespace Network { + export type RemoveDataCollectorResult = EmptyResult; +} export namespace Network { export type RemoveIntercept = { method: 'network.removeIntercept'; @@ -1538,6 +1707,9 @@ export namespace Network { intercept: Network.Intercept; }; } +export namespace Network { + export type RemoveInterceptResult = EmptyResult; +} export namespace Network { export type SetCacheBehavior = { method: 'network.setCacheBehavior'; @@ -1553,6 +1725,9 @@ export namespace Network { ]; }; } +export namespace Network { + export type SetCacheBehaviorResult = EmptyResult; +} export namespace Network { export type SetExtraHeaders = { method: 'network.setExtraHeaders'; @@ -1561,7 +1736,7 @@ export namespace Network { } export namespace Network { export type SetExtraHeadersParameters = { - headers: [Network.Header, ...Network.Header[]]; + headers: [...Network.Header[]]; contexts?: [ BrowsingContext.BrowsingContext, ...BrowsingContext.BrowsingContext[], @@ -1569,30 +1744,59 @@ export namespace Network { userContexts?: [Browser.UserContext, ...Browser.UserContext[]]; }; } -export type ScriptEvent = - | Script.Message - | Script.RealmCreated - | Script.RealmDestroyed; +export namespace Network { + export type SetExtraHeadersResult = EmptyResult; +} +export namespace Network { + export type AuthRequired = { + method: 'network.authRequired'; + params: Network.AuthRequiredParameters; + }; +} export namespace Network { export type AuthRequiredParameters = Network.BaseParameters & { response: Network.ResponseData; }; } +export namespace Network { + export type BeforeRequestSent = { + method: 'network.beforeRequestSent'; + params: Network.BeforeRequestSentParameters; + }; +} export namespace Network { export type BeforeRequestSentParameters = Network.BaseParameters & { initiator?: Network.Initiator; }; } +export namespace Network { + export type FetchError = { + method: 'network.fetchError'; + params: Network.FetchErrorParameters; + }; +} export namespace Network { export type FetchErrorParameters = Network.BaseParameters & { errorText: string; }; } +export namespace Network { + export type ResponseCompleted = { + method: 'network.responseCompleted'; + params: Network.ResponseCompletedParameters; + }; +} export namespace Network { export type ResponseCompletedParameters = Network.BaseParameters & { response: Network.ResponseData; }; } +export namespace Network { + export type ResponseStarted = { + method: 'network.responseStarted'; + params: Network.ResponseStartedParameters; + }; +} export namespace Network { export type ResponseStartedParameters = Network.BaseParameters & { response: Network.ResponseData; @@ -1607,57 +1811,18 @@ export type ScriptCommand = | Script.RemovePreloadScript; export type ScriptResult = | Script.AddPreloadScriptResult + | Script.CallFunctionResult + | Script.DisownResult | Script.EvaluateResult - | Script.GetRealmsResult; -export namespace Network { - export type AuthRequired = { - method: 'network.authRequired'; - params: Network.AuthRequiredParameters; - }; -} -export namespace Network { - export type BeforeRequestSent = { - method: 'network.beforeRequestSent'; - params: Network.BeforeRequestSentParameters; - }; -} -export namespace Network { - export type FetchError = { - method: 'network.fetchError'; - params: Network.FetchErrorParameters; - }; -} -export namespace Network { - export type ResponseCompleted = { - method: 'network.responseCompleted'; - params: Network.ResponseCompletedParameters; - }; -} -export namespace Network { - export type ResponseStarted = { - method: 'network.responseStarted'; - params: Network.ResponseStartedParameters; - }; -} + | Script.GetRealmsResult + | Script.RemovePreloadScriptResult; +export type ScriptEvent = + | Script.Message + | Script.RealmCreated + | Script.RealmDestroyed; export namespace Script { export type Channel = string; } -export namespace Script { - export type EvaluateResultSuccess = { - type: 'success'; - result: Script.RemoteValue; - realm: Script.Realm; - }; -} -export namespace Script { - export type ExceptionDetails = { - columnNumber: JsUint; - exception: Script.RemoteValue; - lineNumber: JsUint; - stackTrace: Script.StackTrace; - text: string; - }; -} export namespace Script { export type ChannelValue = { type: 'channel'; @@ -1676,6 +1841,13 @@ export namespace Script { | Script.EvaluateResultSuccess | Script.EvaluateResultException; } +export namespace Script { + export type EvaluateResultSuccess = { + type: 'success'; + result: Script.RemoteValue; + realm: Script.Realm; + }; +} export namespace Script { export type EvaluateResultException = { type: 'exception'; @@ -1684,13 +1856,19 @@ export namespace Script { }; } export namespace Script { - export type Handle = string; + export type ExceptionDetails = { + columnNumber: JsUint; + exception: Script.RemoteValue; + lineNumber: JsUint; + stackTrace: Script.StackTrace; + text: string; + }; } export namespace Script { - export type InternalId = string; + export type Handle = string; } export namespace Script { - export type ListLocalValue = [...Script.LocalValue[]]; + export type InternalId = string; } export namespace Script { export type LocalValue = @@ -1704,6 +1882,9 @@ export namespace Script { | Script.RegExpLocalValue | Script.SetLocalValue; } +export namespace Script { + export type ListLocalValue = [...Script.LocalValue[]]; +} export namespace Script { export type ArrayLocalValue = { type: 'array'; @@ -1875,12 +2056,21 @@ export namespace Script { | 'worklet'; } export namespace Script { - export type ListRemoteValue = [...Script.RemoteValue[]]; + export type RemoteReference = + | Script.SharedReference + | Script.RemoteObjectReference; } export namespace Script { - export type MappingRemoteValue = [ - ...[Script.RemoteValue | string, Script.RemoteValue][], - ]; + export type SharedReference = { + sharedId: Script.SharedId; + handle?: Script.Handle; + } & Extensible; +} +export namespace Script { + export type RemoteObjectReference = { + handle: Script.Handle; + sharedId?: Script.SharedId; + } & Extensible; } export namespace Script { export type RemoteValue = @@ -1907,21 +2097,12 @@ export namespace Script { | Script.WindowProxyRemoteValue; } export namespace Script { - export type RemoteReference = - | Script.SharedReference - | Script.RemoteObjectReference; -} -export namespace Script { - export type SharedReference = { - sharedId: Script.SharedId; - handle?: Script.Handle; - } & Extensible; + export type ListRemoteValue = [...Script.RemoteValue[]]; } export namespace Script { - export type RemoteObjectReference = { - handle: Script.Handle; - sharedId?: Script.SharedId; - } & Extensible; + export type MappingRemoteValue = [ + ...[Script.RemoteValue | string, Script.RemoteValue][], + ]; } export namespace Script { export type SymbolRemoteValue = { @@ -2183,6 +2364,15 @@ export namespace Script { target: Script.Target; }; } +export namespace Script { + export type DisownResult = EmptyResult; +} +export namespace Script { + export type CallFunction = { + method: 'script.callFunction'; + params: Script.CallFunctionParameters; + }; +} export namespace Script { export type CallFunctionParameters = { functionDeclaration: string; @@ -2199,10 +2389,7 @@ export namespace Script { }; } export namespace Script { - export type CallFunction = { - method: 'script.callFunction'; - params: Script.CallFunctionParameters; - }; + export type CallFunctionResult = Script.EvaluateResult; } export namespace Script { export type Evaluate = { @@ -2251,6 +2438,15 @@ export namespace Script { script: Script.PreloadScript; }; } +export namespace Script { + export type RemovePreloadScriptResult = EmptyResult; +} +export namespace Script { + export type Message = { + method: 'script.message'; + params: Script.MessageParameters; + }; +} export namespace Script { export type MessageParameters = { channel: Script.Channel; @@ -2264,12 +2460,6 @@ export namespace Script { params: Script.RealmInfo; }; } -export namespace Script { - export type Message = { - method: 'script.message'; - params: Script.MessageParameters; - }; -} export namespace Script { export type RealmDestroyed = { method: 'script.realmDestroyed'; @@ -2441,6 +2631,10 @@ export type InputCommand = | Input.PerformActions | Input.ReleaseActions | Input.SetFiles; +export type InputResult = + | Input.PerformActionsResult + | Input.ReleaseActionsResult + | Input.SetFilesResult; export type InputEvent = Input.FileDialogOpened; export namespace Input { export type ElementOrigin = { @@ -2448,12 +2642,25 @@ export namespace Input { element: Script.SharedReference; }; } +export namespace Input { + export type PerformActions = { + method: 'input.performActions'; + params: Input.PerformActionsParameters; + }; +} export namespace Input { export type PerformActionsParameters = { context: BrowsingContext.BrowsingContext; actions: [...Input.SourceActions[]]; }; } +export namespace Input { + export type SourceActions = + | Input.NoneSourceActions + | Input.KeySourceActions + | Input.PointerSourceActions + | Input.WheelSourceActions; +} export namespace Input { export type NoneSourceActions = { type: 'none'; @@ -2461,6 +2668,9 @@ export namespace Input { actions: [...Input.NoneSourceAction[]]; }; } +export namespace Input { + export type NoneSourceAction = Input.PauseAction; +} export namespace Input { export type KeySourceActions = { type: 'key'; @@ -2468,6 +2678,12 @@ export namespace Input { actions: [...Input.KeySourceAction[]]; }; } +export namespace Input { + export type KeySourceAction = + | Input.PauseAction + | Input.KeyDownAction + | Input.KeyUpAction; +} export namespace Input { export type PointerSourceActions = { type: 'pointer'; @@ -2476,28 +2692,6 @@ export namespace Input { actions: [...Input.PointerSourceAction[]]; }; } -export namespace Input { - export type PerformActions = { - method: 'input.performActions'; - params: Input.PerformActionsParameters; - }; -} -export namespace Input { - export type SourceActions = - | Input.NoneSourceActions - | Input.KeySourceActions - | Input.PointerSourceActions - | Input.WheelSourceActions; -} -export namespace Input { - export type NoneSourceAction = Input.PauseAction; -} -export namespace Input { - export type KeySourceAction = - | Input.PauseAction - | Input.KeyDownAction - | Input.KeyUpAction; -} export namespace Input { export const enum PointerType { Mouse = 'mouse', @@ -2513,13 +2707,6 @@ export namespace Input { pointerType?: Input.PointerType; }; } -export namespace Input { - export type WheelSourceActions = { - type: 'wheel'; - id: string; - actions: [...Input.WheelSourceAction[]]; - }; -} export namespace Input { export type PointerSourceAction = | Input.PauseAction @@ -2527,6 +2714,13 @@ export namespace Input { | Input.PointerUpAction | Input.PointerMoveAction; } +export namespace Input { + export type WheelSourceActions = { + type: 'wheel'; + id: string; + actions: [...Input.WheelSourceAction[]]; + }; +} export namespace Input { export type WheelSourceAction = Input.PauseAction | Input.WheelScrollAction; } @@ -2624,6 +2818,9 @@ export namespace Input { export namespace Input { export type Origin = 'viewport' | 'pointer' | Input.ElementOrigin; } +export namespace Input { + export type PerformActionsResult = EmptyResult; +} export namespace Input { export type ReleaseActions = { method: 'input.releaseActions'; @@ -2635,6 +2832,9 @@ export namespace Input { context: BrowsingContext.BrowsingContext; }; } +export namespace Input { + export type ReleaseActionsResult = EmptyResult; +} export namespace Input { export type SetFiles = { method: 'input.setFiles'; @@ -2648,6 +2848,9 @@ export namespace Input { files: [...string[]]; }; } +export namespace Input { + export type SetFilesResult = EmptyResult; +} export namespace Input { export type FileDialogOpened = { method: 'input.fileDialogOpened'; @@ -2662,21 +2865,23 @@ export namespace Input { }; } export type WebExtensionCommand = WebExtension.Install | WebExtension.Uninstall; -export type WebExtensionResult = WebExtension.InstallResult; +export type WebExtensionResult = + | WebExtension.InstallResult + | WebExtension.UninstallResult; export namespace WebExtension { export type Extension = string; } -export namespace WebExtension { - export type InstallParameters = { - extensionData: WebExtension.ExtensionData; - }; -} export namespace WebExtension { export type Install = { method: 'webExtension.install'; params: WebExtension.InstallParameters; }; } +export namespace WebExtension { + export type InstallParameters = { + extensionData: WebExtension.ExtensionData; + }; +} export namespace WebExtension { export type ExtensionData = | WebExtension.ExtensionArchivePath @@ -2717,3 +2922,6 @@ export namespace WebExtension { extension: WebExtension.Extension; }; } +export namespace WebExtension { + export type UninstallResult = EmptyResult; +} diff --git a/packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts b/packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts index c325e2d99882a..6f4df4dd12165 100644 --- a/packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts +++ b/packages/playwright-core/src/server/bidi/third_party/bidiSerializer.ts @@ -9,7 +9,7 @@ import type * as Bidi from './bidiProtocol'; -/* eslint-disable curly, indent */ +/* eslint-disable curly */ /** * @internal @@ -123,7 +123,7 @@ export class BidiSerializer { } throw new UnserializableError( - 'Custom object serialization not possible. Use plain objects instead.' + 'Custom object serialization not possible. Use plain objects instead.' ); } } diff --git a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts index af6617098b0ba..5139f0266c897 100644 --- a/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts +++ b/packages/playwright-core/src/server/bidi/third_party/firefoxPrefs.ts @@ -9,7 +9,7 @@ import fs from 'fs'; import path from 'path'; -/* eslint-disable curly, indent */ +/* eslint-disable curly */ interface ProfileOptions { preferences: Record; @@ -291,14 +291,14 @@ async function writePreferences(options: ProfileOptions): Promise { fs.promises.writeFile(path.join(options.path, 'user.js'), lines.join('\n')), // Create a backup of the preferences file if it already exitsts. fs.promises.access(prefsPath, fs.constants.F_OK).then( - async () => { - await fs.promises.copyFile( - prefsPath, - path.join(options.path, 'prefs.js.playwright') - ); - }, - // Swallow only if file does not exist - () => {} + async () => { + await fs.promises.copyFile( + prefsPath, + path.join(options.path, 'prefs.js.playwright') + ); + }, + // Swallow only if file does not exist + () => {} ), ]); for (const command of result) { diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index 4f6491e11cfea..f8b86e40ca6c3 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -36,7 +36,6 @@ import { RecentLogsCollector } from './utils/debugLogger'; import type { Browser, BrowserOptions, BrowserProcess } from './browser'; import type { BrowserContext } from './browserContext'; -import type { Env } from './utils/processLauncher'; import type { Progress } from './progress'; import type { ProtocolError } from './protocolError'; import type { BrowserName } from './registry'; @@ -175,9 +174,9 @@ export abstract class BrowserType extends SdkObject { if (ignoreAllDefaultArgs) browserArguments.push(...args); else if (ignoreDefaultArgs) - browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1)); + browserArguments.push(...(await this.defaultArgs(options, isPersistent, userDataDir)).filter(arg => ignoreDefaultArgs.indexOf(arg) === -1)); else - browserArguments.push(...this.defaultArgs(options, isPersistent, userDataDir)); + browserArguments.push(...await this.defaultArgs(options, isPersistent, userDataDir)); let executable: string; if (executablePath) { @@ -213,7 +212,7 @@ export abstract class BrowserType extends SdkObject { const { launchedProcess, gracefullyClose, kill } = await launchProcess({ command: prepared.executable, args: prepared.browserArguments, - env: this.amendEnvironment(env, prepared.userDataDir, isPersistent), + env: this.amendEnvironment(env, prepared.userDataDir, isPersistent, options), handleSIGINT, handleSIGTERM, handleSIGHUP, @@ -339,9 +338,9 @@ export abstract class BrowserType extends SdkObject { return options.channel || this._name; } - abstract defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[]; + abstract defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): Promise; abstract connectToTransport(transport: ConnectionTransport, options: BrowserOptions, browserLogsCollector: RecentLogsCollector): Promise; - abstract amendEnvironment(env: Env, userDataDir: string, isPersistent: boolean): Env; + abstract amendEnvironment(env: NodeJS.ProcessEnv, userDataDir: string, isPersistent: boolean, options: types.LaunchOptions): NodeJS.ProcessEnv; abstract doRewriteStartupLog(error: ProtocolError): ProtocolError; abstract attemptToGracefullyCloseBrowser(transport: ConnectionTransport): void; } diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 60d9ccdbe3cb3..3db67148c89fb 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -41,7 +41,6 @@ import { gracefullyCloseSet } from '../utils/processLauncher'; import type { HTTPRequestParams } from '../utils/network'; import type { BrowserOptions, BrowserProcess } from '../browser'; import type { SdkObject } from '../instrumentation'; -import type { Env } from '../utils/processLauncher'; import type { Progress } from '../progress'; import type { ProtocolError } from '../protocolError'; import type { ConnectionTransport, ProtocolRequest } from '../transport'; @@ -165,7 +164,7 @@ export class Chromium extends BrowserType { return error; } - override amendEnvironment(env: Env): Env { + override amendEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { return env; } @@ -281,7 +280,7 @@ export class Chromium extends BrowserType { } } - override defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[] { + override async defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string) { const chromeArguments = this._innerDefaultArgs(options); chromeArguments.push(`--user-data-dir=${userDataDir}`); if (options.cdpPort !== undefined) diff --git a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts index cedf70082c0f8..75a715aea7312 100644 --- a/packages/playwright-core/src/server/chromium/chromiumSwitches.ts +++ b/packages/playwright-core/src/server/chromium/chromiumSwitches.ts @@ -40,6 +40,8 @@ const disabledFeatures = (assistantMode?: boolean) => [ 'Translate', // See https://issues.chromium.org/u/1/issues/435410220 'AutoDeElevate', + // See https://github.com/microsoft/playwright/issues/37714 + 'RenderDocument', assistantMode ? 'AutomationControlled' : '', ].filter(Boolean); @@ -58,7 +60,7 @@ export const chromiumSwitches = (assistantMode?: boolean, channel?: string) => [ '--disable-dev-shm-usage', '--disable-extensions', '--disable-features=' + disabledFeatures(assistantMode).join(','), - channel === 'chromium-tip-of-tree' ? '--enable-features=CDPScreenshotNewSurface' : '', + process.env.PLAYWRIGHT_LEGACY_SCREENSHOT ? '' : '--enable-features=CDPScreenshotNewSurface', '--allow-pre-commit-input', '--disable-hang-monitor', '--disable-ipc-flooding-protection', diff --git a/packages/playwright-core/src/server/chromium/crBrowser.ts b/packages/playwright-core/src/server/chromium/crBrowser.ts index decbf3a86167f..ef6f16e7c84a2 100644 --- a/packages/playwright-core/src/server/chromium/crBrowser.ts +++ b/packages/playwright-core/src/server/chromium/crBrowser.ts @@ -46,7 +46,6 @@ export class CRBrowser extends Browser { private _clientRootSessionPromise: Promise | null = null; readonly _contexts = new Map(); _crPages = new Map(); - _backgroundPages = new Map(); _serviceWorkers = new Map(); _devtools?: CRDevTools; private _version = ''; @@ -151,7 +150,7 @@ export class CRBrowser extends Browser { await Promise.all([...this._crPages.values()].map(crPage => crPage._page.waitForInitializedOrError())); } - _onAttachedToTarget({ targetInfo, sessionId }: Protocol.Target.attachedToTargetPayload) { + _onAttachedToTarget({ targetInfo, sessionId, waitingForDebugger }: Protocol.Target.attachedToTargetPayload) { if (targetInfo.type === 'browser') return; const session = this._session.createChildSession(sessionId); @@ -176,18 +175,11 @@ export class CRBrowser extends Browser { } assert(!this._crPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId); - assert(!this._backgroundPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId); assert(!this._serviceWorkers.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId); - if (targetInfo.type === 'background_page') { - const backgroundPage = new CRPage(session, targetInfo.targetId, context, null, { hasUIWindow: false, isBackgroundPage: true }); - this._backgroundPages.set(targetInfo.targetId, backgroundPage); - return; - } - if (targetInfo.type === 'page' || treatOtherAsPage) { const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null; - const crPage = new CRPage(session, targetInfo.targetId, context, opener, { hasUIWindow: targetInfo.type === 'page', isBackgroundPage: false }); + const crPage = new CRPage(session, targetInfo.targetId, context, opener, { hasUIWindow: targetInfo.type === 'page' }); this._crPages.set(targetInfo.targetId, crPage); return; } @@ -215,12 +207,6 @@ export class CRBrowser extends Browser { crPage.didClose(); return; } - const backgroundPage = this._backgroundPages.get(targetId); - if (backgroundPage) { - this._backgroundPages.delete(targetId); - backgroundPage.didClose(); - return; - } const serviceWorker = this._serviceWorkers.get(targetId); if (serviceWorker) { this._serviceWorkers.delete(targetId); @@ -233,9 +219,6 @@ export class CRBrowser extends Browser { for (const crPage of this._crPages.values()) crPage.didClose(); this._crPages.clear(); - for (const backgroundPage of this._backgroundPages.values()) - backgroundPage.didClose(); - this._backgroundPages.clear(); for (const serviceWorker of this._serviceWorkers.values()) serviceWorker.didClose(); this._serviceWorkers.clear(); @@ -337,7 +320,6 @@ export class CRBrowser extends Browser { export class CRBrowserContext extends BrowserContext { static CREvents = { - BackgroundPage: 'backgroundpage', ServiceWorker: 'serviceworker', }; @@ -566,14 +548,6 @@ export class CRBrowserContext extends BrowserContext { } onClosePersistent() { - // When persistent context is closed, we do not necessary get Target.detachedFromTarget - // for all the background pages. - for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) { - if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined()) { - backgroundPage.didClose(); - this._browser._backgroundPages.delete(targetId); - } - } } override async clearCache(): Promise { @@ -591,15 +565,6 @@ export class CRBrowserContext extends BrowserContext { }); } - backgroundPages(): Page[] { - const result: Page[] = []; - for (const backgroundPage of this._browser._backgroundPages.values()) { - if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined()) - result.push(backgroundPage._page); - } - return result; - } - serviceWorkers(): Worker[] { return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker.browserContext === this); } diff --git a/packages/playwright-core/src/server/chromium/crCoverage.ts b/packages/playwright-core/src/server/chromium/crCoverage.ts index cf68dd59f45ef..8946754c9b4ed 100644 --- a/packages/playwright-core/src/server/chromium/crCoverage.ts +++ b/packages/playwright-core/src/server/chromium/crCoverage.ts @@ -238,9 +238,9 @@ class CSSCoverage { } function convertToDisjointRanges(nestedRanges: { - startOffset: number; - endOffset: number; - count: number; }[]): { start: number; end: number; }[] { + startOffset: number; + endOffset: number; + count: number; }[]): { start: number; end: number; }[] { const points = []; for (const range of nestedRanges) { points.push({ offset: range.startOffset, type: 0, range }); diff --git a/packages/playwright-core/src/server/chromium/crPage.ts b/packages/playwright-core/src/server/chromium/crPage.ts index c019cac28b726..43f810ebd6df4 100644 --- a/packages/playwright-core/src/server/chromium/crPage.ts +++ b/packages/playwright-core/src/server/chromium/crPage.ts @@ -39,7 +39,6 @@ import { CRPDF } from './crPdf'; import { exceptionToError, releaseObject, toConsoleMessageLocation } from './crProtocolHelper'; import { platformToFontFamilies } from './defaultFontFamilies'; import { VideoRecorder } from './videoRecorder'; -import { BrowserContext } from '../browserContext'; import { TargetClosedError } from '../errors'; import { isSessionClosedError } from '../protocolError'; @@ -68,7 +67,6 @@ export class CRPage implements PageDelegate { private readonly _pdf: CRPDF; private readonly _coverage: CRCoverage; readonly _browserContext: CRBrowserContext; - private _isBackgroundPage: boolean; // Holds window features for the next popup being opened via window.open, // until the popup target arrives. This could be racy if two oopifs @@ -82,10 +80,9 @@ export class CRPage implements PageDelegate { return crPage._mainFrameSession; } - constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, bits: { hasUIWindow: boolean, isBackgroundPage: boolean }) { + constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, bits: { hasUIWindow: boolean }) { this._targetId = targetId; this._opener = opener; - this._isBackgroundPage = bits.isBackgroundPage; const dragManager = new DragManager(this); this.rawKeyboard = new RawKeyboardImpl(client, browserContext._browser._platform() === 'mac', dragManager); this.rawMouse = new RawMouseImpl(this, client, dragManager); @@ -113,10 +110,9 @@ export class CRPage implements PageDelegate { this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize }); } - const createdEvent = this._isBackgroundPage ? CRBrowserContext.CREvents.BackgroundPage : BrowserContext.Events.Page; this._mainFrameSession._initialize(bits.hasUIWindow).then( - () => this._page.reportAsNew(this._opener?._page, undefined, createdEvent), - error => this._page.reportAsNew(this._opener?._page, error, createdEvent)); + () => this._page.reportAsNew(this._opener?._page, undefined), + error => this._page.reportAsNew(this._opener?._page, error)); } private async _forAllFrameSessions(cb: (frame: FrameSession) => Promise) { @@ -394,6 +390,7 @@ class FrameSession { private _metricsOverride: Protocol.Emulation.setDeviceMetricsOverrideParameters | undefined; private _workerSessions = new Map(); private _initScriptIds = new Map(); + private _bufferedAttachedToTargetEvents: Protocol.Target.attachedToTargetPayload[] | undefined; constructor(crPage: CRPage, client: CRSession, targetId: string, parentSession: FrameSession | null) { this._client = client; @@ -432,13 +429,13 @@ class FrameSession { eventsHelper.addEventListener(this._client, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)), eventsHelper.addEventListener(this._client, 'Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)), eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()), - eventsHelper.addEventListener(this._client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)), - eventsHelper.addEventListener(this._client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)), ]); } private _addBrowserListeners() { this._eventListeners.push(...[ + eventsHelper.addEventListener(this._client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)), + eventsHelper.addEventListener(this._client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)), eventsHelper.addEventListener(this._client, 'Inspector.targetCrashed', event => this._onTargetCrashed()), eventsHelper.addEventListener(this._client, 'Page.screencastFrame', event => this._onScreencastFrame(event)), eventsHelper.addEventListener(this._client, 'Page.windowOpen', event => this._onWindowOpen(event)), @@ -476,6 +473,13 @@ class FrameSession { if (!this._isMainFrame()) this._addRendererListeners(); this._addBrowserListeners(); + + // Buffer attachedToTarget events until we receive the frame tree. + // This way we'll know where to insert oopif targets in the frame hierarchy. + // Note that we cannot send Target.setAutoAttach after Runtime.runIfWaitingForDebugger, + // so we have to buffer events instead. + this._bufferedAttachedToTargetEvents = []; + const promises: Promise[] = [ this._client.send('Page.enable'), this._client.send('Page.getFrameTree').then(({ frameTree }) => { @@ -484,6 +488,12 @@ class FrameSession { this._addRendererListeners(); } + // Now that we have the frame tree, it is possible to insert oopif targets at the right place. + const attachedToTargetEvents = this._bufferedAttachedToTargetEvents || []; + this._bufferedAttachedToTargetEvents = undefined; + for (const event of attachedToTargetEvents) + this._onAttachedToTarget(event); + const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)!]; for (const frame of localFrames) { // Note: frames might be removed before we send these. @@ -515,16 +525,7 @@ class FrameSession { worldName: this._crPage.utilityWorldName, }), this._crPage._networkManager.addSession(this._client, undefined, this._isMainFrame()), - this._client.send('Target.setAutoAttach', { - autoAttach: true, - waitForDebuggerOnStart: true, - flatten: true, - filter: [ - { type: 'iframe' }, - { type: 'worker' }, - { type: 'service_worker', exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS } - ] - }), + this._client.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }), ]; if (!this._page.isStorageStatePage) { if (this._crPage._browserContext.needsPlaywrightBinding()) @@ -708,12 +709,22 @@ class FrameSession { } _onAttachedToTarget(event: Protocol.Target.attachedToTargetPayload) { + if (this._bufferedAttachedToTargetEvents) { + this._bufferedAttachedToTargetEvents.push(event); + return; + } + const session = this._client.createChildSession(event.sessionId); if (event.targetInfo.type === 'iframe') { // Frame id equals target id. const targetId = event.targetInfo.targetId; - const frame = this._page.frameManager.frame(targetId); + let frame = this._page.frameManager.frame(targetId); + if (!frame && event.targetInfo.parentFrameId) { + // When connecting to an existing page with an iframe, there is an "iframe" target, + // but no local frame is reported in getFrameTree. We can create a remote frame here. + frame = this._page.frameManager.frameAttached(targetId, event.targetInfo.parentFrameId); + } if (!frame) return; // Subtree may be already gone due to renderer/browser race. this._page.frameManager.removeChildFramesRecursively(frame); @@ -744,22 +755,14 @@ class FrameSession { // TODO: attribute workers to the right frame. this._crPage._networkManager.addSession(session, this._page.frameManager.frame(this._targetId) ?? undefined).catch(() => {}); session._sendMayFail('Runtime.runIfWaitingForDebugger'); - session._sendMayFail('Target.setAutoAttach', { - autoAttach: true, - waitForDebuggerOnStart: true, - flatten: true, - filter: [ - { type: 'worker' }, - { type: 'service_worker', exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS } - ] - }); + session._sendMayFail('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); session.on('Target.attachedToTarget', event => this._onAttachedToTarget(event)); session.on('Target.detachedFromTarget', event => this._onDetachedFromTarget(event)); session.on('Runtime.consoleAPICalled', event => { const args = event.args.map(o => createHandle(worker.existingExecutionContext!, o)); this._page.addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace)); }); - session.on('Runtime.exceptionThrown', exception => this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exception.exceptionDetails), this._page)); + session.on('Runtime.exceptionThrown', exception => this._page.addPageError(exceptionToError(exception.exceptionDetails))); } _onDetachedFromTarget(event: Protocol.Target.detachedFromTargetPayload) { @@ -849,7 +852,7 @@ class FrameSession { } _handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) { - this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exceptionDetails), this._page); + this._page.addPageError(exceptionToError(exceptionDetails)); } async _onTargetCrashed() { diff --git a/packages/playwright-core/src/server/chromium/protocol.d.ts b/packages/playwright-core/src/server/chromium/protocol.d.ts index 1fab022bfb268..5d3c9d26c1308 100644 --- a/packages/playwright-core/src/server/chromium/protocol.d.ts +++ b/packages/playwright-core/src/server/chromium/protocol.d.ts @@ -1050,7 +1050,7 @@ registrations being ignored. */ propertyValue?: string; } - export type UserReidentificationIssueType = "BlockedFrameNavigation"|"BlockedSubresource"; + export type UserReidentificationIssueType = "BlockedFrameNavigation"|"BlockedSubresource"|"NoisedCanvasReadback"; /** * This issue warns about uses of APIs that may be considered misuse to re-identify users. @@ -1061,6 +1061,10 @@ re-identify users. * Applies to BlockedFrameNavigation and BlockedSubresource issue types. */ request?: AffectedRequest; + /** + * Applies to NoisedCanvasReadback issue type. + */ + sourceCodeLocation?: SourceCodeLocation; } /** * A unique identifier for the type of issue. Each type may use one of the @@ -1197,125 +1201,6 @@ using Audits.issueAdded event. } } - /** - * Defines commands and events for browser extensions. - */ - export module Extensions { - /** - * Storage areas. - */ - export type StorageArea = "session"|"local"|"sync"|"managed"; - - - /** - * Installs an unpacked extension from the filesystem similar to ---load-extension CLI flags. Returns extension ID once the extension -has been installed. Available if the client is connected using the ---remote-debugging-pipe flag and the --enable-unsafe-extension-debugging -flag is set. - */ - export type loadUnpackedParameters = { - /** - * Absolute file path. - */ - path: string; - } - export type loadUnpackedReturnValue = { - /** - * Extension id. - */ - id: string; - } - /** - * Uninstalls an unpacked extension (others not supported) from the profile. -Available if the client is connected using the --remote-debugging-pipe flag -and the --enable-unsafe-extension-debugging. - */ - export type uninstallParameters = { - /** - * Extension id. - */ - id: string; - } - export type uninstallReturnValue = { - } - /** - * Gets data from extension storage in the given `storageArea`. If `keys` is -specified, these are used to filter the result. - */ - export type getStorageItemsParameters = { - /** - * ID of extension. - */ - id: string; - /** - * StorageArea to retrieve data from. - */ - storageArea: StorageArea; - /** - * Keys to retrieve. - */ - keys?: string[]; - } - export type getStorageItemsReturnValue = { - data: { [key: string]: string }; - } - /** - * Removes `keys` from extension storage in the given `storageArea`. - */ - export type removeStorageItemsParameters = { - /** - * ID of extension. - */ - id: string; - /** - * StorageArea to remove data from. - */ - storageArea: StorageArea; - /** - * Keys to remove. - */ - keys: string[]; - } - export type removeStorageItemsReturnValue = { - } - /** - * Clears extension storage in the given `storageArea`. - */ - export type clearStorageItemsParameters = { - /** - * ID of extension. - */ - id: string; - /** - * StorageArea to remove data from. - */ - storageArea: StorageArea; - } - export type clearStorageItemsReturnValue = { - } - /** - * Sets `values` in extension storage in the given `storageArea`. The provided `values` -will be merged with existing values in the storage area. - */ - export type setStorageItemsParameters = { - /** - * ID of extension. - */ - id: string; - /** - * StorageArea to set data in. - */ - storageArea: StorageArea; - /** - * Values to set. - */ - values: { [key: string]: string }; - } - export type setStorageItemsReturnValue = { - } - } - /** * Defines commands and events for Autofill. */ @@ -1580,4404 +1465,4669 @@ events afterwards if enabled and recording. } /** - * The Browser domain defines methods and events for browser managing. + * This domain allows configuring virtual Bluetooth devices to test +the web-bluetooth API. */ - export module Browser { - export type BrowserContextID = string; - export type WindowID = number; + export module BluetoothEmulation { /** - * The state of the browser window. + * Indicates the various states of Central. */ - export type WindowState = "normal"|"minimized"|"maximized"|"fullscreen"; + export type CentralState = "absent"|"powered-off"|"powered-on"; /** - * Browser window bounds information + * Indicates the various types of GATT event. */ - export interface Bounds { - /** - * The offset from the left edge of the screen to the window in pixels. - */ - left?: number; - /** - * The offset from the top edge of the screen to the window in pixels. - */ - top?: number; - /** - * The window width in pixels. - */ - width?: number; - /** - * The window height in pixels. - */ - height?: number; - /** - * The window state. Default to normal. - */ - windowState?: WindowState; - } - export type PermissionType = "ar"|"audioCapture"|"automaticFullscreen"|"backgroundFetch"|"backgroundSync"|"cameraPanTiltZoom"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"geolocation"|"handTracking"|"idleDetection"|"keyboardLock"|"localFonts"|"localNetworkAccess"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"pointerLock"|"protectedMediaIdentifier"|"sensors"|"smartCard"|"speakerSelection"|"storageAccess"|"topLevelStorageAccess"|"videoCapture"|"vr"|"wakeLockScreen"|"wakeLockSystem"|"webAppInstallation"|"webPrinting"|"windowManagement"; - export type PermissionSetting = "granted"|"denied"|"prompt"; + export type GATTOperationType = "connection"|"discovery"; /** - * Definition of PermissionDescriptor defined in the Permissions API: -https://w3c.github.io/permissions/#dom-permissiondescriptor. + * Indicates the various types of characteristic write. */ - export interface PermissionDescriptor { - /** - * Name of permission. -See https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/permissions/permission_descriptor.idl for valid permission names. - */ - name: string; - /** - * For "midi" permission, may also specify sysex control. - */ - sysex?: boolean; - /** - * For "push" permission, may specify userVisibleOnly. -Note that userVisibleOnly = true is the only currently supported type. - */ - userVisibleOnly?: boolean; - /** - * For "clipboard" permission, may specify allowWithoutSanitization. - */ - allowWithoutSanitization?: boolean; - /** - * For "fullscreen" permission, must specify allowWithoutGesture:true. - */ - allowWithoutGesture?: boolean; - /** - * For "camera" permission, may specify panTiltZoom. - */ - panTiltZoom?: boolean; - } + export type CharacteristicWriteType = "write-default-deprecated"|"write-with-response"|"write-without-response"; /** - * Browser command ids used by executeBrowserCommand. + * Indicates the various types of characteristic operation. */ - export type BrowserCommandId = "openTabSearch"|"closeTabSearch"|"openGlic"; + export type CharacteristicOperationType = "read"|"write"|"subscribe-to-notifications"|"unsubscribe-from-notifications"; /** - * Chrome histogram bucket. + * Indicates the various types of descriptor operation. */ - export interface Bucket { - /** - * Minimum value (inclusive). - */ - low: number; + export type DescriptorOperationType = "read"|"write"; + /** + * Stores the manufacturer data + */ + export interface ManufacturerData { /** - * Maximum value (exclusive). + * Company identifier +https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml +https://usb.org/developers */ - high: number; + key: number; /** - * Number of samples. + * Manufacturer-specific data */ - count: number; + data: binary; } /** - * Chrome histogram. + * Stores the byte data of the advertisement packet sent by a Bluetooth device. */ - export interface Histogram { - /** - * Name. - */ - name: string; + export interface ScanRecord { + name?: string; + uuids?: string[]; /** - * Sum of sample values. + * Stores the external appearance description of the device. */ - sum: number; + appearance?: number; /** - * Total number of samples. + * Stores the transmission power of a broadcasting device. */ - count: number; + txPower?: number; /** - * Buckets. + * Key is the company identifier and the value is an array of bytes of +manufacturer specific data. */ - buckets: Bucket[]; + manufacturerData?: ManufacturerData[]; } - export type PrivacySandboxAPI = "BiddingAndAuctionServices"|"TrustedKeyValue"; - /** - * Fired when page is about to start a download. + * Stores the advertisement packet information that is sent by a Bluetooth device. */ - export type downloadWillBeginPayload = { - /** - * Id of the frame that caused the download to begin. - */ - frameId: Page.FrameId; - /** - * Global unique identifier of the download. - */ - guid: string; - /** - * URL of the resource being downloaded. - */ - url: string; - /** - * Suggested file name of the resource (the actual name of the file saved on disk may differ). - */ - suggestedFilename: string; + export interface ScanEntry { + deviceAddress: string; + rssi: number; + scanRecord: ScanRecord; } /** - * Fired when download makes progress. Last call has |done| == true. + * Describes the properties of a characteristic. This follows Bluetooth Core +Specification BT 4.2 Vol 3 Part G 3.3.1. Characteristic Properties. */ - export type downloadProgressPayload = { - /** - * Global unique identifier of the download. - */ - guid: string; - /** - * Total expected bytes to download. - */ - totalBytes: number; - /** - * Total bytes received. - */ - receivedBytes: number; - /** - * Download status. - */ - state: "inProgress"|"completed"|"canceled"; - /** - * If download is "completed", provides the path of the downloaded file. -Depending on the platform, it is not guaranteed to be set, nor the file -is guaranteed to exist. - */ - filePath?: string; + export interface CharacteristicProperties { + broadcast?: boolean; + read?: boolean; + writeWithoutResponse?: boolean; + write?: boolean; + notify?: boolean; + indicate?: boolean; + authenticatedSignedWrites?: boolean; + extendedProperties?: boolean; } /** - * Set permission settings for given origin. + * Event for when a GATT operation of |type| to the peripheral with |address| +happened. */ - export type setPermissionParameters = { - /** - * Descriptor of permission to override. - */ - permission: PermissionDescriptor; - /** - * Setting of the permission. - */ - setting: PermissionSetting; - /** - * Origin the permission applies to, all origins if not specified. - */ - origin?: string; - /** - * Context to override. When omitted, default browser context is used. - */ - browserContextId?: BrowserContextID; + export type gattOperationReceivedPayload = { + address: string; + type: GATTOperationType; } - export type setPermissionReturnValue = { + /** + * Event for when a characteristic operation of |type| to the characteristic +respresented by |characteristicId| happened. |data| and |writeType| is +expected to exist when |type| is write. + */ + export type characteristicOperationReceivedPayload = { + characteristicId: string; + type: CharacteristicOperationType; + data?: binary; + writeType?: CharacteristicWriteType; } /** - * Grant specific permissions to the given origin and reject all others. + * Event for when a descriptor operation of |type| to the descriptor +respresented by |descriptorId| happened. |data| is expected to exist when +|type| is write. */ - export type grantPermissionsParameters = { - permissions: PermissionType[]; + export type descriptorOperationReceivedPayload = { + descriptorId: string; + type: DescriptorOperationType; + data?: binary; + } + + /** + * Enable the BluetoothEmulation domain. + */ + export type enableParameters = { /** - * Origin the permission applies to, all origins if not specified. + * State of the simulated central. */ - origin?: string; + state: CentralState; /** - * BrowserContext to override permissions. When omitted, default browser context is used. + * If the simulated central supports low-energy. */ - browserContextId?: BrowserContextID; + leSupported: boolean; } - export type grantPermissionsReturnValue = { + export type enableReturnValue = { } /** - * Reset all permission management for all origins. + * Set the state of the simulated central. */ - export type resetPermissionsParameters = { + export type setSimulatedCentralStateParameters = { /** - * BrowserContext to reset permissions. When omitted, default browser context is used. + * State of the simulated central. */ - browserContextId?: BrowserContextID; + state: CentralState; } - export type resetPermissionsReturnValue = { + export type setSimulatedCentralStateReturnValue = { } /** - * Set the behavior when downloading a file. + * Disable the BluetoothEmulation domain. */ - export type setDownloadBehaviorParameters = { - /** - * Whether to allow all or deny all download requests, or use default Chrome behavior if -available (otherwise deny). |allowAndName| allows download and names files according to -their download guids. - */ - behavior: "deny"|"allow"|"allowAndName"|"default"; - /** - * BrowserContext to set download behavior. When omitted, default browser context is used. - */ - browserContextId?: BrowserContextID; - /** - * The default path to save downloaded files to. This is required if behavior is set to 'allow' -or 'allowAndName'. - */ - downloadPath?: string; - /** - * Whether to emit download events (defaults to false). - */ - eventsEnabled?: boolean; + export type disableParameters = { } - export type setDownloadBehaviorReturnValue = { + export type disableReturnValue = { } /** - * Cancel a download if in progress + * Simulates a peripheral with |address|, |name| and |knownServiceUuids| +that has already been connected to the system. */ - export type cancelDownloadParameters = { - /** - * Global unique identifier of the download. - */ - guid: string; - /** - * BrowserContext to perform the action in. When omitted, default browser context is used. - */ - browserContextId?: BrowserContextID; + export type simulatePreconnectedPeripheralParameters = { + address: string; + name: string; + manufacturerData: ManufacturerData[]; + knownServiceUuids: string[]; } - export type cancelDownloadReturnValue = { + export type simulatePreconnectedPeripheralReturnValue = { } /** - * Close browser gracefully. + * Simulates an advertisement packet described in |entry| being received by +the central. */ - export type closeParameters = { + export type simulateAdvertisementParameters = { + entry: ScanEntry; } - export type closeReturnValue = { + export type simulateAdvertisementReturnValue = { } /** - * Crashes browser on the main thread. + * Simulates the response code from the peripheral with |address| for a +GATT operation of |type|. The |code| value follows the HCI Error Codes from +Bluetooth Core Specification Vol 2 Part D 1.3 List Of Error Codes. */ - export type crashParameters = { + export type simulateGATTOperationResponseParameters = { + address: string; + type: GATTOperationType; + code: number; } - export type crashReturnValue = { + export type simulateGATTOperationResponseReturnValue = { } /** - * Crashes GPU process. + * Simulates the response from the characteristic with |characteristicId| for a +characteristic operation of |type|. The |code| value follows the Error +Codes from Bluetooth Core Specification Vol 3 Part F 3.4.1.1 Error Response. +The |data| is expected to exist when simulating a successful read operation +response. */ - export type crashGpuProcessParameters = { + export type simulateCharacteristicOperationResponseParameters = { + characteristicId: string; + type: CharacteristicOperationType; + code: number; + data?: binary; } - export type crashGpuProcessReturnValue = { + export type simulateCharacteristicOperationResponseReturnValue = { } /** - * Returns version information. + * Simulates the response from the descriptor with |descriptorId| for a +descriptor operation of |type|. The |code| value follows the Error +Codes from Bluetooth Core Specification Vol 3 Part F 3.4.1.1 Error Response. +The |data| is expected to exist when simulating a successful read operation +response. */ - export type getVersionParameters = { + export type simulateDescriptorOperationResponseParameters = { + descriptorId: string; + type: DescriptorOperationType; + code: number; + data?: binary; } - export type getVersionReturnValue = { - /** - * Protocol version. - */ - protocolVersion: string; - /** - * Product name. - */ - product: string; - /** - * Product revision. - */ - revision: string; - /** - * User-Agent. - */ - userAgent: string; - /** - * V8 version. - */ - jsVersion: string; + export type simulateDescriptorOperationResponseReturnValue = { } /** - * Returns the command line switches for the browser process if, and only if ---enable-automation is on the commandline. + * Adds a service with |serviceUuid| to the peripheral with |address|. */ - export type getBrowserCommandLineParameters = { + export type addServiceParameters = { + address: string; + serviceUuid: string; } - export type getBrowserCommandLineReturnValue = { + export type addServiceReturnValue = { /** - * Commandline parameters + * An identifier that uniquely represents this service. */ - arguments: string[]; + serviceId: string; } /** - * Get Chrome histograms. + * Removes the service respresented by |serviceId| from the simulated central. */ - export type getHistogramsParameters = { - /** - * Requested substring in name. Only histograms which have query as a -substring in their name are extracted. An empty or absent query returns -all histograms. - */ - query?: string; - /** - * If true, retrieve delta since last delta call. - */ - delta?: boolean; + export type removeServiceParameters = { + serviceId: string; } - export type getHistogramsReturnValue = { - /** - * Histograms. - */ - histograms: Histogram[]; + export type removeServiceReturnValue = { } /** - * Get a Chrome histogram by name. + * Adds a characteristic with |characteristicUuid| and |properties| to the +service represented by |serviceId|. */ - export type getHistogramParameters = { - /** - * Requested histogram name. - */ - name: string; - /** - * If true, retrieve delta since last delta call. - */ - delta?: boolean; + export type addCharacteristicParameters = { + serviceId: string; + characteristicUuid: string; + properties: CharacteristicProperties; } - export type getHistogramReturnValue = { + export type addCharacteristicReturnValue = { /** - * Histogram. + * An identifier that uniquely represents this characteristic. */ - histogram: Histogram; + characteristicId: string; } /** - * Get position and size of the browser window. + * Removes the characteristic respresented by |characteristicId| from the +simulated central. */ - export type getWindowBoundsParameters = { - /** - * Browser window id. - */ - windowId: WindowID; + export type removeCharacteristicParameters = { + characteristicId: string; } - export type getWindowBoundsReturnValue = { - /** - * Bounds information of the window. When window state is 'minimized', the restored window -position and size are returned. - */ - bounds: Bounds; + export type removeCharacteristicReturnValue = { } /** - * Get the browser window that contains the devtools target. + * Adds a descriptor with |descriptorUuid| to the characteristic respresented +by |characteristicId|. */ - export type getWindowForTargetParameters = { - /** - * Devtools agent host id. If called as a part of the session, associated targetId is used. - */ - targetId?: Target.TargetID; + export type addDescriptorParameters = { + characteristicId: string; + descriptorUuid: string; } - export type getWindowForTargetReturnValue = { - /** - * Browser window id. - */ - windowId: WindowID; + export type addDescriptorReturnValue = { /** - * Bounds information of the window. When window state is 'minimized', the restored window -position and size are returned. + * An identifier that uniquely represents this descriptor. */ - bounds: Bounds; + descriptorId: string; } /** - * Set position and/or size of the browser window. + * Removes the descriptor with |descriptorId| from the simulated central. */ - export type setWindowBoundsParameters = { - /** - * Browser window id. - */ - windowId: WindowID; - /** - * New window bounds. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined -with 'left', 'top', 'width' or 'height'. Leaves unspecified fields unchanged. - */ - bounds: Bounds; + export type removeDescriptorParameters = { + descriptorId: string; } - export type setWindowBoundsReturnValue = { + export type removeDescriptorReturnValue = { } /** - * Set size of the browser contents resizing browser window as necessary. + * Simulates a GATT disconnection from the peripheral with |address|. */ - export type setContentsSizeParameters = { + export type simulateGATTDisconnectionParameters = { + address: string; + } + export type simulateGATTDisconnectionReturnValue = { + } + } + + /** + * The Browser domain defines methods and events for browser managing. + */ + export module Browser { + export type BrowserContextID = string; + export type WindowID = number; + /** + * The state of the browser window. + */ + export type WindowState = "normal"|"minimized"|"maximized"|"fullscreen"; + /** + * Browser window bounds information + */ + export interface Bounds { /** - * Browser window id. + * The offset from the left edge of the screen to the window in pixels. */ - windowId: WindowID; + left?: number; /** - * The window contents width in DIP. Assumes current width if omitted. -Must be specified if 'height' is omitted. + * The offset from the top edge of the screen to the window in pixels. + */ + top?: number; + /** + * The window width in pixels. */ width?: number; /** - * The window contents height in DIP. Assumes current height if omitted. -Must be specified if 'width' is omitted. + * The window height in pixels. */ height?: number; - } - export type setContentsSizeReturnValue = { - } - /** - * Set dock tile details, platform-specific. - */ - export type setDockTileParameters = { - badgeLabel?: string; /** - * Png encoded image. + * The window state. Default to normal. */ - image?: binary; - } - export type setDockTileReturnValue = { - } - /** - * Invoke custom browser commands used by telemetry. - */ - export type executeBrowserCommandParameters = { - commandId: BrowserCommandId; - } - export type executeBrowserCommandReturnValue = { - } - /** - * Allows a site to use privacy sandbox features that require enrollment -without the site actually being enrolled. Only supported on page targets. - */ - export type addPrivacySandboxEnrollmentOverrideParameters = { - url: string; - } - export type addPrivacySandboxEnrollmentOverrideReturnValue = { + windowState?: WindowState; } + export type PermissionType = "ar"|"audioCapture"|"automaticFullscreen"|"backgroundFetch"|"backgroundSync"|"cameraPanTiltZoom"|"capturedSurfaceControl"|"clipboardReadWrite"|"clipboardSanitizedWrite"|"displayCapture"|"durableStorage"|"geolocation"|"handTracking"|"idleDetection"|"keyboardLock"|"localFonts"|"localNetworkAccess"|"midi"|"midiSysex"|"nfc"|"notifications"|"paymentHandler"|"periodicBackgroundSync"|"pointerLock"|"protectedMediaIdentifier"|"sensors"|"smartCard"|"speakerSelection"|"storageAccess"|"topLevelStorageAccess"|"videoCapture"|"vr"|"wakeLockScreen"|"wakeLockSystem"|"webAppInstallation"|"webPrinting"|"windowManagement"; + export type PermissionSetting = "granted"|"denied"|"prompt"; /** - * Configures encryption keys used with a given privacy sandbox API to talk -to a trusted coordinator. Since this is intended for test automation only, -coordinatorOrigin must be a .test domain. No existing coordinator -configuration for the origin may exist. + * Definition of PermissionDescriptor defined in the Permissions API: +https://w3c.github.io/permissions/#dom-permissiondescriptor. */ - export type addPrivacySandboxCoordinatorKeyConfigParameters = { - api: PrivacySandboxAPI; - coordinatorOrigin: string; - keyConfig: string; + export interface PermissionDescriptor { /** - * BrowserContext to perform the action in. When omitted, default browser -context is used. + * Name of permission. +See https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/permissions/permission_descriptor.idl for valid permission names. */ - browserContextId?: BrowserContextID; - } - export type addPrivacySandboxCoordinatorKeyConfigReturnValue = { + name: string; + /** + * For "midi" permission, may also specify sysex control. + */ + sysex?: boolean; + /** + * For "push" permission, may specify userVisibleOnly. +Note that userVisibleOnly = true is the only currently supported type. + */ + userVisibleOnly?: boolean; + /** + * For "clipboard" permission, may specify allowWithoutSanitization. + */ + allowWithoutSanitization?: boolean; + /** + * For "fullscreen" permission, must specify allowWithoutGesture:true. + */ + allowWithoutGesture?: boolean; + /** + * For "camera" permission, may specify panTiltZoom. + */ + panTiltZoom?: boolean; } - } - - /** - * This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) -have an associated `id` used in subsequent operations on the related object. Each object type has -a specific `id` structure, and those are not interchangeable between objects of different kinds. -CSS objects can be loaded using the `get*ForNode()` calls (which accept a DOM node id). A client -can also keep track of stylesheets via the `styleSheetAdded`/`styleSheetRemoved` events and -subsequently load the required stylesheet contents using the `getStyleSheet[Text]()` methods. - */ - export module CSS { - export type StyleSheetId = string; /** - * Stylesheet type: "injected" for stylesheets injected via extension, "user-agent" for user-agent -stylesheets, "inspector" for stylesheets created by the inspector (i.e. those holding the "via -inspector" rules), "regular" for regular stylesheets. + * Browser command ids used by executeBrowserCommand. */ - export type StyleSheetOrigin = "injected"|"user-agent"|"inspector"|"regular"; + export type BrowserCommandId = "openTabSearch"|"closeTabSearch"|"openGlic"; /** - * CSS rule collection for a single pseudo style. + * Chrome histogram bucket. */ - export interface PseudoElementMatches { + export interface Bucket { /** - * Pseudo element type. + * Minimum value (inclusive). */ - pseudoType: DOM.PseudoType; + low: number; /** - * Pseudo element custom ident. + * Maximum value (exclusive). */ - pseudoIdentifier?: string; + high: number; /** - * Matches of CSS rules applicable to the pseudo style. + * Number of samples. */ - matches: RuleMatch[]; + count: number; } /** - * CSS style coming from animations with the name of the animation. + * Chrome histogram. */ - export interface CSSAnimationStyle { + export interface Histogram { /** - * The name of the animation. + * Name. */ - name?: string; + name: string; /** - * The style coming from the animation. + * Sum of sample values. */ - style: CSSStyle; - } - /** - * Inherited CSS rule collection from ancestor node. - */ - export interface InheritedStyleEntry { + sum: number; /** - * The ancestor node's inline style, if any, in the style inheritance chain. + * Total number of samples. */ - inlineStyle?: CSSStyle; + count: number; /** - * Matches of CSS rules matching the ancestor node in the style inheritance chain. + * Buckets. */ - matchedCSSRules: RuleMatch[]; + buckets: Bucket[]; } + export type PrivacySandboxAPI = "BiddingAndAuctionServices"|"TrustedKeyValue"; + /** - * Inherited CSS style collection for animated styles from ancestor node. + * Fired when page is about to start a download. */ - export interface InheritedAnimatedStyleEntry { + export type downloadWillBeginPayload = { /** - * Styles coming from the animations of the ancestor, if any, in the style inheritance chain. + * Id of the frame that caused the download to begin. */ - animationStyles?: CSSAnimationStyle[]; + frameId: Page.FrameId; /** - * The style coming from the transitions of the ancestor, if any, in the style inheritance chain. + * Global unique identifier of the download. */ - transitionsStyle?: CSSStyle; - } - /** - * Inherited pseudo element matches from pseudos of an ancestor node. - */ - export interface InheritedPseudoElementMatches { + guid: string; /** - * Matches of pseudo styles from the pseudos of an ancestor node. + * URL of the resource being downloaded. */ - pseudoElements: PseudoElementMatches[]; + url: string; + /** + * Suggested file name of the resource (the actual name of the file saved on disk may differ). + */ + suggestedFilename: string; } /** - * Match data for a CSS rule. + * Fired when download makes progress. Last call has |done| == true. */ - export interface RuleMatch { + export type downloadProgressPayload = { /** - * CSS rule in the match. + * Global unique identifier of the download. */ - rule: CSSRule; + guid: string; /** - * Matching selector indices in the rule's selectorList selectors (0-based). + * Total expected bytes to download. */ - matchingSelectors: number[]; - } - /** - * Data for a simple selector (these are delimited by commas in a selector list). - */ - export interface Value { + totalBytes: number; /** - * Value text. + * Total bytes received. */ - text: string; + receivedBytes: number; /** - * Value range in the underlying resource (if available). + * Download status. */ - range?: SourceRange; + state: "inProgress"|"completed"|"canceled"; /** - * Specificity of the selector. + * If download is "completed", provides the path of the downloaded file. +Depending on the platform, it is not guaranteed to be set, nor the file +is guaranteed to exist. */ - specificity?: Specificity; + filePath?: string; } + /** - * Specificity: -https://drafts.csswg.org/selectors/#specificity-rules + * Set permission settings for given requesting and embedding origins. */ - export interface Specificity { + export type setPermissionParameters = { /** - * The a component, which represents the number of ID selectors. + * Descriptor of permission to override. */ - a: number; + permission: PermissionDescriptor; /** - * The b component, which represents the number of class selectors, attributes selectors, and -pseudo-classes. + * Setting of the permission. */ - b: number; + setting: PermissionSetting; /** - * The c component, which represents the number of type selectors and pseudo-elements. + * Requesting origin the permission applies to, all origins if not specified. */ - c: number; - } - /** - * Selector list data. - */ - export interface SelectorList { + origin?: string; /** - * Selectors in the list. + * Embedding origin the permission applies to. It is ignored unless the requesting origin is +present and valid. If the requesting origin is provided but the embedding origin isn't, the +requesting origin is used as the embedding origin. */ - selectors: Value[]; + embeddingOrigin?: string; /** - * Rule selector text. + * Context to override. When omitted, default browser context is used. */ - text: string; + browserContextId?: BrowserContextID; + } + export type setPermissionReturnValue = { } /** - * CSS stylesheet metainformation. + * Grant specific permissions to the given origin and reject all others. */ - export interface CSSStyleSheetHeader { + export type grantPermissionsParameters = { + permissions: PermissionType[]; /** - * The stylesheet identifier. + * Origin the permission applies to, all origins if not specified. */ - styleSheetId: StyleSheetId; + origin?: string; /** - * Owner frame identifier. + * BrowserContext to override permissions. When omitted, default browser context is used. */ - frameId: Page.FrameId; + browserContextId?: BrowserContextID; + } + export type grantPermissionsReturnValue = { + } + /** + * Reset all permission management for all origins. + */ + export type resetPermissionsParameters = { /** - * Stylesheet resource URL. Empty if this is a constructed stylesheet created using -new CSSStyleSheet() (but non-empty if this is a constructed stylesheet imported -as a CSS module script). + * BrowserContext to reset permissions. When omitted, default browser context is used. */ - sourceURL: string; + browserContextId?: BrowserContextID; + } + export type resetPermissionsReturnValue = { + } + /** + * Set the behavior when downloading a file. + */ + export type setDownloadBehaviorParameters = { /** - * URL of source map associated with the stylesheet (if any). + * Whether to allow all or deny all download requests, or use default Chrome behavior if +available (otherwise deny). |allowAndName| allows download and names files according to +their download guids. */ - sourceMapURL?: string; + behavior: "deny"|"allow"|"allowAndName"|"default"; /** - * Stylesheet origin. + * BrowserContext to set download behavior. When omitted, default browser context is used. */ - origin: StyleSheetOrigin; + browserContextId?: BrowserContextID; /** - * Stylesheet title. + * The default path to save downloaded files to. This is required if behavior is set to 'allow' +or 'allowAndName'. */ - title: string; + downloadPath?: string; /** - * The backend id for the owner node of the stylesheet. + * Whether to emit download events (defaults to false). */ - ownerNode?: DOM.BackendNodeId; + eventsEnabled?: boolean; + } + export type setDownloadBehaviorReturnValue = { + } + /** + * Cancel a download if in progress + */ + export type cancelDownloadParameters = { /** - * Denotes whether the stylesheet is disabled. + * Global unique identifier of the download. */ - disabled: boolean; + guid: string; /** - * Whether the sourceURL field value comes from the sourceURL comment. + * BrowserContext to perform the action in. When omitted, default browser context is used. */ - hasSourceURL?: boolean; + browserContextId?: BrowserContextID; + } + export type cancelDownloadReturnValue = { + } + /** + * Close browser gracefully. + */ + export type closeParameters = { + } + export type closeReturnValue = { + } + /** + * Crashes browser on the main thread. + */ + export type crashParameters = { + } + export type crashReturnValue = { + } + /** + * Crashes GPU process. + */ + export type crashGpuProcessParameters = { + } + export type crashGpuProcessReturnValue = { + } + /** + * Returns version information. + */ + export type getVersionParameters = { + } + export type getVersionReturnValue = { /** - * Whether this stylesheet is created for STYLE tag by parser. This flag is not set for -document.written STYLE tags. + * Protocol version. */ - isInline: boolean; + protocolVersion: string; /** - * Whether this stylesheet is mutable. Inline stylesheets become mutable -after they have been modified via CSSOM API. -`` element's stylesheets become mutable only if DevTools modifies them. -Constructed stylesheets (new CSSStyleSheet()) are mutable immediately after creation. + * Product name. */ - isMutable: boolean; + product: string; /** - * True if this stylesheet is created through new CSSStyleSheet() or imported as a -CSS module script. + * Product revision. */ - isConstructed: boolean; + revision: string; /** - * Line offset of the stylesheet within the resource (zero based). + * User-Agent. */ - startLine: number; + userAgent: string; /** - * Column offset of the stylesheet within the resource (zero based). + * V8 version. */ - startColumn: number; + jsVersion: string; + } + /** + * Returns the command line switches for the browser process if, and only if +--enable-automation is on the commandline. + */ + export type getBrowserCommandLineParameters = { + } + export type getBrowserCommandLineReturnValue = { /** - * Size of the content (in characters). + * Commandline parameters */ - length: number; + arguments: string[]; + } + /** + * Get Chrome histograms. + */ + export type getHistogramsParameters = { /** - * Line offset of the end of the stylesheet within the resource (zero based). + * Requested substring in name. Only histograms which have query as a +substring in their name are extracted. An empty or absent query returns +all histograms. */ - endLine: number; + query?: string; /** - * Column offset of the end of the stylesheet within the resource (zero based). + * If true, retrieve delta since last delta call. */ - endColumn: number; + delta?: boolean; + } + export type getHistogramsReturnValue = { /** - * If the style sheet was loaded from a network resource, this indicates when the resource failed to load + * Histograms. */ - loadingFailed?: boolean; + histograms: Histogram[]; } /** - * CSS rule representation. + * Get a Chrome histogram by name. */ - export interface CSSRule { + export type getHistogramParameters = { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Requested histogram name. */ - styleSheetId?: StyleSheetId; + name: string; /** - * Rule selector data. + * If true, retrieve delta since last delta call. */ - selectorList: SelectorList; + delta?: boolean; + } + export type getHistogramReturnValue = { /** - * Array of selectors from ancestor style rules, sorted by distance from the current rule. + * Histogram. */ - nestingSelectors?: string[]; - /** - * Parent stylesheet's origin. - */ - origin: StyleSheetOrigin; - /** - * Associated style declaration. - */ - style: CSSStyle; + histogram: Histogram; + } + /** + * Get position and size of the browser window. + */ + export type getWindowBoundsParameters = { /** - * Media list array (for rules involving media queries). The array enumerates media queries -starting with the innermost one, going outwards. + * Browser window id. */ - media?: CSSMedia[]; + windowId: WindowID; + } + export type getWindowBoundsReturnValue = { /** - * Container query list array (for rules involving container queries). -The array enumerates container queries starting with the innermost one, going outwards. + * Bounds information of the window. When window state is 'minimized', the restored window +position and size are returned. */ - containerQueries?: CSSContainerQuery[]; + bounds: Bounds; + } + /** + * Get the browser window that contains the devtools target. + */ + export type getWindowForTargetParameters = { /** - * @supports CSS at-rule array. -The array enumerates @supports at-rules starting with the innermost one, going outwards. + * Devtools agent host id. If called as a part of the session, associated targetId is used. */ - supports?: CSSSupports[]; + targetId?: Target.TargetID; + } + export type getWindowForTargetReturnValue = { /** - * Cascade layer array. Contains the layer hierarchy that this rule belongs to starting -with the innermost layer and going outwards. + * Browser window id. */ - layers?: CSSLayer[]; + windowId: WindowID; /** - * @scope CSS at-rule array. -The array enumerates @scope at-rules starting with the innermost one, going outwards. + * Bounds information of the window. When window state is 'minimized', the restored window +position and size are returned. */ - scopes?: CSSScope[]; + bounds: Bounds; + } + /** + * Set position and/or size of the browser window. + */ + export type setWindowBoundsParameters = { /** - * The array keeps the types of ancestor CSSRules from the innermost going outwards. + * Browser window id. */ - ruleTypes?: CSSRuleType[]; + windowId: WindowID; /** - * @starting-style CSS at-rule array. -The array enumerates @starting-style at-rules starting with the innermost one, going outwards. + * New window bounds. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined +with 'left', 'top', 'width' or 'height'. Leaves unspecified fields unchanged. */ - startingStyles?: CSSStartingStyle[]; + bounds: Bounds; + } + export type setWindowBoundsReturnValue = { } /** - * Enum indicating the type of a CSS rule, used to represent the order of a style rule's ancestors. -This list only contains rule types that are collected during the ancestor rule collection. - */ - export type CSSRuleType = "MediaRule"|"SupportsRule"|"ContainerRule"|"LayerRule"|"ScopeRule"|"StyleRule"|"StartingStyleRule"; - /** - * CSS coverage information. + * Set size of the browser contents resizing browser window as necessary. */ - export interface RuleUsage { + export type setContentsSizeParameters = { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Browser window id. */ - styleSheetId: StyleSheetId; + windowId: WindowID; /** - * Offset of the start of the rule (including selector) from the beginning of the stylesheet. + * The window contents width in DIP. Assumes current width if omitted. +Must be specified if 'height' is omitted. */ - startOffset: number; + width?: number; /** - * Offset of the end of the rule body from the beginning of the stylesheet. + * The window contents height in DIP. Assumes current height if omitted. +Must be specified if 'width' is omitted. */ - endOffset: number; + height?: number; + } + export type setContentsSizeReturnValue = { + } + /** + * Set dock tile details, platform-specific. + */ + export type setDockTileParameters = { + badgeLabel?: string; /** - * Indicates whether the rule was actually used by some element in the page. + * Png encoded image. */ - used: boolean; + image?: binary; + } + export type setDockTileReturnValue = { } /** - * Text range within a resource. All numbers are zero-based. + * Invoke custom browser commands used by telemetry. */ - export interface SourceRange { + export type executeBrowserCommandParameters = { + commandId: BrowserCommandId; + } + export type executeBrowserCommandReturnValue = { + } + /** + * Allows a site to use privacy sandbox features that require enrollment +without the site actually being enrolled. Only supported on page targets. + */ + export type addPrivacySandboxEnrollmentOverrideParameters = { + url: string; + } + export type addPrivacySandboxEnrollmentOverrideReturnValue = { + } + /** + * Configures encryption keys used with a given privacy sandbox API to talk +to a trusted coordinator. Since this is intended for test automation only, +coordinatorOrigin must be a .test domain. No existing coordinator +configuration for the origin may exist. + */ + export type addPrivacySandboxCoordinatorKeyConfigParameters = { + api: PrivacySandboxAPI; + coordinatorOrigin: string; + keyConfig: string; /** - * Start line of range. + * BrowserContext to perform the action in. When omitted, default browser +context is used. */ - startLine: number; + browserContextId?: BrowserContextID; + } + export type addPrivacySandboxCoordinatorKeyConfigReturnValue = { + } + } + + /** + * This domain exposes CSS read/write operations. All CSS objects (stylesheets, rules, and styles) +have an associated `id` used in subsequent operations on the related object. Each object type has +a specific `id` structure, and those are not interchangeable between objects of different kinds. +CSS objects can be loaded using the `get*ForNode()` calls (which accept a DOM node id). A client +can also keep track of stylesheets via the `styleSheetAdded`/`styleSheetRemoved` events and +subsequently load the required stylesheet contents using the `getStyleSheet[Text]()` methods. + */ + export module CSS { + export type StyleSheetId = string; + /** + * Stylesheet type: "injected" for stylesheets injected via extension, "user-agent" for user-agent +stylesheets, "inspector" for stylesheets created by the inspector (i.e. those holding the "via +inspector" rules), "regular" for regular stylesheets. + */ + export type StyleSheetOrigin = "injected"|"user-agent"|"inspector"|"regular"; + /** + * CSS rule collection for a single pseudo style. + */ + export interface PseudoElementMatches { /** - * Start column of range (inclusive). + * Pseudo element type. */ - startColumn: number; + pseudoType: DOM.PseudoType; /** - * End line of range + * Pseudo element custom ident. */ - endLine: number; + pseudoIdentifier?: string; /** - * End column of range (exclusive). + * Matches of CSS rules applicable to the pseudo style. */ - endColumn: number; + matches: RuleMatch[]; } - export interface ShorthandEntry { - /** - * Shorthand name. - */ - name: string; + /** + * CSS style coming from animations with the name of the animation. + */ + export interface CSSAnimationStyle { /** - * Shorthand value. + * The name of the animation. */ - value: string; + name?: string; /** - * Whether the property has "!important" annotation (implies `false` if absent). + * The style coming from the animation. */ - important?: boolean; + style: CSSStyle; } - export interface CSSComputedStyleProperty { + /** + * Inherited CSS rule collection from ancestor node. + */ + export interface InheritedStyleEntry { /** - * Computed style property name. + * The ancestor node's inline style, if any, in the style inheritance chain. */ - name: string; + inlineStyle?: CSSStyle; /** - * Computed style property value. + * Matches of CSS rules matching the ancestor node in the style inheritance chain. */ - value: string; + matchedCSSRules: RuleMatch[]; } /** - * CSS style representation. + * Inherited CSS style collection for animated styles from ancestor node. */ - export interface CSSStyle { + export interface InheritedAnimatedStyleEntry { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Styles coming from the animations of the ancestor, if any, in the style inheritance chain. */ - styleSheetId?: StyleSheetId; + animationStyles?: CSSAnimationStyle[]; /** - * CSS properties in the style. + * The style coming from the transitions of the ancestor, if any, in the style inheritance chain. */ - cssProperties: CSSProperty[]; + transitionsStyle?: CSSStyle; + } + /** + * Inherited pseudo element matches from pseudos of an ancestor node. + */ + export interface InheritedPseudoElementMatches { /** - * Computed values for all shorthands found in the style. + * Matches of pseudo styles from the pseudos of an ancestor node. */ - shorthandEntries: ShorthandEntry[]; + pseudoElements: PseudoElementMatches[]; + } + /** + * Match data for a CSS rule. + */ + export interface RuleMatch { /** - * Style declaration text (if available). + * CSS rule in the match. */ - cssText?: string; + rule: CSSRule; /** - * Style declaration range in the enclosing stylesheet (if available). + * Matching selector indices in the rule's selectorList selectors (0-based). */ - range?: SourceRange; + matchingSelectors: number[]; } /** - * CSS property declaration data. + * Data for a simple selector (these are delimited by commas in a selector list). */ - export interface CSSProperty { - /** - * The property name. - */ - name: string; + export interface Value { /** - * The property value. + * Value text. */ - value: string; + text: string; /** - * Whether the property has "!important" annotation (implies `false` if absent). + * Value range in the underlying resource (if available). */ - important?: boolean; + range?: SourceRange; /** - * Whether the property is implicit (implies `false` if absent). + * Specificity of the selector. */ - implicit?: boolean; + specificity?: Specificity; + } + /** + * Specificity: +https://drafts.csswg.org/selectors/#specificity-rules + */ + export interface Specificity { /** - * The full property text as specified in the style. + * The a component, which represents the number of ID selectors. */ - text?: string; + a: number; /** - * Whether the property is understood by the browser (implies `true` if absent). + * The b component, which represents the number of class selectors, attributes selectors, and +pseudo-classes. */ - parsedOk?: boolean; + b: number; /** - * Whether the property is disabled by the user (present for source-based properties only). + * The c component, which represents the number of type selectors and pseudo-elements. */ - disabled?: boolean; + c: number; + } + /** + * Selector list data. + */ + export interface SelectorList { /** - * The entire property range in the enclosing style declaration (if available). + * Selectors in the list. */ - range?: SourceRange; + selectors: Value[]; /** - * Parsed longhand components of this property if it is a shorthand. -This field will be empty if the given property is not a shorthand. + * Rule selector text. */ - longhandProperties?: CSSProperty[]; + text: string; } /** - * CSS media rule descriptor. + * CSS stylesheet metainformation. */ - export interface CSSMedia { + export interface CSSStyleSheetHeader { /** - * Media query text. + * The stylesheet identifier. */ - text: string; + styleSheetId: StyleSheetId; /** - * Source of the media query: "mediaRule" if specified by a @media rule, "importRule" if -specified by an @import rule, "linkedSheet" if specified by a "media" attribute in a linked -stylesheet's LINK tag, "inlineSheet" if specified by a "media" attribute in an inline -stylesheet's STYLE tag. + * Owner frame identifier. */ - source: "mediaRule"|"importRule"|"linkedSheet"|"inlineSheet"; + frameId: Page.FrameId; /** - * URL of the document containing the media query description. + * Stylesheet resource URL. Empty if this is a constructed stylesheet created using +new CSSStyleSheet() (but non-empty if this is a constructed stylesheet imported +as a CSS module script). */ - sourceURL?: string; + sourceURL: string; /** - * The associated rule (@media or @import) header range in the enclosing stylesheet (if -available). + * URL of source map associated with the stylesheet (if any). */ - range?: SourceRange; + sourceMapURL?: string; /** - * Identifier of the stylesheet containing this object (if exists). + * Stylesheet origin. */ - styleSheetId?: StyleSheetId; + origin: StyleSheetOrigin; /** - * Array of media queries. + * Stylesheet title. */ - mediaList?: MediaQuery[]; - } - /** - * Media query descriptor. - */ - export interface MediaQuery { + title: string; /** - * Array of media query expressions. + * The backend id for the owner node of the stylesheet. */ - expressions: MediaQueryExpression[]; + ownerNode?: DOM.BackendNodeId; /** - * Whether the media query condition is satisfied. + * Denotes whether the stylesheet is disabled. */ - active: boolean; - } - /** - * Media query expression descriptor. - */ - export interface MediaQueryExpression { + disabled: boolean; /** - * Media query expression value. + * Whether the sourceURL field value comes from the sourceURL comment. */ - value: number; + hasSourceURL?: boolean; /** - * Media query expression units. + * Whether this stylesheet is created for STYLE tag by parser. This flag is not set for +document.written STYLE tags. */ - unit: string; + isInline: boolean; /** - * Media query expression feature. + * Whether this stylesheet is mutable. Inline stylesheets become mutable +after they have been modified via CSSOM API. +`` element's stylesheets become mutable only if DevTools modifies them. +Constructed stylesheets (new CSSStyleSheet()) are mutable immediately after creation. */ - feature: string; + isMutable: boolean; /** - * The associated range of the value text in the enclosing stylesheet (if available). + * True if this stylesheet is created through new CSSStyleSheet() or imported as a +CSS module script. */ - valueRange?: SourceRange; + isConstructed: boolean; /** - * Computed length of media query expression (if applicable). + * Line offset of the stylesheet within the resource (zero based). */ - computedLength?: number; + startLine: number; + /** + * Column offset of the stylesheet within the resource (zero based). + */ + startColumn: number; + /** + * Size of the content (in characters). + */ + length: number; + /** + * Line offset of the end of the stylesheet within the resource (zero based). + */ + endLine: number; + /** + * Column offset of the end of the stylesheet within the resource (zero based). + */ + endColumn: number; + /** + * If the style sheet was loaded from a network resource, this indicates when the resource failed to load + */ + loadingFailed?: boolean; } /** - * CSS container query rule descriptor. + * CSS rule representation. */ - export interface CSSContainerQuery { + export interface CSSRule { /** - * Container query text. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - text: string; + styleSheetId?: StyleSheetId; /** - * The associated rule header range in the enclosing stylesheet (if -available). + * Rule selector data. */ - range?: SourceRange; + selectorList: SelectorList; /** - * Identifier of the stylesheet containing this object (if exists). + * Array of selectors from ancestor style rules, sorted by distance from the current rule. */ - styleSheetId?: StyleSheetId; + nestingSelectors?: string[]; /** - * Optional name for the container. + * Parent stylesheet's origin. */ - name?: string; + origin: StyleSheetOrigin; /** - * Optional physical axes queried for the container. + * Associated style declaration. */ - physicalAxes?: DOM.PhysicalAxes; + style: CSSStyle; /** - * Optional logical axes queried for the container. + * Media list array (for rules involving media queries). The array enumerates media queries +starting with the innermost one, going outwards. */ - logicalAxes?: DOM.LogicalAxes; + media?: CSSMedia[]; /** - * true if the query contains scroll-state() queries. + * Container query list array (for rules involving container queries). +The array enumerates container queries starting with the innermost one, going outwards. */ - queriesScrollState?: boolean; + containerQueries?: CSSContainerQuery[]; /** - * true if the query contains anchored() queries. + * @supports CSS at-rule array. +The array enumerates @supports at-rules starting with the innermost one, going outwards. */ - queriesAnchored?: boolean; - } - /** - * CSS Supports at-rule descriptor. - */ - export interface CSSSupports { + supports?: CSSSupports[]; /** - * Supports rule text. + * Cascade layer array. Contains the layer hierarchy that this rule belongs to starting +with the innermost layer and going outwards. */ - text: string; + layers?: CSSLayer[]; /** - * Whether the supports condition is satisfied. + * @scope CSS at-rule array. +The array enumerates @scope at-rules starting with the innermost one, going outwards. */ - active: boolean; + scopes?: CSSScope[]; /** - * The associated rule header range in the enclosing stylesheet (if -available). + * The array keeps the types of ancestor CSSRules from the innermost going outwards. */ - range?: SourceRange; + ruleTypes?: CSSRuleType[]; /** - * Identifier of the stylesheet containing this object (if exists). + * @starting-style CSS at-rule array. +The array enumerates @starting-style at-rules starting with the innermost one, going outwards. */ - styleSheetId?: StyleSheetId; + startingStyles?: CSSStartingStyle[]; } /** - * CSS Scope at-rule descriptor. + * Enum indicating the type of a CSS rule, used to represent the order of a style rule's ancestors. +This list only contains rule types that are collected during the ancestor rule collection. */ - export interface CSSScope { + export type CSSRuleType = "MediaRule"|"SupportsRule"|"ContainerRule"|"LayerRule"|"ScopeRule"|"StyleRule"|"StartingStyleRule"; + /** + * CSS coverage information. + */ + export interface RuleUsage { /** - * Scope rule text. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - text: string; + styleSheetId: StyleSheetId; /** - * The associated rule header range in the enclosing stylesheet (if -available). + * Offset of the start of the rule (including selector) from the beginning of the stylesheet. */ - range?: SourceRange; + startOffset: number; /** - * Identifier of the stylesheet containing this object (if exists). + * Offset of the end of the rule body from the beginning of the stylesheet. */ - styleSheetId?: StyleSheetId; + endOffset: number; + /** + * Indicates whether the rule was actually used by some element in the page. + */ + used: boolean; } /** - * CSS Layer at-rule descriptor. + * Text range within a resource. All numbers are zero-based. */ - export interface CSSLayer { + export interface SourceRange { /** - * Layer name. + * Start line of range. */ - text: string; + startLine: number; /** - * The associated rule header range in the enclosing stylesheet (if -available). + * Start column of range (inclusive). */ - range?: SourceRange; + startColumn: number; /** - * Identifier of the stylesheet containing this object (if exists). + * End line of range */ - styleSheetId?: StyleSheetId; - } - /** - * CSS Starting Style at-rule descriptor. - */ - export interface CSSStartingStyle { + endLine: number; /** - * The associated rule header range in the enclosing stylesheet (if -available). + * End column of range (exclusive). */ - range?: SourceRange; + endColumn: number; + } + export interface ShorthandEntry { /** - * Identifier of the stylesheet containing this object (if exists). - */ - styleSheetId?: StyleSheetId; - } - /** - * CSS Layer data. - */ - export interface CSSLayerData { - /** - * Layer name. + * Shorthand name. */ name: string; /** - * Direct sub-layers + * Shorthand value. */ - subLayers?: CSSLayerData[]; + value: string; /** - * Layer order. The order determines the order of the layer in the cascade order. -A higher number has higher priority in the cascade order. + * Whether the property has "!important" annotation (implies `false` if absent). */ - order: number; + important?: boolean; } - /** - * Information about amount of glyphs that were rendered with given font. - */ - export interface PlatformFontUsage { - /** - * Font's family name reported by platform. - */ - familyName: string; + export interface CSSComputedStyleProperty { /** - * Font's PostScript name reported by platform. + * Computed style property name. */ - postScriptName: string; + name: string; /** - * Indicates if the font was downloaded or resolved locally. + * Computed style property value. */ - isCustomFont: boolean; + value: string; + } + export interface ComputedStyleExtraFields { /** - * Amount of glyphs that were rendered with this font. + * Returns whether or not this node is being rendered with base appearance, +which happens when it has its appearance property set to base/base-select +or it is in the subtree of an element being rendered with base appearance. */ - glyphCount: number; + isAppearanceBase: boolean; } /** - * Information about font variation axes for variable fonts + * CSS style representation. */ - export interface FontVariationAxis { + export interface CSSStyle { /** - * The font-variation-setting tag (a.k.a. "axis tag"). + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - tag: string; + styleSheetId?: StyleSheetId; /** - * Human-readable variation name in the default language (normally, "en"). + * CSS properties in the style. */ - name: string; + cssProperties: CSSProperty[]; /** - * The minimum value (inclusive) the font supports for this tag. + * Computed values for all shorthands found in the style. */ - minValue: number; + shorthandEntries: ShorthandEntry[]; /** - * The maximum value (inclusive) the font supports for this tag. + * Style declaration text (if available). */ - maxValue: number; + cssText?: string; /** - * The default value. + * Style declaration range in the enclosing stylesheet (if available). */ - defaultValue: number; + range?: SourceRange; } /** - * Properties of a web font: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#font-descriptions -and additional information such as platformFontFamily and fontVariationAxes. + * CSS property declaration data. */ - export interface FontFace { - /** - * The font-family. - */ - fontFamily: string; + export interface CSSProperty { /** - * The font-style. + * The property name. */ - fontStyle: string; + name: string; /** - * The font-variant. + * The property value. */ - fontVariant: string; + value: string; /** - * The font-weight. + * Whether the property has "!important" annotation (implies `false` if absent). */ - fontWeight: string; + important?: boolean; /** - * The font-stretch. + * Whether the property is implicit (implies `false` if absent). */ - fontStretch: string; + implicit?: boolean; /** - * The font-display. + * The full property text as specified in the style. */ - fontDisplay: string; + text?: string; /** - * The unicode-range. + * Whether the property is understood by the browser (implies `true` if absent). */ - unicodeRange: string; + parsedOk?: boolean; /** - * The src. + * Whether the property is disabled by the user (present for source-based properties only). */ - src: string; + disabled?: boolean; /** - * The resolved platform font family + * The entire property range in the enclosing style declaration (if available). */ - platformFontFamily: string; + range?: SourceRange; /** - * Available variation settings (a.k.a. "axes"). + * Parsed longhand components of this property if it is a shorthand. +This field will be empty if the given property is not a shorthand. */ - fontVariationAxes?: FontVariationAxis[]; + longhandProperties?: CSSProperty[]; } /** - * CSS try rule representation. + * CSS media rule descriptor. */ - export interface CSSTryRule { + export interface CSSMedia { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Media query text. */ - styleSheetId?: StyleSheetId; + text: string; /** - * Parent stylesheet's origin. + * Source of the media query: "mediaRule" if specified by a @media rule, "importRule" if +specified by an @import rule, "linkedSheet" if specified by a "media" attribute in a linked +stylesheet's LINK tag, "inlineSheet" if specified by a "media" attribute in an inline +stylesheet's STYLE tag. */ - origin: StyleSheetOrigin; + source: "mediaRule"|"importRule"|"linkedSheet"|"inlineSheet"; /** - * Associated style declaration. + * URL of the document containing the media query description. */ - style: CSSStyle; - } - /** - * CSS @position-try rule representation. - */ - export interface CSSPositionTryRule { + sourceURL?: string; /** - * The prelude dashed-ident name + * The associated rule (@media or @import) header range in the enclosing stylesheet (if +available). */ - name: Value; + range?: SourceRange; /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Identifier of the stylesheet containing this object (if exists). */ styleSheetId?: StyleSheetId; /** - * Parent stylesheet's origin. - */ - origin: StyleSheetOrigin; - /** - * Associated style declaration. + * Array of media queries. */ - style: CSSStyle; - active: boolean; + mediaList?: MediaQuery[]; } /** - * CSS keyframes rule representation. + * Media query descriptor. */ - export interface CSSKeyframesRule { + export interface MediaQuery { /** - * Animation name. + * Array of media query expressions. */ - animationName: Value; + expressions: MediaQueryExpression[]; /** - * List of keyframes. + * Whether the media query condition is satisfied. */ - keyframes: CSSKeyframeRule[]; - } - /** - * Representation of a custom property registration through CSS.registerProperty - */ - export interface CSSPropertyRegistration { - propertyName: string; - initialValue?: Value; - inherits: boolean; - syntax: string; + active: boolean; } /** - * CSS font-palette-values rule representation. + * Media query expression descriptor. */ - export interface CSSFontPaletteValuesRule { + export interface MediaQueryExpression { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Media query expression value. */ - styleSheetId?: StyleSheetId; + value: number; /** - * Parent stylesheet's origin. + * Media query expression units. */ - origin: StyleSheetOrigin; + unit: string; /** - * Associated font palette name. + * Media query expression feature. */ - fontPaletteName: Value; + feature: string; /** - * Associated style declaration. + * The associated range of the value text in the enclosing stylesheet (if available). */ - style: CSSStyle; + valueRange?: SourceRange; + /** + * Computed length of media query expression (if applicable). + */ + computedLength?: number; } /** - * CSS property at-rule representation. + * CSS container query rule descriptor. */ - export interface CSSPropertyRule { + export interface CSSContainerQuery { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Container query text. + */ + text: string; + /** + * The associated rule header range in the enclosing stylesheet (if +available). + */ + range?: SourceRange; + /** + * Identifier of the stylesheet containing this object (if exists). */ styleSheetId?: StyleSheetId; /** - * Parent stylesheet's origin. + * Optional name for the container. */ - origin: StyleSheetOrigin; + name?: string; /** - * Associated property name. + * Optional physical axes queried for the container. */ - propertyName: Value; + physicalAxes?: DOM.PhysicalAxes; /** - * Associated style declaration. + * Optional logical axes queried for the container. */ - style: CSSStyle; - } - /** - * CSS function argument representation. - */ - export interface CSSFunctionParameter { + logicalAxes?: DOM.LogicalAxes; /** - * The parameter name. + * true if the query contains scroll-state() queries. */ - name: string; + queriesScrollState?: boolean; /** - * The parameter type. + * true if the query contains anchored() queries. */ - type: string; + queriesAnchored?: boolean; } /** - * CSS function conditional block representation. + * CSS Supports at-rule descriptor. */ - export interface CSSFunctionConditionNode { + export interface CSSSupports { /** - * Media query for this conditional block. Only one type of condition should be set. + * Supports rule text. */ - media?: CSSMedia; + text: string; /** - * Container query for this conditional block. Only one type of condition should be set. + * Whether the supports condition is satisfied. */ - containerQueries?: CSSContainerQuery; + active: boolean; /** - * @supports CSS at-rule condition. Only one type of condition should be set. + * The associated rule header range in the enclosing stylesheet (if +available). */ - supports?: CSSSupports; + range?: SourceRange; /** - * Block body. + * Identifier of the stylesheet containing this object (if exists). */ - children: CSSFunctionNode[]; + styleSheetId?: StyleSheetId; + } + /** + * CSS Scope at-rule descriptor. + */ + export interface CSSScope { /** - * The condition text. + * Scope rule text. */ - conditionText: string; + text: string; + /** + * The associated rule header range in the enclosing stylesheet (if +available). + */ + range?: SourceRange; + /** + * Identifier of the stylesheet containing this object (if exists). + */ + styleSheetId?: StyleSheetId; } /** - * Section of the body of a CSS function rule. + * CSS Layer at-rule descriptor. */ - export interface CSSFunctionNode { + export interface CSSLayer { /** - * A conditional block. If set, style should not be set. + * Layer name. */ - condition?: CSSFunctionConditionNode; + text: string; /** - * Values set by this node. If set, condition should not be set. + * The associated rule header range in the enclosing stylesheet (if +available). */ - style?: CSSStyle; + range?: SourceRange; + /** + * Identifier of the stylesheet containing this object (if exists). + */ + styleSheetId?: StyleSheetId; } /** - * CSS function at-rule representation. + * CSS Starting Style at-rule descriptor. */ - export interface CSSFunctionRule { + export interface CSSStartingStyle { /** - * Name of the function. + * The associated rule header range in the enclosing stylesheet (if +available). */ - name: Value; + range?: SourceRange; /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Identifier of the stylesheet containing this object (if exists). */ styleSheetId?: StyleSheetId; + } + /** + * CSS Layer data. + */ + export interface CSSLayerData { /** - * Parent stylesheet's origin. + * Layer name. */ - origin: StyleSheetOrigin; + name: string; /** - * List of parameters. + * Direct sub-layers */ - parameters: CSSFunctionParameter[]; + subLayers?: CSSLayerData[]; /** - * Function body. + * Layer order. The order determines the order of the layer in the cascade order. +A higher number has higher priority in the cascade order. */ - children: CSSFunctionNode[]; + order: number; } /** - * CSS keyframe rule representation. + * Information about amount of glyphs that were rendered with given font. */ - export interface CSSKeyframeRule { + export interface PlatformFontUsage { /** - * The css style sheet identifier (absent for user agent stylesheet and user-specified -stylesheet rules) this rule came from. + * Font's family name reported by platform. */ - styleSheetId?: StyleSheetId; + familyName: string; /** - * Parent stylesheet's origin. + * Font's PostScript name reported by platform. */ - origin: StyleSheetOrigin; + postScriptName: string; /** - * Associated key text. + * Indicates if the font was downloaded or resolved locally. */ - keyText: Value; + isCustomFont: boolean; /** - * Associated style declaration. + * Amount of glyphs that were rendered with this font. */ - style: CSSStyle; + glyphCount: number; } /** - * A descriptor of operation to mutate style declaration text. + * Information about font variation axes for variable fonts */ - export interface StyleDeclarationEdit { + export interface FontVariationAxis { /** - * The css style sheet identifier. + * The font-variation-setting tag (a.k.a. "axis tag"). */ - styleSheetId: StyleSheetId; + tag: string; /** - * The range of the style text in the enclosing stylesheet. + * Human-readable variation name in the default language (normally, "en"). */ - range: SourceRange; + name: string; /** - * New style text. + * The minimum value (inclusive) the font supports for this tag. */ - text: string; - } - - /** - * Fires whenever a web font is updated. A non-empty font parameter indicates a successfully loaded -web font. - */ - export type fontsUpdatedPayload = { + minValue: number; /** - * The web font that has loaded. + * The maximum value (inclusive) the font supports for this tag. */ - font?: FontFace; - } - /** - * Fires whenever a MediaQuery result changes (for example, after a browser window has been -resized.) The current implementation considers only viewport-dependent media features. - */ - export type mediaQueryResultChangedPayload = void; - /** - * Fired whenever an active document stylesheet is added. - */ - export type styleSheetAddedPayload = { + maxValue: number; /** - * Added stylesheet metainfo. + * The default value. */ - header: CSSStyleSheetHeader; - } - /** - * Fired whenever a stylesheet is changed as a result of the client operation. - */ - export type styleSheetChangedPayload = { - styleSheetId: StyleSheetId; + defaultValue: number; } /** - * Fired whenever an active document stylesheet is removed. + * Properties of a web font: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#font-descriptions +and additional information such as platformFontFamily and fontVariationAxes. */ - export type styleSheetRemovedPayload = { + export interface FontFace { /** - * Identifier of the removed stylesheet. + * The font-family. */ - styleSheetId: StyleSheetId; - } - export type computedStyleUpdatedPayload = { + fontFamily: string; /** - * The node id that has updated computed styles. + * The font-style. */ - nodeId: DOM.NodeId; - } - - /** - * Inserts a new rule with the given `ruleText` in a stylesheet with given `styleSheetId`, at the -position specified by `location`. - */ - export type addRuleParameters = { + fontStyle: string; /** - * The css style sheet identifier where a new rule should be inserted. + * The font-variant. */ - styleSheetId: StyleSheetId; + fontVariant: string; /** - * The text of a new rule. + * The font-weight. */ - ruleText: string; + fontWeight: string; /** - * Text position of a new rule in the target style sheet. + * The font-stretch. */ - location: SourceRange; + fontStretch: string; /** - * NodeId for the DOM node in whose context custom property declarations for registered properties should be -validated. If omitted, declarations in the new rule text can only be validated statically, which may produce -incorrect results if the declaration contains a var() for example. + * The font-display. */ - nodeForPropertySyntaxValidation?: DOM.NodeId; - } - export type addRuleReturnValue = { + fontDisplay: string; /** - * The newly created rule. + * The unicode-range. */ - rule: CSSRule; + unicodeRange: string; + /** + * The src. + */ + src: string; + /** + * The resolved platform font family + */ + platformFontFamily: string; + /** + * Available variation settings (a.k.a. "axes"). + */ + fontVariationAxes?: FontVariationAxis[]; } /** - * Returns all class names from specified stylesheet. + * CSS try rule representation. */ - export type collectClassNamesParameters = { - styleSheetId: StyleSheetId; - } - export type collectClassNamesReturnValue = { + export interface CSSTryRule { /** - * Class name list. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - classNames: string[]; + styleSheetId?: StyleSheetId; + /** + * Parent stylesheet's origin. + */ + origin: StyleSheetOrigin; + /** + * Associated style declaration. + */ + style: CSSStyle; } /** - * Creates a new special "via-inspector" stylesheet in the frame with given `frameId`. + * CSS @position-try rule representation. */ - export type createStyleSheetParameters = { + export interface CSSPositionTryRule { /** - * Identifier of the frame where "via-inspector" stylesheet should be created. + * The prelude dashed-ident name */ - frameId: Page.FrameId; + name: Value; /** - * If true, creates a new stylesheet for every call. If false, -returns a stylesheet previously created by a call with force=false -for the frame's document if it exists or creates a new stylesheet -(default: false). + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - force?: boolean; - } - export type createStyleSheetReturnValue = { + styleSheetId?: StyleSheetId; /** - * Identifier of the created "via-inspector" stylesheet. + * Parent stylesheet's origin. */ - styleSheetId: StyleSheetId; + origin: StyleSheetOrigin; + /** + * Associated style declaration. + */ + style: CSSStyle; + active: boolean; } /** - * Disables the CSS agent for the given page. + * CSS keyframes rule representation. */ - export type disableParameters = { - } - export type disableReturnValue = { + export interface CSSKeyframesRule { + /** + * Animation name. + */ + animationName: Value; + /** + * List of keyframes. + */ + keyframes: CSSKeyframeRule[]; } /** - * Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been -enabled until the result of this command is received. + * Representation of a custom property registration through CSS.registerProperty */ - export type enableParameters = { - } - export type enableReturnValue = { + export interface CSSPropertyRegistration { + propertyName: string; + initialValue?: Value; + inherits: boolean; + syntax: string; } /** - * Ensures that the given node will have specified pseudo-classes whenever its style is computed by -the browser. + * CSS font-palette-values rule representation. */ - export type forcePseudoStateParameters = { + export interface CSSFontPaletteValuesRule { /** - * The element id for which to force the pseudo state. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - nodeId: DOM.NodeId; + styleSheetId?: StyleSheetId; /** - * Element pseudo classes to force when computing the element's style. + * Parent stylesheet's origin. */ - forcedPseudoClasses: string[]; - } - export type forcePseudoStateReturnValue = { - } - /** - * Ensures that the given node is in its starting-style state. - */ - export type forceStartingStyleParameters = { + origin: StyleSheetOrigin; /** - * The element id for which to force the starting-style state. + * Associated font palette name. */ - nodeId: DOM.NodeId; + fontPaletteName: Value; /** - * Boolean indicating if this is on or off. + * Associated style declaration. */ - forced: boolean; - } - export type forceStartingStyleReturnValue = { + style: CSSStyle; } - export type getBackgroundColorsParameters = { + /** + * CSS property at-rule representation. + */ + export interface CSSPropertyRule { /** - * Id of the node to get background colors for. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - nodeId: DOM.NodeId; - } - export type getBackgroundColorsReturnValue = { + styleSheetId?: StyleSheetId; /** - * The range of background colors behind this element, if it contains any visible text. If no -visible text is present, this will be undefined. In the case of a flat background color, -this will consist of simply that color. In the case of a gradient, this will consist of each -of the color stops. For anything more complicated, this will be an empty array. Images will -be ignored (as if the image had failed to load). + * Parent stylesheet's origin. */ - backgroundColors?: string[]; + origin: StyleSheetOrigin; /** - * The computed font size for this node, as a CSS computed value string (e.g. '12px'). + * Associated property name. */ - computedFontSize?: string; + propertyName: Value; /** - * The computed font weight for this node, as a CSS computed value string (e.g. 'normal' or -'100'). + * Associated style declaration. */ - computedFontWeight?: string; + style: CSSStyle; } /** - * Returns the computed style for a DOM node identified by `nodeId`. + * CSS function argument representation. */ - export type getComputedStyleForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getComputedStyleForNodeReturnValue = { + export interface CSSFunctionParameter { /** - * Computed style for the specified DOM node. + * The parameter name. */ - computedStyle: CSSComputedStyleProperty[]; + name: string; + /** + * The parameter type. + */ + type: string; } /** - * Resolve the specified values in the context of the provided element. -For example, a value of '1em' is evaluated according to the computed -'font-size' of the element and a value 'calc(1px + 2px)' will be -resolved to '3px'. -If the `propertyName` was specified the `values` are resolved as if -they were property's declaration. If a value cannot be parsed according -to the provided property syntax, the value is parsed using combined -syntax as if null `propertyName` was provided. If the value cannot be -resolved even then, return the provided value without any changes. + * CSS function conditional block representation. */ - export type resolveValuesParameters = { - /** - * Substitution functions (var()/env()/attr()) and cascade-dependent -keywords (revert/revert-layer) do not work. - */ - values: string[]; - /** - * Id of the node in whose context the expression is evaluated - */ - nodeId: DOM.NodeId; + export interface CSSFunctionConditionNode { /** - * Only longhands and custom property names are accepted. + * Media query for this conditional block. Only one type of condition should be set. */ - propertyName?: string; + media?: CSSMedia; /** - * Pseudo element type, only works for pseudo elements that generate -elements in the tree, such as ::before and ::after. + * Container query for this conditional block. Only one type of condition should be set. */ - pseudoType?: DOM.PseudoType; + containerQueries?: CSSContainerQuery; /** - * Pseudo element custom ident. + * @supports CSS at-rule condition. Only one type of condition should be set. */ - pseudoIdentifier?: string; - } - export type resolveValuesReturnValue = { - results: string[]; - } - export type getLonghandPropertiesParameters = { - shorthandName: string; - value: string; - } - export type getLonghandPropertiesReturnValue = { - longhandProperties: CSSProperty[]; - } - /** - * Returns the styles defined inline (explicitly in the "style" attribute and implicitly, using DOM -attributes) for a DOM node identified by `nodeId`. - */ - export type getInlineStylesForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getInlineStylesForNodeReturnValue = { + supports?: CSSSupports; /** - * Inline style for the specified DOM node. + * Block body. */ - inlineStyle?: CSSStyle; + children: CSSFunctionNode[]; /** - * Attribute-defined element style (e.g. resulting from "width=20 height=100%"). + * The condition text. */ - attributesStyle?: CSSStyle; + conditionText: string; } /** - * Returns the styles coming from animations & transitions -including the animation & transition styles coming from inheritance chain. + * Section of the body of a CSS function rule. */ - export type getAnimatedStylesForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getAnimatedStylesForNodeReturnValue = { - /** - * Styles coming from animations. - */ - animationStyles?: CSSAnimationStyle[]; + export interface CSSFunctionNode { /** - * Style coming from transitions. + * A conditional block. If set, style should not be set. */ - transitionsStyle?: CSSStyle; + condition?: CSSFunctionConditionNode; /** - * Inherited style entries for animationsStyle and transitionsStyle from -the inheritance chain of the element. + * Values set by this node. If set, condition should not be set. */ - inherited?: InheritedAnimatedStyleEntry[]; + style?: CSSStyle; } /** - * Returns requested styles for a DOM node identified by `nodeId`. + * CSS function at-rule representation. */ - export type getMatchedStylesForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getMatchedStylesForNodeReturnValue = { - /** - * Inline style for the specified DOM node. - */ - inlineStyle?: CSSStyle; - /** - * Attribute-defined element style (e.g. resulting from "width=20 height=100%"). - */ - attributesStyle?: CSSStyle; + export interface CSSFunctionRule { /** - * CSS rules matching this node, from all applicable stylesheets. + * Name of the function. */ - matchedCSSRules?: RuleMatch[]; + name: Value; /** - * Pseudo style matches for this node. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - pseudoElements?: PseudoElementMatches[]; + styleSheetId?: StyleSheetId; /** - * A chain of inherited styles (from the immediate node parent up to the DOM tree root). + * Parent stylesheet's origin. */ - inherited?: InheritedStyleEntry[]; + origin: StyleSheetOrigin; /** - * A chain of inherited pseudo element styles (from the immediate node parent up to the DOM tree root). + * List of parameters. */ - inheritedPseudoElements?: InheritedPseudoElementMatches[]; + parameters: CSSFunctionParameter[]; /** - * A list of CSS keyframed animations matching this node. + * Function body. */ - cssKeyframesRules?: CSSKeyframesRule[]; + children: CSSFunctionNode[]; + } + /** + * CSS keyframe rule representation. + */ + export interface CSSKeyframeRule { /** - * A list of CSS @position-try rules matching this node, based on the position-try-fallbacks property. + * The css style sheet identifier (absent for user agent stylesheet and user-specified +stylesheet rules) this rule came from. */ - cssPositionTryRules?: CSSPositionTryRule[]; + styleSheetId?: StyleSheetId; /** - * Index of the active fallback in the applied position-try-fallback property, -will not be set if there is no active position-try fallback. + * Parent stylesheet's origin. */ - activePositionFallbackIndex?: number; + origin: StyleSheetOrigin; /** - * A list of CSS at-property rules matching this node. + * Associated key text. */ - cssPropertyRules?: CSSPropertyRule[]; + keyText: Value; /** - * A list of CSS property registrations matching this node. + * Associated style declaration. */ - cssPropertyRegistrations?: CSSPropertyRegistration[]; + style: CSSStyle; + } + /** + * A descriptor of operation to mutate style declaration text. + */ + export interface StyleDeclarationEdit { /** - * A font-palette-values rule matching this node. + * The css style sheet identifier. */ - cssFontPaletteValuesRule?: CSSFontPaletteValuesRule; + styleSheetId: StyleSheetId; /** - * Id of the first parent element that does not have display: contents. + * The range of the style text in the enclosing stylesheet. */ - parentLayoutNodeId?: DOM.NodeId; + range: SourceRange; /** - * A list of CSS at-function rules referenced by styles of this node. + * New style text. */ - cssFunctionRules?: CSSFunctionRule[]; + text: string; } + /** - * Returns the values of the default UA-defined environment variables used in env() + * Fires whenever a web font is updated. A non-empty font parameter indicates a successfully loaded +web font. */ - export type getEnvironmentVariablesParameters = { - } - export type getEnvironmentVariablesReturnValue = { - environmentVariables: { [key: string]: string }; + export type fontsUpdatedPayload = { + /** + * The web font that has loaded. + */ + font?: FontFace; } /** - * Returns all media queries parsed by the rendering engine. - */ - export type getMediaQueriesParameters = { - } - export type getMediaQueriesReturnValue = { - medias: CSSMedia[]; - } - /** - * Requests information about platform fonts which we used to render child TextNodes in the given -node. + * Fires whenever a MediaQuery result changes (for example, after a browser window has been +resized.) The current implementation considers only viewport-dependent media features. */ - export type getPlatformFontsForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getPlatformFontsForNodeReturnValue = { - /** - * Usage statistics for every employed platform font. - */ - fonts: PlatformFontUsage[]; - } + export type mediaQueryResultChangedPayload = void; /** - * Returns the current textual content for a stylesheet. + * Fired whenever an active document stylesheet is added. */ - export type getStyleSheetTextParameters = { - styleSheetId: StyleSheetId; - } - export type getStyleSheetTextReturnValue = { + export type styleSheetAddedPayload = { /** - * The stylesheet text. + * Added stylesheet metainfo. */ - text: string; - } - /** - * Returns all layers parsed by the rendering engine for the tree scope of a node. -Given a DOM element identified by nodeId, getLayersForNode returns the root -layer for the nearest ancestor document or shadow root. The layer root contains -the full layer tree for the tree scope and their ordering. - */ - export type getLayersForNodeParameters = { - nodeId: DOM.NodeId; - } - export type getLayersForNodeReturnValue = { - rootLayer: CSSLayerData; + header: CSSStyleSheetHeader; } /** - * Given a CSS selector text and a style sheet ID, getLocationForSelector -returns an array of locations of the CSS selector in the style sheet. + * Fired whenever a stylesheet is changed as a result of the client operation. */ - export type getLocationForSelectorParameters = { + export type styleSheetChangedPayload = { styleSheetId: StyleSheetId; - selectorText: string; - } - export type getLocationForSelectorReturnValue = { - ranges: SourceRange[]; - } - /** - * Starts tracking the given node for the computed style updates -and whenever the computed style is updated for node, it queues -a `computedStyleUpdated` event with throttling. -There can only be 1 node tracked for computed style updates -so passing a new node id removes tracking from the previous node. -Pass `undefined` to disable tracking. - */ - export type trackComputedStyleUpdatesForNodeParameters = { - nodeId?: DOM.NodeId; - } - export type trackComputedStyleUpdatesForNodeReturnValue = { - } - /** - * Starts tracking the given computed styles for updates. The specified array of properties -replaces the one previously specified. Pass empty array to disable tracking. -Use takeComputedStyleUpdates to retrieve the list of nodes that had properties modified. -The changes to computed style properties are only tracked for nodes pushed to the front-end -by the DOM agent. If no changes to the tracked properties occur after the node has been pushed -to the front-end, no updates will be issued for the node. - */ - export type trackComputedStyleUpdatesParameters = { - propertiesToTrack: CSSComputedStyleProperty[]; - } - export type trackComputedStyleUpdatesReturnValue = { } /** - * Polls the next batch of computed style updates. + * Fired whenever an active document stylesheet is removed. */ - export type takeComputedStyleUpdatesParameters = { - } - export type takeComputedStyleUpdatesReturnValue = { + export type styleSheetRemovedPayload = { /** - * The list of node Ids that have their tracked computed styles updated. + * Identifier of the removed stylesheet. */ - nodeIds: DOM.NodeId[]; + styleSheetId: StyleSheetId; } - /** - * Find a rule with the given active property for the given node and set the new value for this -property - */ - export type setEffectivePropertyValueForNodeParameters = { + export type computedStyleUpdatedPayload = { /** - * The element id for which to set property. + * The node id that has updated computed styles. */ nodeId: DOM.NodeId; - propertyName: string; - value: string; - } - export type setEffectivePropertyValueForNodeReturnValue = { } + /** - * Modifies the property rule property name. + * Inserts a new rule with the given `ruleText` in a stylesheet with given `styleSheetId`, at the +position specified by `location`. */ - export type setPropertyRulePropertyNameParameters = { - styleSheetId: StyleSheetId; - range: SourceRange; - propertyName: string; - } - export type setPropertyRulePropertyNameReturnValue = { + export type addRuleParameters = { /** - * The resulting key text after modification. + * The css style sheet identifier where a new rule should be inserted. */ - propertyName: Value; - } - /** - * Modifies the keyframe rule key text. - */ - export type setKeyframeKeyParameters = { styleSheetId: StyleSheetId; - range: SourceRange; - keyText: string; - } - export type setKeyframeKeyReturnValue = { /** - * The resulting key text after modification. + * The text of a new rule. */ - keyText: Value; - } - /** - * Modifies the rule selector. - */ - export type setMediaTextParameters = { - styleSheetId: StyleSheetId; - range: SourceRange; - text: string; + ruleText: string; + /** + * Text position of a new rule in the target style sheet. + */ + location: SourceRange; + /** + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. + */ + nodeForPropertySyntaxValidation?: DOM.NodeId; } - export type setMediaTextReturnValue = { + export type addRuleReturnValue = { /** - * The resulting CSS media rule after modification. + * The newly created rule. */ - media: CSSMedia; + rule: CSSRule; } /** - * Modifies the expression of a container query. + * Returns all class names from specified stylesheet. */ - export type setContainerQueryTextParameters = { + export type collectClassNamesParameters = { styleSheetId: StyleSheetId; - range: SourceRange; - text: string; } - export type setContainerQueryTextReturnValue = { + export type collectClassNamesReturnValue = { /** - * The resulting CSS container query rule after modification. + * Class name list. */ - containerQuery: CSSContainerQuery; + classNames: string[]; } /** - * Modifies the expression of a supports at-rule. + * Creates a new special "via-inspector" stylesheet in the frame with given `frameId`. */ - export type setSupportsTextParameters = { - styleSheetId: StyleSheetId; - range: SourceRange; - text: string; + export type createStyleSheetParameters = { + /** + * Identifier of the frame where "via-inspector" stylesheet should be created. + */ + frameId: Page.FrameId; + /** + * If true, creates a new stylesheet for every call. If false, +returns a stylesheet previously created by a call with force=false +for the frame's document if it exists or creates a new stylesheet +(default: false). + */ + force?: boolean; } - export type setSupportsTextReturnValue = { + export type createStyleSheetReturnValue = { /** - * The resulting CSS Supports rule after modification. + * Identifier of the created "via-inspector" stylesheet. */ - supports: CSSSupports; + styleSheetId: StyleSheetId; } /** - * Modifies the expression of a scope at-rule. + * Disables the CSS agent for the given page. */ - export type setScopeTextParameters = { - styleSheetId: StyleSheetId; - range: SourceRange; - text: string; + export type disableParameters = { } - export type setScopeTextReturnValue = { - /** - * The resulting CSS Scope rule after modification. - */ - scope: CSSScope; + export type disableReturnValue = { } /** - * Modifies the rule selector. + * Enables the CSS agent for the given page. Clients should not assume that the CSS agent has been +enabled until the result of this command is received. */ - export type setRuleSelectorParameters = { - styleSheetId: StyleSheetId; - range: SourceRange; - selector: string; + export type enableParameters = { } - export type setRuleSelectorReturnValue = { - /** - * The resulting selector list after modification. - */ - selectorList: SelectorList; + export type enableReturnValue = { } /** - * Sets the new stylesheet text. + * Ensures that the given node will have specified pseudo-classes whenever its style is computed by +the browser. */ - export type setStyleSheetTextParameters = { - styleSheetId: StyleSheetId; - text: string; - } - export type setStyleSheetTextReturnValue = { + export type forcePseudoStateParameters = { /** - * URL of source map associated with script (if any). + * The element id for which to force the pseudo state. */ - sourceMapURL?: string; + nodeId: DOM.NodeId; + /** + * Element pseudo classes to force when computing the element's style. + */ + forcedPseudoClasses: string[]; + } + export type forcePseudoStateReturnValue = { } /** - * Applies specified style edits one after another in the given order. + * Ensures that the given node is in its starting-style state. */ - export type setStyleTextsParameters = { - edits: StyleDeclarationEdit[]; + export type forceStartingStyleParameters = { /** - * NodeId for the DOM node in whose context custom property declarations for registered properties should be -validated. If omitted, declarations in the new rule text can only be validated statically, which may produce -incorrect results if the declaration contains a var() for example. + * The element id for which to force the starting-style state. */ - nodeForPropertySyntaxValidation?: DOM.NodeId; - } - export type setStyleTextsReturnValue = { + nodeId: DOM.NodeId; /** - * The resulting styles after modification. + * Boolean indicating if this is on or off. */ - styles: CSSStyle[]; + forced: boolean; } - /** - * Enables the selector recording. - */ - export type startRuleUsageTrackingParameters = { + export type forceStartingStyleReturnValue = { } - export type startRuleUsageTrackingReturnValue = { - } - /** - * Stop tracking rule usage and return the list of rules that were used since last call to -`takeCoverageDelta` (or since start of coverage instrumentation). - */ - export type stopRuleUsageTrackingParameters = { - } - export type stopRuleUsageTrackingReturnValue = { - ruleUsage: RuleUsage[]; - } - /** - * Obtain list of rules that became used since last call to this method (or since start of coverage -instrumentation). - */ - export type takeCoverageDeltaParameters = { - } - export type takeCoverageDeltaReturnValue = { - coverage: RuleUsage[]; - /** - * Monotonically increasing time, in seconds. - */ - timestamp: number; - } - /** - * Enables/disables rendering of local CSS fonts (enabled by default). - */ - export type setLocalFontsEnabledParameters = { + export type getBackgroundColorsParameters = { /** - * Whether rendering of local fonts is enabled. + * Id of the node to get background colors for. */ - enabled: boolean; - } - export type setLocalFontsEnabledReturnValue = { + nodeId: DOM.NodeId; } - } - - export module CacheStorage { - /** - * Unique identifier of the Cache object. - */ - export type CacheId = string; - /** - * type of HTTP response cached - */ - export type CachedResponseType = "basic"|"cors"|"default"|"error"|"opaqueResponse"|"opaqueRedirect"; - /** - * Data entry. - */ - export interface DataEntry { - /** - * Request URL. - */ - requestURL: string; - /** - * Request method. - */ - requestMethod: string; - /** - * Request headers - */ - requestHeaders: Header[]; + export type getBackgroundColorsReturnValue = { /** - * Number of seconds since epoch. + * The range of background colors behind this element, if it contains any visible text. If no +visible text is present, this will be undefined. In the case of a flat background color, +this will consist of simply that color. In the case of a gradient, this will consist of each +of the color stops. For anything more complicated, this will be an empty array. Images will +be ignored (as if the image had failed to load). */ - responseTime: number; + backgroundColors?: string[]; /** - * HTTP response status code. + * The computed font size for this node, as a CSS computed value string (e.g. '12px'). */ - responseStatus: number; + computedFontSize?: string; /** - * HTTP response status text. + * The computed font weight for this node, as a CSS computed value string (e.g. 'normal' or +'100'). */ - responseStatusText: string; + computedFontWeight?: string; + } + /** + * Returns the computed style for a DOM node identified by `nodeId`. + */ + export type getComputedStyleForNodeParameters = { + nodeId: DOM.NodeId; + } + export type getComputedStyleForNodeReturnValue = { /** - * HTTP response type + * Computed style for the specified DOM node. */ - responseType: CachedResponseType; + computedStyle: CSSComputedStyleProperty[]; /** - * Response headers + * A list of non-standard "extra fields" which blink stores alongside each +computed style. */ - responseHeaders: Header[]; + extraFields: ComputedStyleExtraFields; } /** - * Cache identifier. + * Resolve the specified values in the context of the provided element. +For example, a value of '1em' is evaluated according to the computed +'font-size' of the element and a value 'calc(1px + 2px)' will be +resolved to '3px'. +If the `propertyName` was specified the `values` are resolved as if +they were property's declaration. If a value cannot be parsed according +to the provided property syntax, the value is parsed using combined +syntax as if null `propertyName` was provided. If the value cannot be +resolved even then, return the provided value without any changes. */ - export interface Cache { + export type resolveValuesParameters = { /** - * An opaque unique id of the cache. + * Cascade-dependent keywords (revert/revert-layer) do not work. */ - cacheId: CacheId; + values: string[]; /** - * Security origin of the cache. + * Id of the node in whose context the expression is evaluated */ - securityOrigin: string; + nodeId: DOM.NodeId; /** - * Storage key of the cache. + * Only longhands and custom property names are accepted. */ - storageKey: string; + propertyName?: string; /** - * Storage bucket of the cache. + * Pseudo element type, only works for pseudo elements that generate +elements in the tree, such as ::before and ::after. */ - storageBucket?: Storage.StorageBucket; + pseudoType?: DOM.PseudoType; /** - * The name of the cache. + * Pseudo element custom ident. */ - cacheName: string; + pseudoIdentifier?: string; } - export interface Header { - name: string; + export type resolveValuesReturnValue = { + results: string[]; + } + export type getLonghandPropertiesParameters = { + shorthandName: string; value: string; } + export type getLonghandPropertiesReturnValue = { + longhandProperties: CSSProperty[]; + } /** - * Cached response + * Returns the styles defined inline (explicitly in the "style" attribute and implicitly, using DOM +attributes) for a DOM node identified by `nodeId`. */ - export interface CachedResponse { + export type getInlineStylesForNodeParameters = { + nodeId: DOM.NodeId; + } + export type getInlineStylesForNodeReturnValue = { /** - * Entry content, base64-encoded. + * Inline style for the specified DOM node. */ - body: binary; - } - - - /** - * Deletes a cache. - */ - export type deleteCacheParameters = { + inlineStyle?: CSSStyle; /** - * Id of cache for deletion. + * Attribute-defined element style (e.g. resulting from "width=20 height=100%"). */ - cacheId: CacheId; - } - export type deleteCacheReturnValue = { + attributesStyle?: CSSStyle; } /** - * Deletes a cache entry. + * Returns the styles coming from animations & transitions +including the animation & transition styles coming from inheritance chain. */ - export type deleteEntryParameters = { + export type getAnimatedStylesForNodeParameters = { + nodeId: DOM.NodeId; + } + export type getAnimatedStylesForNodeReturnValue = { /** - * Id of cache where the entry will be deleted. + * Styles coming from animations. */ - cacheId: CacheId; + animationStyles?: CSSAnimationStyle[]; /** - * URL spec of the request. + * Style coming from transitions. */ - request: string; - } - export type deleteEntryReturnValue = { + transitionsStyle?: CSSStyle; + /** + * Inherited style entries for animationsStyle and transitionsStyle from +the inheritance chain of the element. + */ + inherited?: InheritedAnimatedStyleEntry[]; } /** - * Requests cache names. + * Returns requested styles for a DOM node identified by `nodeId`. */ - export type requestCacheNamesParameters = { + export type getMatchedStylesForNodeParameters = { + nodeId: DOM.NodeId; + } + export type getMatchedStylesForNodeReturnValue = { /** - * At least and at most one of securityOrigin, storageKey, storageBucket must be specified. -Security origin. + * Inline style for the specified DOM node. */ - securityOrigin?: string; + inlineStyle?: CSSStyle; /** - * Storage key. + * Attribute-defined element style (e.g. resulting from "width=20 height=100%"). */ - storageKey?: string; + attributesStyle?: CSSStyle; /** - * Storage bucket. If not specified, it uses the default bucket. + * CSS rules matching this node, from all applicable stylesheets. */ - storageBucket?: Storage.StorageBucket; - } - export type requestCacheNamesReturnValue = { + matchedCSSRules?: RuleMatch[]; /** - * Caches for the security origin. + * Pseudo style matches for this node. */ - caches: Cache[]; - } - /** - * Fetches cache entry. - */ - export type requestCachedResponseParameters = { + pseudoElements?: PseudoElementMatches[]; /** - * Id of cache that contains the entry. + * A chain of inherited styles (from the immediate node parent up to the DOM tree root). */ - cacheId: CacheId; + inherited?: InheritedStyleEntry[]; /** - * URL spec of the request. + * A chain of inherited pseudo element styles (from the immediate node parent up to the DOM tree root). */ - requestURL: string; + inheritedPseudoElements?: InheritedPseudoElementMatches[]; /** - * headers of the request. + * A list of CSS keyframed animations matching this node. */ - requestHeaders: Header[]; - } - export type requestCachedResponseReturnValue = { + cssKeyframesRules?: CSSKeyframesRule[]; /** - * Response read from the cache. + * A list of CSS @position-try rules matching this node, based on the position-try-fallbacks property. */ - response: CachedResponse; - } - /** - * Requests data from cache. - */ - export type requestEntriesParameters = { + cssPositionTryRules?: CSSPositionTryRule[]; /** - * ID of cache to get entries from. + * Index of the active fallback in the applied position-try-fallback property, +will not be set if there is no active position-try fallback. */ - cacheId: CacheId; + activePositionFallbackIndex?: number; /** - * Number of records to skip. + * A list of CSS at-property rules matching this node. */ - skipCount?: number; + cssPropertyRules?: CSSPropertyRule[]; /** - * Number of records to fetch. + * A list of CSS property registrations matching this node. */ - pageSize?: number; + cssPropertyRegistrations?: CSSPropertyRegistration[]; /** - * If present, only return the entries containing this substring in the path + * A font-palette-values rule matching this node. */ - pathFilter?: string; - } - export type requestEntriesReturnValue = { + cssFontPaletteValuesRule?: CSSFontPaletteValuesRule; /** - * Array of object store data entries. - */ - cacheDataEntries: DataEntry[]; - /** - * Count of returned entries from this storage. If pathFilter is empty, it -is the count of all entries from this storage. + * Id of the first parent element that does not have display: contents. */ - returnCount: number; - } - } - - /** - * A domain for interacting with Cast, Presentation API, and Remote Playback API -functionalities. - */ - export module Cast { - export interface Sink { - name: string; - id: string; + parentLayoutNodeId?: DOM.NodeId; /** - * Text describing the current session. Present only if there is an active -session on the sink. + * A list of CSS at-function rules referenced by styles of this node. */ - session?: string; + cssFunctionRules?: CSSFunctionRule[]; } - /** - * This is fired whenever the list of available sinks changes. A sink is a -device or a software surface that you can cast to. + * Returns the values of the default UA-defined environment variables used in env() */ - export type sinksUpdatedPayload = { - sinks: Sink[]; + export type getEnvironmentVariablesParameters = { } - /** - * This is fired whenever the outstanding issue/error message changes. -|issueMessage| is empty if there is no issue. - */ - export type issueUpdatedPayload = { - issueMessage: string; + export type getEnvironmentVariablesReturnValue = { + environmentVariables: { [key: string]: string }; } - /** - * Starts observing for sinks that can be used for tab mirroring, and if set, -sinks compatible with |presentationUrl| as well. When sinks are found, a -|sinksUpdated| event is fired. -Also starts observing for issue messages. When an issue is added or removed, -an |issueUpdated| event is fired. + * Returns all media queries parsed by the rendering engine. */ - export type enableParameters = { - presentationUrl?: string; + export type getMediaQueriesParameters = { } - export type enableReturnValue = { + export type getMediaQueriesReturnValue = { + medias: CSSMedia[]; } /** - * Stops observing for sinks and issues. + * Requests information about platform fonts which we used to render child TextNodes in the given +node. */ - export type disableParameters = { + export type getPlatformFontsForNodeParameters = { + nodeId: DOM.NodeId; } - export type disableReturnValue = { + export type getPlatformFontsForNodeReturnValue = { + /** + * Usage statistics for every employed platform font. + */ + fonts: PlatformFontUsage[]; } /** - * Sets a sink to be used when the web page requests the browser to choose a -sink via Presentation API, Remote Playback API, or Cast SDK. + * Returns the current textual content for a stylesheet. */ - export type setSinkToUseParameters = { - sinkName: string; + export type getStyleSheetTextParameters = { + styleSheetId: StyleSheetId; } - export type setSinkToUseReturnValue = { + export type getStyleSheetTextReturnValue = { + /** + * The stylesheet text. + */ + text: string; } /** - * Starts mirroring the desktop to the sink. + * Returns all layers parsed by the rendering engine for the tree scope of a node. +Given a DOM element identified by nodeId, getLayersForNode returns the root +layer for the nearest ancestor document or shadow root. The layer root contains +the full layer tree for the tree scope and their ordering. */ - export type startDesktopMirroringParameters = { - sinkName: string; + export type getLayersForNodeParameters = { + nodeId: DOM.NodeId; } - export type startDesktopMirroringReturnValue = { + export type getLayersForNodeReturnValue = { + rootLayer: CSSLayerData; } /** - * Starts mirroring the tab to the sink. + * Given a CSS selector text and a style sheet ID, getLocationForSelector +returns an array of locations of the CSS selector in the style sheet. */ - export type startTabMirroringParameters = { - sinkName: string; + export type getLocationForSelectorParameters = { + styleSheetId: StyleSheetId; + selectorText: string; } - export type startTabMirroringReturnValue = { + export type getLocationForSelectorReturnValue = { + ranges: SourceRange[]; } /** - * Stops the active Cast session on the sink. + * Starts tracking the given node for the computed style updates +and whenever the computed style is updated for node, it queues +a `computedStyleUpdated` event with throttling. +There can only be 1 node tracked for computed style updates +so passing a new node id removes tracking from the previous node. +Pass `undefined` to disable tracking. */ - export type stopCastingParameters = { - sinkName: string; + export type trackComputedStyleUpdatesForNodeParameters = { + nodeId?: DOM.NodeId; } - export type stopCastingReturnValue = { + export type trackComputedStyleUpdatesForNodeReturnValue = { } - } - - /** - * This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object -that has an `id`. This `id` can be used to get additional information on the Node, resolve it into -the JavaScript object wrapper, etc. It is important that client receives DOM events only for the -nodes that are known to the client. Backend keeps track of the nodes that were sent to the client -and never sends the same node twice. It is client's responsibility to collect information about -the nodes that were sent to the client. Note that `iframe` owner elements will return -corresponding document elements as their child nodes. - */ - export module DOM { - /** - * Unique DOM node identifier. - */ - export type NodeId = number; /** - * Unique DOM node identifier used to reference a node that may not have been pushed to the -front-end. + * Starts tracking the given computed styles for updates. The specified array of properties +replaces the one previously specified. Pass empty array to disable tracking. +Use takeComputedStyleUpdates to retrieve the list of nodes that had properties modified. +The changes to computed style properties are only tracked for nodes pushed to the front-end +by the DOM agent. If no changes to the tracked properties occur after the node has been pushed +to the front-end, no updates will be issued for the node. */ - export type BackendNodeId = number; + export type trackComputedStyleUpdatesParameters = { + propertiesToTrack: CSSComputedStyleProperty[]; + } + export type trackComputedStyleUpdatesReturnValue = { + } /** - * Backend node with a friendly name. + * Polls the next batch of computed style updates. */ - export interface BackendNode { - /** - * `Node`'s nodeType. - */ - nodeType: number; + export type takeComputedStyleUpdatesParameters = { + } + export type takeComputedStyleUpdatesReturnValue = { /** - * `Node`'s nodeName. + * The list of node Ids that have their tracked computed styles updated. */ - nodeName: string; - backendNodeId: BackendNodeId; + nodeIds: DOM.NodeId[]; } /** - * Pseudo element type. + * Find a rule with the given active property for the given node and set the new value for this +property */ - export type PseudoType = "first-line"|"first-letter"|"checkmark"|"before"|"after"|"picker-icon"|"marker"|"backdrop"|"column"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-group-children"|"view-transition-old"|"view-transition-new"|"placeholder"|"file-selector-button"|"details-content"|"picker"|"permission-icon"; + export type setEffectivePropertyValueForNodeParameters = { + /** + * The element id for which to set property. + */ + nodeId: DOM.NodeId; + propertyName: string; + value: string; + } + export type setEffectivePropertyValueForNodeReturnValue = { + } /** - * Shadow root type. + * Modifies the property rule property name. */ - export type ShadowRootType = "user-agent"|"open"|"closed"; + export type setPropertyRulePropertyNameParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + propertyName: string; + } + export type setPropertyRulePropertyNameReturnValue = { + /** + * The resulting key text after modification. + */ + propertyName: Value; + } /** - * Document compatibility mode. + * Modifies the keyframe rule key text. */ - export type CompatibilityMode = "QuirksMode"|"LimitedQuirksMode"|"NoQuirksMode"; + export type setKeyframeKeyParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + keyText: string; + } + export type setKeyframeKeyReturnValue = { + /** + * The resulting key text after modification. + */ + keyText: Value; + } /** - * ContainerSelector physical axes + * Modifies the rule selector. */ - export type PhysicalAxes = "Horizontal"|"Vertical"|"Both"; + export type setMediaTextParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + text: string; + } + export type setMediaTextReturnValue = { + /** + * The resulting CSS media rule after modification. + */ + media: CSSMedia; + } /** - * ContainerSelector logical axes + * Modifies the expression of a container query. */ - export type LogicalAxes = "Inline"|"Block"|"Both"; + export type setContainerQueryTextParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + text: string; + } + export type setContainerQueryTextReturnValue = { + /** + * The resulting CSS container query rule after modification. + */ + containerQuery: CSSContainerQuery; + } /** - * Physical scroll orientation + * Modifies the expression of a supports at-rule. */ - export type ScrollOrientation = "horizontal"|"vertical"; + export type setSupportsTextParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + text: string; + } + export type setSupportsTextReturnValue = { + /** + * The resulting CSS Supports rule after modification. + */ + supports: CSSSupports; + } /** - * DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. -DOMNode is a base node mirror type. + * Modifies the expression of a scope at-rule. */ - export interface Node { + export type setScopeTextParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + text: string; + } + export type setScopeTextReturnValue = { /** - * Node identifier that is passed into the rest of the DOM messages as the `nodeId`. Backend -will only push node with given `id` once. It is aware of all requested nodes and will only -fire DOM events for nodes known to the client. + * The resulting CSS Scope rule after modification. */ - nodeId: NodeId; - /** - * The id of the parent node if any. - */ - parentId?: NodeId; + scope: CSSScope; + } + /** + * Modifies the rule selector. + */ + export type setRuleSelectorParameters = { + styleSheetId: StyleSheetId; + range: SourceRange; + selector: string; + } + export type setRuleSelectorReturnValue = { /** - * The BackendNodeId for this node. + * The resulting selector list after modification. */ - backendNodeId: BackendNodeId; + selectorList: SelectorList; + } + /** + * Sets the new stylesheet text. + */ + export type setStyleSheetTextParameters = { + styleSheetId: StyleSheetId; + text: string; + } + export type setStyleSheetTextReturnValue = { /** - * `Node`'s nodeType. + * URL of source map associated with script (if any). */ - nodeType: number; + sourceMapURL?: string; + } + /** + * Applies specified style edits one after another in the given order. + */ + export type setStyleTextsParameters = { + edits: StyleDeclarationEdit[]; /** - * `Node`'s nodeName. + * NodeId for the DOM node in whose context custom property declarations for registered properties should be +validated. If omitted, declarations in the new rule text can only be validated statically, which may produce +incorrect results if the declaration contains a var() for example. */ - nodeName: string; + nodeForPropertySyntaxValidation?: DOM.NodeId; + } + export type setStyleTextsReturnValue = { /** - * `Node`'s localName. + * The resulting styles after modification. */ - localName: string; + styles: CSSStyle[]; + } + /** + * Enables the selector recording. + */ + export type startRuleUsageTrackingParameters = { + } + export type startRuleUsageTrackingReturnValue = { + } + /** + * Stop tracking rule usage and return the list of rules that were used since last call to +`takeCoverageDelta` (or since start of coverage instrumentation). + */ + export type stopRuleUsageTrackingParameters = { + } + export type stopRuleUsageTrackingReturnValue = { + ruleUsage: RuleUsage[]; + } + /** + * Obtain list of rules that became used since last call to this method (or since start of coverage +instrumentation). + */ + export type takeCoverageDeltaParameters = { + } + export type takeCoverageDeltaReturnValue = { + coverage: RuleUsage[]; /** - * `Node`'s nodeValue. + * Monotonically increasing time, in seconds. */ - nodeValue: string; + timestamp: number; + } + /** + * Enables/disables rendering of local CSS fonts (enabled by default). + */ + export type setLocalFontsEnabledParameters = { /** - * Child count for `Container` nodes. + * Whether rendering of local fonts is enabled. */ - childNodeCount?: number; + enabled: boolean; + } + export type setLocalFontsEnabledReturnValue = { + } + } + + export module CacheStorage { + /** + * Unique identifier of the Cache object. + */ + export type CacheId = string; + /** + * type of HTTP response cached + */ + export type CachedResponseType = "basic"|"cors"|"default"|"error"|"opaqueResponse"|"opaqueRedirect"; + /** + * Data entry. + */ + export interface DataEntry { /** - * Child nodes of this node when requested with children. + * Request URL. */ - children?: Node[]; + requestURL: string; /** - * Attributes of the `Element` node in the form of flat array `[name1, value1, name2, value2]`. + * Request method. */ - attributes?: string[]; + requestMethod: string; /** - * Document URL that `Document` or `FrameOwner` node points to. + * Request headers */ - documentURL?: string; + requestHeaders: Header[]; /** - * Base URL that `Document` or `FrameOwner` node uses for URL completion. + * Number of seconds since epoch. */ - baseURL?: string; + responseTime: number; /** - * `DocumentType`'s publicId. + * HTTP response status code. */ - publicId?: string; + responseStatus: number; /** - * `DocumentType`'s systemId. + * HTTP response status text. */ - systemId?: string; + responseStatusText: string; /** - * `DocumentType`'s internalSubset. + * HTTP response type */ - internalSubset?: string; + responseType: CachedResponseType; /** - * `Document`'s XML version in case of XML documents. + * Response headers */ - xmlVersion?: string; + responseHeaders: Header[]; + } + /** + * Cache identifier. + */ + export interface Cache { /** - * `Attr`'s name. + * An opaque unique id of the cache. */ - name?: string; + cacheId: CacheId; /** - * `Attr`'s value. + * Security origin of the cache. */ - value?: string; + securityOrigin: string; /** - * Pseudo element type for this node. + * Storage key of the cache. */ - pseudoType?: PseudoType; + storageKey: string; /** - * Pseudo element identifier for this node. Only present if there is a -valid pseudoType. + * Storage bucket of the cache. */ - pseudoIdentifier?: string; + storageBucket?: Storage.StorageBucket; /** - * Shadow root type. + * The name of the cache. */ - shadowRootType?: ShadowRootType; + cacheName: string; + } + export interface Header { + name: string; + value: string; + } + /** + * Cached response + */ + export interface CachedResponse { /** - * Frame ID for frame owner elements. + * Entry content, base64-encoded. */ - frameId?: Page.FrameId; + body: binary; + } + + + /** + * Deletes a cache. + */ + export type deleteCacheParameters = { /** - * Content document for frame owner elements. + * Id of cache for deletion. */ - contentDocument?: Node; + cacheId: CacheId; + } + export type deleteCacheReturnValue = { + } + /** + * Deletes a cache entry. + */ + export type deleteEntryParameters = { /** - * Shadow root list for given element host. + * Id of cache where the entry will be deleted. */ - shadowRoots?: Node[]; + cacheId: CacheId; /** - * Content document fragment for template elements. + * URL spec of the request. */ - templateContent?: Node; + request: string; + } + export type deleteEntryReturnValue = { + } + /** + * Requests cache names. + */ + export type requestCacheNamesParameters = { /** - * Pseudo elements associated with this node. + * At least and at most one of securityOrigin, storageKey, storageBucket must be specified. +Security origin. */ - pseudoElements?: Node[]; + securityOrigin?: string; /** - * Deprecated, as the HTML Imports API has been removed (crbug.com/937746). -This property used to return the imported document for the HTMLImport links. -The property is always undefined now. + * Storage key. */ - importedDocument?: Node; + storageKey?: string; /** - * Distributed nodes for given insertion point. + * Storage bucket. If not specified, it uses the default bucket. */ - distributedNodes?: BackendNode[]; + storageBucket?: Storage.StorageBucket; + } + export type requestCacheNamesReturnValue = { /** - * Whether the node is SVG. + * Caches for the security origin. */ - isSVG?: boolean; - compatibilityMode?: CompatibilityMode; - assignedSlot?: BackendNode; - isScrollable?: boolean; - } - /** - * A structure to hold the top-level node of a detached tree and an array of its retained descendants. - */ - export interface DetachedElementInfo { - treeNode: Node; - retainedNodeIds: NodeId[]; + caches: Cache[]; } /** - * A structure holding an RGBA color. + * Fetches cache entry. */ - export interface RGBA { + export type requestCachedResponseParameters = { /** - * The red component, in the [0-255] range. + * Id of cache that contains the entry. */ - r: number; + cacheId: CacheId; /** - * The green component, in the [0-255] range. + * URL spec of the request. */ - g: number; + requestURL: string; /** - * The blue component, in the [0-255] range. + * headers of the request. */ - b: number; + requestHeaders: Header[]; + } + export type requestCachedResponseReturnValue = { /** - * The alpha component, in the [0-1] range (default: 1). + * Response read from the cache. */ - a?: number; + response: CachedResponse; } /** - * An array of quad vertices, x immediately followed by y for each point, points clock-wise. - */ - export type Quad = number[]; - /** - * Box model. + * Requests data from cache. */ - export interface BoxModel { + export type requestEntriesParameters = { /** - * Content box + * ID of cache to get entries from. */ - content: Quad; + cacheId: CacheId; /** - * Padding box + * Number of records to skip. */ - padding: Quad; + skipCount?: number; /** - * Border box + * Number of records to fetch. */ - border: Quad; + pageSize?: number; /** - * Margin box + * If present, only return the entries containing this substring in the path */ - margin: Quad; + pathFilter?: string; + } + export type requestEntriesReturnValue = { /** - * Node width + * Array of object store data entries. */ - width: number; + cacheDataEntries: DataEntry[]; /** - * Node height + * Count of returned entries from this storage. If pathFilter is empty, it +is the count of all entries from this storage. */ - height: number; + returnCount: number; + } + } + + /** + * A domain for interacting with Cast, Presentation API, and Remote Playback API +functionalities. + */ + export module Cast { + export interface Sink { + name: string; + id: string; /** - * Shape outside coordinates + * Text describing the current session. Present only if there is an active +session on the sink. */ - shapeOutside?: ShapeOutsideInfo; + session?: string; } + /** - * CSS Shape Outside details. + * This is fired whenever the list of available sinks changes. A sink is a +device or a software surface that you can cast to. */ - export interface ShapeOutsideInfo { - /** - * Shape bounds - */ - bounds: Quad; - /** - * Shape coordinate details - */ - shape: any[]; - /** - * Margin shape bounds - */ - marginShape: any[]; + export type sinksUpdatedPayload = { + sinks: Sink[]; } /** - * Rectangle. + * This is fired whenever the outstanding issue/error message changes. +|issueMessage| is empty if there is no issue. */ - export interface Rect { - /** - * X coordinate - */ - x: number; - /** - * Y coordinate - */ - y: number; - /** - * Rectangle width - */ - width: number; - /** - * Rectangle height - */ - height: number; - } - export interface CSSComputedStyleProperty { - /** - * Computed style property name. - */ - name: string; - /** - * Computed style property value. - */ - value: string; + export type issueUpdatedPayload = { + issueMessage: string; } /** - * Fired when `Element`'s attribute is modified. + * Starts observing for sinks that can be used for tab mirroring, and if set, +sinks compatible with |presentationUrl| as well. When sinks are found, a +|sinksUpdated| event is fired. +Also starts observing for issue messages. When an issue is added or removed, +an |issueUpdated| event is fired. */ - export type attributeModifiedPayload = { - /** - * Id of the node that has changed. - */ - nodeId: NodeId; - /** - * Attribute name. - */ - name: string; - /** - * Attribute value. - */ - value: string; + export type enableParameters = { + presentationUrl?: string; + } + export type enableReturnValue = { } /** - * Fired when `Element`'s attribute is removed. + * Stops observing for sinks and issues. */ - export type attributeRemovedPayload = { - /** - * Id of the node that has changed. - */ - nodeId: NodeId; - /** - * A ttribute name. - */ - name: string; + export type disableParameters = { + } + export type disableReturnValue = { } /** - * Mirrors `DOMCharacterDataModified` event. + * Sets a sink to be used when the web page requests the browser to choose a +sink via Presentation API, Remote Playback API, or Cast SDK. */ - export type characterDataModifiedPayload = { - /** - * Id of the node that has changed. - */ - nodeId: NodeId; - /** - * New text value. - */ - characterData: string; + export type setSinkToUseParameters = { + sinkName: string; + } + export type setSinkToUseReturnValue = { } /** - * Fired when `Container`'s child node count has changed. + * Starts mirroring the desktop to the sink. */ - export type childNodeCountUpdatedPayload = { - /** - * Id of the node that has changed. - */ - nodeId: NodeId; - /** - * New node count. - */ - childNodeCount: number; + export type startDesktopMirroringParameters = { + sinkName: string; + } + export type startDesktopMirroringReturnValue = { } /** - * Mirrors `DOMNodeInserted` event. + * Starts mirroring the tab to the sink. */ - export type childNodeInsertedPayload = { - /** - * Id of the node that has changed. - */ - parentNodeId: NodeId; - /** - * Id of the previous sibling. - */ - previousNodeId: NodeId; - /** - * Inserted node data. - */ - node: Node; + export type startTabMirroringParameters = { + sinkName: string; + } + export type startTabMirroringReturnValue = { } /** - * Mirrors `DOMNodeRemoved` event. + * Stops the active Cast session on the sink. */ - export type childNodeRemovedPayload = { - /** - * Parent id. - */ - parentNodeId: NodeId; - /** - * Id of the node that has been removed. - */ - nodeId: NodeId; + export type stopCastingParameters = { + sinkName: string; + } + export type stopCastingReturnValue = { } + } + + /** + * This domain exposes DOM read/write operations. Each DOM Node is represented with its mirror object +that has an `id`. This `id` can be used to get additional information on the Node, resolve it into +the JavaScript object wrapper, etc. It is important that client receives DOM events only for the +nodes that are known to the client. Backend keeps track of the nodes that were sent to the client +and never sends the same node twice. It is client's responsibility to collect information about +the nodes that were sent to the client. Note that `iframe` owner elements will return +corresponding document elements as their child nodes. + */ + export module DOM { /** - * Called when distribution is changed. + * Unique DOM node identifier. */ - export type distributedNodesUpdatedPayload = { + export type NodeId = number; + /** + * Unique DOM node identifier used to reference a node that may not have been pushed to the +front-end. + */ + export type BackendNodeId = number; + /** + * Backend node with a friendly name. + */ + export interface BackendNode { /** - * Insertion point where distributed nodes were updated. + * `Node`'s nodeType. */ - insertionPointId: NodeId; + nodeType: number; /** - * Distributed nodes for given insertion point. + * `Node`'s nodeName. */ - distributedNodes: BackendNode[]; + nodeName: string; + backendNodeId: BackendNodeId; } /** - * Fired when `Document` has been totally updated. Node ids are no longer valid. + * Pseudo element type. */ - export type documentUpdatedPayload = void; + export type PseudoType = "first-line"|"first-letter"|"checkmark"|"before"|"after"|"picker-icon"|"interest-hint"|"marker"|"backdrop"|"column"|"selection"|"search-text"|"target-text"|"spelling-error"|"grammar-error"|"highlight"|"first-line-inherited"|"scroll-marker"|"scroll-marker-group"|"scroll-button"|"scrollbar"|"scrollbar-thumb"|"scrollbar-button"|"scrollbar-track"|"scrollbar-track-piece"|"scrollbar-corner"|"resizer"|"input-list-button"|"view-transition"|"view-transition-group"|"view-transition-image-pair"|"view-transition-group-children"|"view-transition-old"|"view-transition-new"|"placeholder"|"file-selector-button"|"details-content"|"picker"|"permission-icon"; /** - * Fired when `Element`'s inline style is modified via a CSS property modification. + * Shadow root type. */ - export type inlineStyleInvalidatedPayload = { - /** - * Ids of the nodes for which the inline styles have been invalidated. - */ - nodeIds: NodeId[]; - } + export type ShadowRootType = "user-agent"|"open"|"closed"; /** - * Called when a pseudo element is added to an element. + * Document compatibility mode. */ - export type pseudoElementAddedPayload = { - /** - * Pseudo element's parent element id. - */ - parentId: NodeId; - /** - * The added pseudo element. - */ - pseudoElement: Node; - } + export type CompatibilityMode = "QuirksMode"|"LimitedQuirksMode"|"NoQuirksMode"; /** - * Called when top layer elements are changed. + * ContainerSelector physical axes */ - export type topLayerElementsUpdatedPayload = void; + export type PhysicalAxes = "Horizontal"|"Vertical"|"Both"; /** - * Fired when a node's scrollability state changes. + * ContainerSelector logical axes */ - export type scrollableFlagUpdatedPayload = { + export type LogicalAxes = "Inline"|"Block"|"Both"; + /** + * Physical scroll orientation + */ + export type ScrollOrientation = "horizontal"|"vertical"; + /** + * DOM interaction is implemented in terms of mirror objects that represent the actual DOM nodes. +DOMNode is a base node mirror type. + */ + export interface Node { /** - * The id of the node. + * Node identifier that is passed into the rest of the DOM messages as the `nodeId`. Backend +will only push node with given `id` once. It is aware of all requested nodes and will only +fire DOM events for nodes known to the client. */ - nodeId: DOM.NodeId; + nodeId: NodeId; /** - * If the node is scrollable. + * The id of the parent node if any. */ - isScrollable: boolean; - } - /** - * Called when a pseudo element is removed from an element. - */ - export type pseudoElementRemovedPayload = { + parentId?: NodeId; /** - * Pseudo element's parent element id. + * The BackendNodeId for this node. */ - parentId: NodeId; + backendNodeId: BackendNodeId; /** - * The removed pseudo element id. + * `Node`'s nodeType. */ - pseudoElementId: NodeId; - } - /** - * Fired when backend wants to provide client with the missing DOM structure. This happens upon -most of the calls requesting node ids. - */ - export type setChildNodesPayload = { + nodeType: number; /** - * Parent node id to populate with children. + * `Node`'s nodeName. */ - parentId: NodeId; + nodeName: string; /** - * Child nodes array. + * `Node`'s localName. */ - nodes: Node[]; - } - /** - * Called when shadow root is popped from the element. - */ - export type shadowRootPoppedPayload = { + localName: string; /** - * Host element id. + * `Node`'s nodeValue. */ - hostId: NodeId; + nodeValue: string; /** - * Shadow root id. + * Child count for `Container` nodes. */ - rootId: NodeId; - } - /** - * Called when shadow root is pushed into the element. - */ - export type shadowRootPushedPayload = { + childNodeCount?: number; /** - * Host element id. + * Child nodes of this node when requested with children. */ - hostId: NodeId; + children?: Node[]; /** - * Shadow root. + * Attributes of the `Element` node in the form of flat array `[name1, value1, name2, value2]`. */ - root: Node; - } - - /** - * Collects class names for the node with given id and all of it's child nodes. - */ - export type collectClassNamesFromSubtreeParameters = { + attributes?: string[]; /** - * Id of the node to collect class names. + * Document URL that `Document` or `FrameOwner` node points to. */ - nodeId: NodeId; - } - export type collectClassNamesFromSubtreeReturnValue = { + documentURL?: string; /** - * Class name list. + * Base URL that `Document` or `FrameOwner` node uses for URL completion. */ - classNames: string[]; - } - /** - * Creates a deep copy of the specified node and places it into the target container before the -given anchor. - */ - export type copyToParameters = { + baseURL?: string; /** - * Id of the node to copy. + * `DocumentType`'s publicId. */ - nodeId: NodeId; + publicId?: string; /** - * Id of the element to drop the copy into. + * `DocumentType`'s systemId. */ - targetNodeId: NodeId; + systemId?: string; /** - * Drop the copy before this node (if absent, the copy becomes the last child of -`targetNodeId`). + * `DocumentType`'s internalSubset. */ - insertBeforeNodeId?: NodeId; - } - export type copyToReturnValue = { + internalSubset?: string; /** - * Id of the node clone. + * `Document`'s XML version in case of XML documents. */ - nodeId: NodeId; - } - /** - * Describes node given its id, does not require domain to be enabled. Does not start tracking any -objects, can be used for automation. - */ - export type describeNodeParameters = { + xmlVersion?: string; /** - * Identifier of the node. + * `Attr`'s name. */ - nodeId?: NodeId; + name?: string; /** - * Identifier of the backend node. + * `Attr`'s value. */ - backendNodeId?: BackendNodeId; + value?: string; /** - * JavaScript object id of the node wrapper. + * Pseudo element type for this node. */ - objectId?: Runtime.RemoteObjectId; + pseudoType?: PseudoType; /** - * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the -entire subtree or provide an integer larger than 0. + * Pseudo element identifier for this node. Only present if there is a +valid pseudoType. */ - depth?: number; + pseudoIdentifier?: string; /** - * Whether or not iframes and shadow roots should be traversed when returning the subtree -(default is false). + * Shadow root type. */ - pierce?: boolean; - } - export type describeNodeReturnValue = { + shadowRootType?: ShadowRootType; /** - * Node description. + * Frame ID for frame owner elements. */ - node: Node; - } - /** - * Scrolls the specified rect of the given node into view if not already visible. -Note: exactly one between nodeId, backendNodeId and objectId should be passed -to identify the node. - */ - export type scrollIntoViewIfNeededParameters = { + frameId?: Page.FrameId; /** - * Identifier of the node. + * Content document for frame owner elements. */ - nodeId?: NodeId; + contentDocument?: Node; /** - * Identifier of the backend node. + * Shadow root list for given element host. */ - backendNodeId?: BackendNodeId; + shadowRoots?: Node[]; /** - * JavaScript object id of the node wrapper. + * Content document fragment for template elements. */ - objectId?: Runtime.RemoteObjectId; + templateContent?: Node; /** - * The rect to be scrolled into view, relative to the node's border box, in CSS pixels. -When omitted, center of the node will be used, similar to Element.scrollIntoView. + * Pseudo elements associated with this node. */ - rect?: Rect; - } - export type scrollIntoViewIfNeededReturnValue = { - } - /** - * Disables DOM agent for the given page. - */ - export type disableParameters = { - } - export type disableReturnValue = { - } - /** - * Discards search results from the session with the given id. `getSearchResults` should no longer -be called for that search. - */ - export type discardSearchResultsParameters = { + pseudoElements?: Node[]; /** - * Unique search session identifier. + * Deprecated, as the HTML Imports API has been removed (crbug.com/937746). +This property used to return the imported document for the HTMLImport links. +The property is always undefined now. */ - searchId: string; - } - export type discardSearchResultsReturnValue = { - } - /** - * Enables DOM agent for the given page. - */ - export type enableParameters = { + importedDocument?: Node; /** - * Whether to include whitespaces in the children array of returned Nodes. + * Distributed nodes for given insertion point. */ - includeWhitespace?: "none"|"all"; + distributedNodes?: BackendNode[]; + /** + * Whether the node is SVG. + */ + isSVG?: boolean; + compatibilityMode?: CompatibilityMode; + assignedSlot?: BackendNode; + isScrollable?: boolean; } - export type enableReturnValue = { + /** + * A structure to hold the top-level node of a detached tree and an array of its retained descendants. + */ + export interface DetachedElementInfo { + treeNode: Node; + retainedNodeIds: NodeId[]; } /** - * Focuses the given element. + * A structure holding an RGBA color. */ - export type focusParameters = { + export interface RGBA { /** - * Identifier of the node. + * The red component, in the [0-255] range. */ - nodeId?: NodeId; + r: number; /** - * Identifier of the backend node. + * The green component, in the [0-255] range. */ - backendNodeId?: BackendNodeId; + g: number; /** - * JavaScript object id of the node wrapper. + * The blue component, in the [0-255] range. */ - objectId?: Runtime.RemoteObjectId; - } - export type focusReturnValue = { + b: number; + /** + * The alpha component, in the [0-1] range (default: 1). + */ + a?: number; } /** - * Returns attributes for the specified node. + * An array of quad vertices, x immediately followed by y for each point, points clock-wise. */ - export type getAttributesParameters = { + export type Quad = number[]; + /** + * Box model. + */ + export interface BoxModel { /** - * Id of the node to retrieve attributes for. + * Content box */ - nodeId: NodeId; - } - export type getAttributesReturnValue = { + content: Quad; /** - * An interleaved array of node attribute names and values. + * Padding box */ - attributes: string[]; - } - /** - * Returns boxes for the given node. - */ - export type getBoxModelParameters = { + padding: Quad; /** - * Identifier of the node. + * Border box */ - nodeId?: NodeId; + border: Quad; /** - * Identifier of the backend node. + * Margin box */ - backendNodeId?: BackendNodeId; + margin: Quad; /** - * JavaScript object id of the node wrapper. + * Node width */ - objectId?: Runtime.RemoteObjectId; - } - export type getBoxModelReturnValue = { + width: number; /** - * Box model for the node. + * Node height */ - model: BoxModel; + height: number; + /** + * Shape outside coordinates + */ + shapeOutside?: ShapeOutsideInfo; } /** - * Returns quads that describe node position on the page. This method -might return multiple quads for inline nodes. + * CSS Shape Outside details. */ - export type getContentQuadsParameters = { - /** - * Identifier of the node. - */ - nodeId?: NodeId; + export interface ShapeOutsideInfo { /** - * Identifier of the backend node. + * Shape bounds */ - backendNodeId?: BackendNodeId; + bounds: Quad; /** - * JavaScript object id of the node wrapper. + * Shape coordinate details */ - objectId?: Runtime.RemoteObjectId; - } - export type getContentQuadsReturnValue = { + shape: any[]; /** - * Quads that describe node layout relative to viewport. + * Margin shape bounds */ - quads: Quad[]; + marginShape: any[]; } /** - * Returns the root DOM node (and optionally the subtree) to the caller. -Implicitly enables the DOM domain events for the current target. + * Rectangle. */ - export type getDocumentParameters = { + export interface Rect { /** - * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the -entire subtree or provide an integer larger than 0. + * X coordinate */ - depth?: number; + x: number; /** - * Whether or not iframes and shadow roots should be traversed when returning the subtree -(default is false). + * Y coordinate */ - pierce?: boolean; - } - export type getDocumentReturnValue = { + y: number; /** - * Resulting node. + * Rectangle width */ - root: Node; - } - /** - * Returns the root DOM node (and optionally the subtree) to the caller. -Deprecated, as it is not designed to work well with the rest of the DOM agent. -Use DOMSnapshot.captureSnapshot instead. - */ - export type getFlattenedDocumentParameters = { + width: number; /** - * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the -entire subtree or provide an integer larger than 0. + * Rectangle height */ - depth?: number; + height: number; + } + export interface CSSComputedStyleProperty { /** - * Whether or not iframes and shadow roots should be traversed when returning the subtree -(default is false). + * Computed style property name. */ - pierce?: boolean; - } - export type getFlattenedDocumentReturnValue = { + name: string; /** - * Resulting node. + * Computed style property value. */ - nodes: Node[]; + value: string; } + /** - * Finds nodes with a given computed style in a subtree. + * Fired when `Element`'s attribute is modified. */ - export type getNodesForSubtreeByStyleParameters = { + export type attributeModifiedPayload = { /** - * Node ID pointing to the root of a subtree. + * Id of the node that has changed. */ nodeId: NodeId; /** - * The style to filter nodes by (includes nodes if any of properties matches). - */ - computedStyles: CSSComputedStyleProperty[]; - /** - * Whether or not iframes and shadow roots in the same target should be traversed when returning the -results (default is false). + * Attribute name. */ - pierce?: boolean; - } - export type getNodesForSubtreeByStyleReturnValue = { + name: string; /** - * Resulting nodes. + * Attribute value. */ - nodeIds: NodeId[]; + value: string; } /** - * Returns node id at given location. Depending on whether DOM domain is enabled, nodeId is -either returned or not. + * Fired when `Element`'s attribute is removed. */ - export type getNodeForLocationParameters = { - /** - * X coordinate. - */ - x: number; - /** - * Y coordinate. - */ - y: number; + export type attributeRemovedPayload = { /** - * False to skip to the nearest non-UA shadow root ancestor (default: false). + * Id of the node that has changed. */ - includeUserAgentShadowDOM?: boolean; + nodeId: NodeId; /** - * Whether to ignore pointer-events: none on elements and hit test them. + * A ttribute name. */ - ignorePointerEventsNone?: boolean; + name: string; } - export type getNodeForLocationReturnValue = { - /** - * Resulting node. - */ - backendNodeId: BackendNodeId; + /** + * Mirrors `DOMCharacterDataModified` event. + */ + export type characterDataModifiedPayload = { /** - * Frame this node belongs to. + * Id of the node that has changed. */ - frameId: Page.FrameId; + nodeId: NodeId; /** - * Id of the node at given coordinates, only when enabled and requested document. + * New text value. */ - nodeId?: NodeId; + characterData: string; } /** - * Returns node's HTML markup. + * Fired when `Container`'s child node count has changed. */ - export type getOuterHTMLParameters = { + export type childNodeCountUpdatedPayload = { /** - * Identifier of the node. + * Id of the node that has changed. */ - nodeId?: NodeId; + nodeId: NodeId; /** - * Identifier of the backend node. + * New node count. */ - backendNodeId?: BackendNodeId; + childNodeCount: number; + } + /** + * Mirrors `DOMNodeInserted` event. + */ + export type childNodeInsertedPayload = { /** - * JavaScript object id of the node wrapper. + * Id of the node that has changed. */ - objectId?: Runtime.RemoteObjectId; + parentNodeId: NodeId; /** - * Include all shadow roots. Equals to false if not specified. + * Id of the previous sibling. */ - includeShadowDOM?: boolean; - } - export type getOuterHTMLReturnValue = { + previousNodeId: NodeId; /** - * Outer HTML markup. + * Inserted node data. */ - outerHTML: string; + node: Node; } /** - * Returns the id of the nearest ancestor that is a relayout boundary. + * Mirrors `DOMNodeRemoved` event. */ - export type getRelayoutBoundaryParameters = { + export type childNodeRemovedPayload = { /** - * Id of the node. + * Parent id. */ - nodeId: NodeId; - } - export type getRelayoutBoundaryReturnValue = { + parentNodeId: NodeId; /** - * Relayout boundary node id for the given node. + * Id of the node that has been removed. */ nodeId: NodeId; } /** - * Returns search results from given `fromIndex` to given `toIndex` from the search with the given -identifier. + * Called when distribution is changed. */ - export type getSearchResultsParameters = { - /** - * Unique search session identifier. - */ - searchId: string; + export type distributedNodesUpdatedPayload = { /** - * Start index of the search result to be returned. + * Insertion point where distributed nodes were updated. */ - fromIndex: number; + insertionPointId: NodeId; /** - * End index of the search result to be returned. + * Distributed nodes for given insertion point. */ - toIndex: number; + distributedNodes: BackendNode[]; } - export type getSearchResultsReturnValue = { + /** + * Fired when `Document` has been totally updated. Node ids are no longer valid. + */ + export type documentUpdatedPayload = void; + /** + * Fired when `Element`'s inline style is modified via a CSS property modification. + */ + export type inlineStyleInvalidatedPayload = { /** - * Ids of the search result nodes. + * Ids of the nodes for which the inline styles have been invalidated. */ nodeIds: NodeId[]; } /** - * Hides any highlight. + * Called when a pseudo element is added to an element. */ - export type hideHighlightParameters = { - } - export type hideHighlightReturnValue = { - } - /** - * Highlights DOM node. - */ - export type highlightNodeParameters = { - } - export type highlightNodeReturnValue = { - } - /** - * Highlights given rectangle. - */ - export type highlightRectParameters = { - } - export type highlightRectReturnValue = { + export type pseudoElementAddedPayload = { + /** + * Pseudo element's parent element id. + */ + parentId: NodeId; + /** + * The added pseudo element. + */ + pseudoElement: Node; } /** - * Marks last undoable state. + * Called when top layer elements are changed. */ - export type markUndoableStateParameters = { - } - export type markUndoableStateReturnValue = { - } + export type topLayerElementsUpdatedPayload = void; /** - * Moves node into the new container, places it before the given anchor. + * Fired when a node's scrollability state changes. */ - export type moveToParameters = { + export type scrollableFlagUpdatedPayload = { /** - * Id of the node to move. + * The id of the node. */ - nodeId: NodeId; + nodeId: DOM.NodeId; /** - * Id of the element to drop the moved node into. + * If the node is scrollable. */ - targetNodeId: NodeId; + isScrollable: boolean; + } + /** + * Called when a pseudo element is removed from an element. + */ + export type pseudoElementRemovedPayload = { /** - * Drop node before this one (if absent, the moved node becomes the last child of -`targetNodeId`). + * Pseudo element's parent element id. */ - insertBeforeNodeId?: NodeId; - } - export type moveToReturnValue = { + parentId: NodeId; /** - * New id of the moved node. + * The removed pseudo element id. */ - nodeId: NodeId; + pseudoElementId: NodeId; } /** - * Searches for a given string in the DOM tree. Use `getSearchResults` to access search results or -`cancelSearch` to end this search session. + * Fired when backend wants to provide client with the missing DOM structure. This happens upon +most of the calls requesting node ids. */ - export type performSearchParameters = { + export type setChildNodesPayload = { /** - * Plain text or query selector or XPath search query. + * Parent node id to populate with children. */ - query: string; + parentId: NodeId; /** - * True to search in user agent shadow DOM. + * Child nodes array. */ - includeUserAgentShadowDOM?: boolean; + nodes: Node[]; } - export type performSearchReturnValue = { + /** + * Called when shadow root is popped from the element. + */ + export type shadowRootPoppedPayload = { /** - * Unique search session identifier. + * Host element id. */ - searchId: string; + hostId: NodeId; /** - * Number of search results. + * Shadow root id. */ - resultCount: number; + rootId: NodeId; } /** - * Requests that the node is sent to the caller given its path. // FIXME, use XPath + * Called when shadow root is pushed into the element. */ - export type pushNodeByPathToFrontendParameters = { + export type shadowRootPushedPayload = { /** - * Path to node in the proprietary format. + * Host element id. */ - path: string; - } - export type pushNodeByPathToFrontendReturnValue = { + hostId: NodeId; /** - * Id of the node for given path. + * Shadow root. */ - nodeId: NodeId; + root: Node; } + /** - * Requests that a batch of nodes is sent to the caller given their backend node ids. + * Collects class names for the node with given id and all of it's child nodes. */ - export type pushNodesByBackendIdsToFrontendParameters = { + export type collectClassNamesFromSubtreeParameters = { /** - * The array of backend node ids. + * Id of the node to collect class names. */ - backendNodeIds: BackendNodeId[]; + nodeId: NodeId; } - export type pushNodesByBackendIdsToFrontendReturnValue = { + export type collectClassNamesFromSubtreeReturnValue = { /** - * The array of ids of pushed nodes that correspond to the backend ids specified in -backendNodeIds. + * Class name list. */ - nodeIds: NodeId[]; + classNames: string[]; } /** - * Executes `querySelector` on a given node. + * Creates a deep copy of the specified node and places it into the target container before the +given anchor. */ - export type querySelectorParameters = { + export type copyToParameters = { /** - * Id of the node to query upon. + * Id of the node to copy. */ nodeId: NodeId; /** - * Selector string. + * Id of the element to drop the copy into. */ - selector: string; + targetNodeId: NodeId; + /** + * Drop the copy before this node (if absent, the copy becomes the last child of +`targetNodeId`). + */ + insertBeforeNodeId?: NodeId; } - export type querySelectorReturnValue = { + export type copyToReturnValue = { /** - * Query selector result. + * Id of the node clone. */ nodeId: NodeId; } /** - * Executes `querySelectorAll` on a given node. + * Describes node given its id, does not require domain to be enabled. Does not start tracking any +objects, can be used for automation. */ - export type querySelectorAllParameters = { + export type describeNodeParameters = { /** - * Id of the node to query upon. + * Identifier of the node. */ - nodeId: NodeId; + nodeId?: NodeId; /** - * Selector string. + * Identifier of the backend node. */ - selector: string; - } - export type querySelectorAllReturnValue = { + backendNodeId?: BackendNodeId; /** - * Query selector result. + * JavaScript object id of the node wrapper. */ - nodeIds: NodeId[]; - } - /** - * Returns NodeIds of current top layer elements. -Top layer is rendered closest to the user within a viewport, therefore its elements always -appear on top of all other content. - */ - export type getTopLayerElementsParameters = { + objectId?: Runtime.RemoteObjectId; + /** + * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the +entire subtree or provide an integer larger than 0. + */ + depth?: number; + /** + * Whether or not iframes and shadow roots should be traversed when returning the subtree +(default is false). + */ + pierce?: boolean; } - export type getTopLayerElementsReturnValue = { + export type describeNodeReturnValue = { /** - * NodeIds of top layer elements + * Node description. */ - nodeIds: NodeId[]; + node: Node; } /** - * Returns the NodeId of the matched element according to certain relations. + * Scrolls the specified rect of the given node into view if not already visible. +Note: exactly one between nodeId, backendNodeId and objectId should be passed +to identify the node. */ - export type getElementByRelationParameters = { + export type scrollIntoViewIfNeededParameters = { /** - * Id of the node from which to query the relation. + * Identifier of the node. */ - nodeId: NodeId; + nodeId?: NodeId; /** - * Type of relation to get. + * Identifier of the backend node. */ - relation: "PopoverTarget"|"InterestTarget"|"CommandFor"; - } - export type getElementByRelationReturnValue = { + backendNodeId?: BackendNodeId; /** - * NodeId of the element matching the queried relation. + * JavaScript object id of the node wrapper. */ - nodeId: NodeId; + objectId?: Runtime.RemoteObjectId; + /** + * The rect to be scrolled into view, relative to the node's border box, in CSS pixels. +When omitted, center of the node will be used, similar to Element.scrollIntoView. + */ + rect?: Rect; + } + export type scrollIntoViewIfNeededReturnValue = { } /** - * Re-does the last undone action. + * Disables DOM agent for the given page. */ - export type redoParameters = { + export type disableParameters = { } - export type redoReturnValue = { + export type disableReturnValue = { } /** - * Removes attribute with given name from an element with given id. + * Discards search results from the session with the given id. `getSearchResults` should no longer +be called for that search. */ - export type removeAttributeParameters = { - /** - * Id of the element to remove attribute from. - */ - nodeId: NodeId; + export type discardSearchResultsParameters = { /** - * Name of the attribute to remove. + * Unique search session identifier. */ - name: string; + searchId: string; } - export type removeAttributeReturnValue = { + export type discardSearchResultsReturnValue = { } /** - * Removes node with given id. + * Enables DOM agent for the given page. */ - export type removeNodeParameters = { + export type enableParameters = { /** - * Id of the node to remove. + * Whether to include whitespaces in the children array of returned Nodes. */ - nodeId: NodeId; + includeWhitespace?: "none"|"all"; } - export type removeNodeReturnValue = { + export type enableReturnValue = { } /** - * Requests that children of the node with given id are returned to the caller in form of -`setChildNodes` events where not only immediate children are retrieved, but all children down to -the specified depth. + * Focuses the given element. */ - export type requestChildNodesParameters = { + export type focusParameters = { /** - * Id of the node to get children for. + * Identifier of the node. */ - nodeId: NodeId; + nodeId?: NodeId; /** - * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the -entire subtree or provide an integer larger than 0. + * Identifier of the backend node. */ - depth?: number; + backendNodeId?: BackendNodeId; /** - * Whether or not iframes and shadow roots should be traversed when returning the sub-tree -(default is false). + * JavaScript object id of the node wrapper. */ - pierce?: boolean; + objectId?: Runtime.RemoteObjectId; } - export type requestChildNodesReturnValue = { + export type focusReturnValue = { } /** - * Requests that the node is sent to the caller given the JavaScript node object reference. All -nodes that form the path from the node to the root are also sent to the client as a series of -`setChildNodes` notifications. + * Returns attributes for the specified node. */ - export type requestNodeParameters = { + export type getAttributesParameters = { /** - * JavaScript object id to convert into node. + * Id of the node to retrieve attributes for. */ - objectId: Runtime.RemoteObjectId; + nodeId: NodeId; } - export type requestNodeReturnValue = { + export type getAttributesReturnValue = { /** - * Node id for given object. + * An interleaved array of node attribute names and values. */ - nodeId: NodeId; + attributes: string[]; } /** - * Resolves the JavaScript node object for a given NodeId or BackendNodeId. + * Returns boxes for the given node. */ - export type resolveNodeParameters = { + export type getBoxModelParameters = { /** - * Id of the node to resolve. + * Identifier of the node. */ nodeId?: NodeId; /** - * Backend identifier of the node to resolve. - */ - backendNodeId?: DOM.BackendNodeId; - /** - * Symbolic group name that can be used to release multiple objects. + * Identifier of the backend node. */ - objectGroup?: string; + backendNodeId?: BackendNodeId; /** - * Execution context in which to resolve the node. + * JavaScript object id of the node wrapper. */ - executionContextId?: Runtime.ExecutionContextId; + objectId?: Runtime.RemoteObjectId; } - export type resolveNodeReturnValue = { + export type getBoxModelReturnValue = { /** - * JavaScript object wrapper for given node. + * Box model for the node. */ - object: Runtime.RemoteObject; + model: BoxModel; } /** - * Sets attribute for an element with given id. + * Returns quads that describe node position on the page. This method +might return multiple quads for inline nodes. */ - export type setAttributeValueParameters = { + export type getContentQuadsParameters = { /** - * Id of the element to set attribute for. + * Identifier of the node. */ - nodeId: NodeId; + nodeId?: NodeId; /** - * Attribute name. + * Identifier of the backend node. */ - name: string; + backendNodeId?: BackendNodeId; /** - * Attribute value. + * JavaScript object id of the node wrapper. */ - value: string; + objectId?: Runtime.RemoteObjectId; } - export type setAttributeValueReturnValue = { + export type getContentQuadsReturnValue = { + /** + * Quads that describe node layout relative to viewport. + */ + quads: Quad[]; } /** - * Sets attributes on element with given id. This method is useful when user edits some existing -attribute value and types in several attribute name/value pairs. + * Returns the root DOM node (and optionally the subtree) to the caller. +Implicitly enables the DOM domain events for the current target. */ - export type setAttributesAsTextParameters = { + export type getDocumentParameters = { /** - * Id of the element to set attributes for. + * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the +entire subtree or provide an integer larger than 0. */ - nodeId: NodeId; + depth?: number; /** - * Text with a number of attributes. Will parse this text using HTML parser. + * Whether or not iframes and shadow roots should be traversed when returning the subtree +(default is false). */ - text: string; + pierce?: boolean; + } + export type getDocumentReturnValue = { /** - * Attribute name to replace with new attributes derived from text in case text parsed -successfully. + * Resulting node. */ - name?: string; - } - export type setAttributesAsTextReturnValue = { + root: Node; } /** - * Sets files for the given file input element. + * Returns the root DOM node (and optionally the subtree) to the caller. +Deprecated, as it is not designed to work well with the rest of the DOM agent. +Use DOMSnapshot.captureSnapshot instead. */ - export type setFileInputFilesParameters = { - /** - * Array of file paths to set. - */ - files: string[]; + export type getFlattenedDocumentParameters = { /** - * Identifier of the node. + * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the +entire subtree or provide an integer larger than 0. */ - nodeId?: NodeId; + depth?: number; /** - * Identifier of the backend node. + * Whether or not iframes and shadow roots should be traversed when returning the subtree +(default is false). */ - backendNodeId?: BackendNodeId; + pierce?: boolean; + } + export type getFlattenedDocumentReturnValue = { /** - * JavaScript object id of the node wrapper. + * Resulting node. */ - objectId?: Runtime.RemoteObjectId; - } - export type setFileInputFilesReturnValue = { + nodes: Node[]; } /** - * Sets if stack traces should be captured for Nodes. See `Node.getNodeStackTraces`. Default is disabled. + * Finds nodes with a given computed style in a subtree. */ - export type setNodeStackTracesEnabledParameters = { + export type getNodesForSubtreeByStyleParameters = { /** - * Enable or disable. + * Node ID pointing to the root of a subtree. */ - enable: boolean; + nodeId: NodeId; + /** + * The style to filter nodes by (includes nodes if any of properties matches). + */ + computedStyles: CSSComputedStyleProperty[]; + /** + * Whether or not iframes and shadow roots in the same target should be traversed when returning the +results (default is false). + */ + pierce?: boolean; } - export type setNodeStackTracesEnabledReturnValue = { + export type getNodesForSubtreeByStyleReturnValue = { + /** + * Resulting nodes. + */ + nodeIds: NodeId[]; } /** - * Gets stack traces associated with a Node. As of now, only provides stack trace for Node creation. + * Returns node id at given location. Depending on whether DOM domain is enabled, nodeId is +either returned or not. */ - export type getNodeStackTracesParameters = { + export type getNodeForLocationParameters = { /** - * Id of the node to get stack traces for. + * X coordinate. */ - nodeId: NodeId; + x: number; + /** + * Y coordinate. + */ + y: number; + /** + * False to skip to the nearest non-UA shadow root ancestor (default: false). + */ + includeUserAgentShadowDOM?: boolean; + /** + * Whether to ignore pointer-events: none on elements and hit test them. + */ + ignorePointerEventsNone?: boolean; } - export type getNodeStackTracesReturnValue = { + export type getNodeForLocationReturnValue = { /** - * Creation stack trace, if available. + * Resulting node. */ - creation?: Runtime.StackTrace; + backendNodeId: BackendNodeId; + /** + * Frame this node belongs to. + */ + frameId: Page.FrameId; + /** + * Id of the node at given coordinates, only when enabled and requested document. + */ + nodeId?: NodeId; } /** - * Returns file information for the given -File wrapper. + * Returns node's HTML markup. */ - export type getFileInfoParameters = { + export type getOuterHTMLParameters = { + /** + * Identifier of the node. + */ + nodeId?: NodeId; + /** + * Identifier of the backend node. + */ + backendNodeId?: BackendNodeId; /** * JavaScript object id of the node wrapper. */ - objectId: Runtime.RemoteObjectId; - } - export type getFileInfoReturnValue = { - path: string; - } - /** - * Returns list of detached nodes - */ - export type getDetachedDomNodesParameters = { - } - export type getDetachedDomNodesReturnValue = { + objectId?: Runtime.RemoteObjectId; /** - * The list of detached nodes + * Include all shadow roots. Equals to false if not specified. */ - detachedNodes: DetachedElementInfo[]; + includeShadowDOM?: boolean; } - /** - * Enables console to refer to the node with given id via $x (see Command Line API for more details -$x functions). - */ - export type setInspectedNodeParameters = { + export type getOuterHTMLReturnValue = { /** - * DOM node id to be accessible by means of $x command line API. + * Outer HTML markup. */ - nodeId: NodeId; - } - export type setInspectedNodeReturnValue = { + outerHTML: string; } /** - * Sets node name for a node with given id. + * Returns the id of the nearest ancestor that is a relayout boundary. */ - export type setNodeNameParameters = { + export type getRelayoutBoundaryParameters = { /** - * Id of the node to set name for. + * Id of the node. */ nodeId: NodeId; - /** - * New node's name. - */ - name: string; } - export type setNodeNameReturnValue = { + export type getRelayoutBoundaryReturnValue = { /** - * New node's id. + * Relayout boundary node id for the given node. */ nodeId: NodeId; } /** - * Sets node value for a node with given id. + * Returns search results from given `fromIndex` to given `toIndex` from the search with the given +identifier. */ - export type setNodeValueParameters = { + export type getSearchResultsParameters = { /** - * Id of the node to set value for. + * Unique search session identifier. */ - nodeId: NodeId; + searchId: string; /** - * New node's value. + * Start index of the search result to be returned. */ - value: string; + fromIndex: number; + /** + * End index of the search result to be returned. + */ + toIndex: number; } - export type setNodeValueReturnValue = { + export type getSearchResultsReturnValue = { + /** + * Ids of the search result nodes. + */ + nodeIds: NodeId[]; } /** - * Sets node HTML markup, returns new node id. + * Hides any highlight. */ - export type setOuterHTMLParameters = { - /** - * Id of the node to set markup for. - */ - nodeId: NodeId; - /** - * Outer HTML markup to set. - */ - outerHTML: string; + export type hideHighlightParameters = { } - export type setOuterHTMLReturnValue = { + export type hideHighlightReturnValue = { } /** - * Undoes the last performed action. + * Highlights DOM node. */ - export type undoParameters = { + export type highlightNodeParameters = { } - export type undoReturnValue = { + export type highlightNodeReturnValue = { } /** - * Returns iframe node that owns iframe with the given domain. + * Highlights given rectangle. */ - export type getFrameOwnerParameters = { - frameId: Page.FrameId; + export type highlightRectParameters = { } - export type getFrameOwnerReturnValue = { - /** - * Resulting node. - */ - backendNodeId: BackendNodeId; - /** - * Id of the node at given coordinates, only when enabled and requested document. - */ - nodeId?: NodeId; + export type highlightRectReturnValue = { } /** - * Returns the query container of the given node based on container query -conditions: containerName, physical and logical axes, and whether it queries -scroll-state or anchored elements. If no axes are provided and -queriesScrollState is false, the style container is returned, which is the -direct parent or the closest element with a matching container-name. + * Marks last undoable state. */ - export type getContainerForNodeParameters = { - nodeId: NodeId; - containerName?: string; - physicalAxes?: PhysicalAxes; - logicalAxes?: LogicalAxes; - queriesScrollState?: boolean; - queriesAnchored?: boolean; + export type markUndoableStateParameters = { } - export type getContainerForNodeReturnValue = { - /** - * The container node for the given node, or null if not found. - */ - nodeId?: NodeId; + export type markUndoableStateReturnValue = { } /** - * Returns the descendants of a container query container that have -container queries against this container. + * Moves node into the new container, places it before the given anchor. */ - export type getQueryingDescendantsForContainerParameters = { + export type moveToParameters = { /** - * Id of the container node to find querying descendants from. + * Id of the node to move. */ nodeId: NodeId; - } - export type getQueryingDescendantsForContainerReturnValue = { - /** - * Descendant nodes with container queries against the given container. - */ - nodeIds: NodeId[]; - } - /** - * Returns the target anchor element of the given anchor query according to -https://www.w3.org/TR/css-anchor-position-1/#target. - */ - export type getAnchorElementParameters = { /** - * Id of the positioned element from which to find the anchor. + * Id of the element to drop the moved node into. */ - nodeId: NodeId; + targetNodeId: NodeId; /** - * An optional anchor specifier, as defined in -https://www.w3.org/TR/css-anchor-position-1/#anchor-specifier. -If not provided, it will return the implicit anchor element for -the given positioned element. + * Drop node before this one (if absent, the moved node becomes the last child of +`targetNodeId`). */ - anchorSpecifier?: string; + insertBeforeNodeId?: NodeId; } - export type getAnchorElementReturnValue = { + export type moveToReturnValue = { /** - * The anchor element of the given anchor query. + * New id of the moved node. */ nodeId: NodeId; } /** - * When enabling, this API force-opens the popover identified by nodeId -and keeps it open until disabled. + * Searches for a given string in the DOM tree. Use `getSearchResults` to access search results or +`cancelSearch` to end this search session. */ - export type forceShowPopoverParameters = { + export type performSearchParameters = { /** - * Id of the popover HTMLElement + * Plain text or query selector or XPath search query. */ - nodeId: NodeId; + query: string; /** - * If true, opens the popover and keeps it open. If false, closes the -popover if it was previously force-opened. + * True to search in user agent shadow DOM. */ - enable: boolean; + includeUserAgentShadowDOM?: boolean; } - export type forceShowPopoverReturnValue = { + export type performSearchReturnValue = { /** - * List of popovers that were closed in order to respect popover stacking order. + * Unique search session identifier. */ - nodeIds: NodeId[]; + searchId: string; + /** + * Number of search results. + */ + resultCount: number; } - } - - /** - * DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript -execution will stop on these operations as if there was a regular breakpoint set. - */ - export module DOMDebugger { - /** - * DOM breakpoint type. - */ - export type DOMBreakpointType = "subtree-modified"|"attribute-modified"|"node-removed"; - /** - * CSP Violation type. - */ - export type CSPViolationType = "trustedtype-sink-violation"|"trustedtype-policy-violation"; /** - * Object event listener. + * Requests that the node is sent to the caller given its path. // FIXME, use XPath */ - export interface EventListener { + export type pushNodeByPathToFrontendParameters = { /** - * `EventListener`'s type. + * Path to node in the proprietary format. */ - type: string; + path: string; + } + export type pushNodeByPathToFrontendReturnValue = { /** - * `EventListener`'s useCapture. + * Id of the node for given path. */ - useCapture: boolean; + nodeId: NodeId; + } + /** + * Requests that a batch of nodes is sent to the caller given their backend node ids. + */ + export type pushNodesByBackendIdsToFrontendParameters = { /** - * `EventListener`'s passive flag. + * The array of backend node ids. */ - passive: boolean; + backendNodeIds: BackendNodeId[]; + } + export type pushNodesByBackendIdsToFrontendReturnValue = { /** - * `EventListener`'s once flag. + * The array of ids of pushed nodes that correspond to the backend ids specified in +backendNodeIds. */ - once: boolean; + nodeIds: NodeId[]; + } + /** + * Executes `querySelector` on a given node. + */ + export type querySelectorParameters = { /** - * Script id of the handler code. + * Id of the node to query upon. */ - scriptId: Runtime.ScriptId; + nodeId: NodeId; /** - * Line number in the script (0-based). + * Selector string. */ - lineNumber: number; + selector: string; + } + export type querySelectorReturnValue = { /** - * Column number in the script (0-based). + * Query selector result. */ - columnNumber: number; + nodeId: NodeId; + } + /** + * Executes `querySelectorAll` on a given node. + */ + export type querySelectorAllParameters = { /** - * Event handler function value. + * Id of the node to query upon. */ - handler?: Runtime.RemoteObject; + nodeId: NodeId; /** - * Event original handler function value. + * Selector string. */ - originalHandler?: Runtime.RemoteObject; + selector: string; + } + export type querySelectorAllReturnValue = { /** - * Node the listener is added to (if any). + * Query selector result. */ - backendNodeId?: DOM.BackendNodeId; + nodeIds: NodeId[]; } - - /** - * Returns event listeners of the given object. + * Returns NodeIds of current top layer elements. +Top layer is rendered closest to the user within a viewport, therefore its elements always +appear on top of all other content. */ - export type getEventListenersParameters = { + export type getTopLayerElementsParameters = { + } + export type getTopLayerElementsReturnValue = { /** - * Identifier of the object to return listeners for. + * NodeIds of top layer elements */ - objectId: Runtime.RemoteObjectId; + nodeIds: NodeId[]; + } + /** + * Returns the NodeId of the matched element according to certain relations. + */ + export type getElementByRelationParameters = { /** - * The maximum depth at which Node children should be retrieved, defaults to 1. Use -1 for the -entire subtree or provide an integer larger than 0. + * Id of the node from which to query the relation. */ - depth?: number; + nodeId: NodeId; /** - * Whether or not iframes and shadow roots should be traversed when returning the subtree -(default is false). Reports listeners for all contexts if pierce is enabled. + * Type of relation to get. */ - pierce?: boolean; + relation: "PopoverTarget"|"InterestTarget"|"CommandFor"; } - export type getEventListenersReturnValue = { + export type getElementByRelationReturnValue = { /** - * Array of relevant listeners. + * NodeId of the element matching the queried relation. */ - listeners: EventListener[]; + nodeId: NodeId; } /** - * Removes DOM breakpoint that was set using `setDOMBreakpoint`. + * Re-does the last undone action. */ - export type removeDOMBreakpointParameters = { + export type redoParameters = { + } + export type redoReturnValue = { + } + /** + * Removes attribute with given name from an element with given id. + */ + export type removeAttributeParameters = { /** - * Identifier of the node to remove breakpoint from. + * Id of the element to remove attribute from. */ - nodeId: DOM.NodeId; + nodeId: NodeId; /** - * Type of the breakpoint to remove. + * Name of the attribute to remove. */ - type: DOMBreakpointType; + name: string; } - export type removeDOMBreakpointReturnValue = { + export type removeAttributeReturnValue = { } /** - * Removes breakpoint on particular DOM event. + * Removes node with given id. */ - export type removeEventListenerBreakpointParameters = { + export type removeNodeParameters = { /** - * Event name. - */ - eventName: string; - /** - * EventTarget interface name. + * Id of the node to remove. */ - targetName?: string; + nodeId: NodeId; } - export type removeEventListenerBreakpointReturnValue = { + export type removeNodeReturnValue = { } /** - * Removes breakpoint on particular native event. + * Requests that children of the node with given id are returned to the caller in form of +`setChildNodes` events where not only immediate children are retrieved, but all children down to +the specified depth. */ - export type removeInstrumentationBreakpointParameters = { + export type requestChildNodesParameters = { /** - * Instrumentation name to stop on. + * Id of the node to get children for. */ - eventName: string; + nodeId: NodeId; + /** + * The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the +entire subtree or provide an integer larger than 0. + */ + depth?: number; + /** + * Whether or not iframes and shadow roots should be traversed when returning the sub-tree +(default is false). + */ + pierce?: boolean; } - export type removeInstrumentationBreakpointReturnValue = { + export type requestChildNodesReturnValue = { } /** - * Removes breakpoint from XMLHttpRequest. + * Requests that the node is sent to the caller given the JavaScript node object reference. All +nodes that form the path from the node to the root are also sent to the client as a series of +`setChildNodes` notifications. */ - export type removeXHRBreakpointParameters = { + export type requestNodeParameters = { /** - * Resource URL substring. + * JavaScript object id to convert into node. */ - url: string; + objectId: Runtime.RemoteObjectId; } - export type removeXHRBreakpointReturnValue = { + export type requestNodeReturnValue = { + /** + * Node id for given object. + */ + nodeId: NodeId; } /** - * Sets breakpoint on particular CSP violations. + * Resolves the JavaScript node object for a given NodeId or BackendNodeId. */ - export type setBreakOnCSPViolationParameters = { + export type resolveNodeParameters = { /** - * CSP Violations to stop upon. + * Id of the node to resolve. */ - violationTypes: CSPViolationType[]; + nodeId?: NodeId; + /** + * Backend identifier of the node to resolve. + */ + backendNodeId?: DOM.BackendNodeId; + /** + * Symbolic group name that can be used to release multiple objects. + */ + objectGroup?: string; + /** + * Execution context in which to resolve the node. + */ + executionContextId?: Runtime.ExecutionContextId; } - export type setBreakOnCSPViolationReturnValue = { + export type resolveNodeReturnValue = { + /** + * JavaScript object wrapper for given node. + */ + object: Runtime.RemoteObject; } /** - * Sets breakpoint on particular operation with DOM. + * Sets attribute for an element with given id. */ - export type setDOMBreakpointParameters = { + export type setAttributeValueParameters = { /** - * Identifier of the node to set breakpoint on. + * Id of the element to set attribute for. */ - nodeId: DOM.NodeId; + nodeId: NodeId; /** - * Type of the operation to stop upon. + * Attribute name. */ - type: DOMBreakpointType; + name: string; + /** + * Attribute value. + */ + value: string; } - export type setDOMBreakpointReturnValue = { + export type setAttributeValueReturnValue = { } /** - * Sets breakpoint on particular DOM event. + * Sets attributes on element with given id. This method is useful when user edits some existing +attribute value and types in several attribute name/value pairs. */ - export type setEventListenerBreakpointParameters = { + export type setAttributesAsTextParameters = { /** - * DOM Event name to stop on (any DOM event will do). + * Id of the element to set attributes for. */ - eventName: string; + nodeId: NodeId; /** - * EventTarget interface name to stop on. If equal to `"*"` or not provided, will stop on any -EventTarget. + * Text with a number of attributes. Will parse this text using HTML parser. */ - targetName?: string; + text: string; + /** + * Attribute name to replace with new attributes derived from text in case text parsed +successfully. + */ + name?: string; } - export type setEventListenerBreakpointReturnValue = { + export type setAttributesAsTextReturnValue = { } /** - * Sets breakpoint on particular native event. + * Sets files for the given file input element. */ - export type setInstrumentationBreakpointParameters = { + export type setFileInputFilesParameters = { /** - * Instrumentation name to stop on. + * Array of file paths to set. */ - eventName: string; + files: string[]; + /** + * Identifier of the node. + */ + nodeId?: NodeId; + /** + * Identifier of the backend node. + */ + backendNodeId?: BackendNodeId; + /** + * JavaScript object id of the node wrapper. + */ + objectId?: Runtime.RemoteObjectId; } - export type setInstrumentationBreakpointReturnValue = { + export type setFileInputFilesReturnValue = { } /** - * Sets breakpoint on XMLHttpRequest. + * Sets if stack traces should be captured for Nodes. See `Node.getNodeStackTraces`. Default is disabled. */ - export type setXHRBreakpointParameters = { + export type setNodeStackTracesEnabledParameters = { /** - * Resource URL substring. All XHRs having this substring in the URL will get stopped upon. + * Enable or disable. */ - url: string; + enable: boolean; } - export type setXHRBreakpointReturnValue = { + export type setNodeStackTracesEnabledReturnValue = { } - } - - /** - * EventBreakpoints permits setting JavaScript breakpoints on operations and events -occurring in native code invoked from JavaScript. Once breakpoint is hit, it is -reported through Debugger domain, similarly to regular breakpoints being hit. - */ - export module EventBreakpoints { - - /** - * Sets breakpoint on particular native event. + * Gets stack traces associated with a Node. As of now, only provides stack trace for Node creation. */ - export type setInstrumentationBreakpointParameters = { + export type getNodeStackTracesParameters = { /** - * Instrumentation name to stop on. + * Id of the node to get stack traces for. */ - eventName: string; + nodeId: NodeId; } - export type setInstrumentationBreakpointReturnValue = { + export type getNodeStackTracesReturnValue = { + /** + * Creation stack trace, if available. + */ + creation?: Runtime.StackTrace; } /** - * Removes breakpoint on particular native event. + * Returns file information for the given +File wrapper. */ - export type removeInstrumentationBreakpointParameters = { + export type getFileInfoParameters = { /** - * Instrumentation name to stop on. + * JavaScript object id of the node wrapper. */ - eventName: string; + objectId: Runtime.RemoteObjectId; } - export type removeInstrumentationBreakpointReturnValue = { + export type getFileInfoReturnValue = { + path: string; } /** - * Removes all breakpoints + * Returns list of detached nodes */ - export type disableParameters = { + export type getDetachedDomNodesParameters = { } - export type disableReturnValue = { + export type getDetachedDomNodesReturnValue = { + /** + * The list of detached nodes + */ + detachedNodes: DetachedElementInfo[]; } - } - - /** - * This domain facilitates obtaining document snapshots with DOM, layout, and style information. - */ - export module DOMSnapshot { /** - * A Node in the DOM tree. + * Enables console to refer to the node with given id via $x (see Command Line API for more details +$x functions). */ - export interface DOMNode { + export type setInspectedNodeParameters = { /** - * `Node`'s nodeType. + * DOM node id to be accessible by means of $x command line API. */ - nodeType: number; + nodeId: NodeId; + } + export type setInspectedNodeReturnValue = { + } + /** + * Sets node name for a node with given id. + */ + export type setNodeNameParameters = { /** - * `Node`'s nodeName. + * Id of the node to set name for. */ - nodeName: string; + nodeId: NodeId; /** - * `Node`'s nodeValue. + * New node's name. */ - nodeValue: string; + name: string; + } + export type setNodeNameReturnValue = { /** - * Only set for textarea elements, contains the text value. + * New node's id. */ - textValue?: string; + nodeId: NodeId; + } + /** + * Sets node value for a node with given id. + */ + export type setNodeValueParameters = { /** - * Only set for input elements, contains the input's associated text value. + * Id of the node to set value for. */ - inputValue?: string; + nodeId: NodeId; /** - * Only set for radio and checkbox input elements, indicates if the element has been checked + * New node's value. */ - inputChecked?: boolean; + value: string; + } + export type setNodeValueReturnValue = { + } + /** + * Sets node HTML markup, returns new node id. + */ + export type setOuterHTMLParameters = { /** - * Only set for option elements, indicates if the element has been selected + * Id of the node to set markup for. */ - optionSelected?: boolean; + nodeId: NodeId; /** - * `Node`'s id, corresponds to DOM.Node.backendNodeId. + * Outer HTML markup to set. */ - backendNodeId: DOM.BackendNodeId; - /** - * The indexes of the node's child nodes in the `domNodes` array returned by `getSnapshot`, if -any. - */ - childNodeIndexes?: number[]; + outerHTML: string; + } + export type setOuterHTMLReturnValue = { + } + /** + * Undoes the last performed action. + */ + export type undoParameters = { + } + export type undoReturnValue = { + } + /** + * Returns iframe node that owns iframe with the given domain. + */ + export type getFrameOwnerParameters = { + frameId: Page.FrameId; + } + export type getFrameOwnerReturnValue = { /** - * Attributes of an `Element` node. + * Resulting node. */ - attributes?: NameValue[]; + backendNodeId: BackendNodeId; /** - * Indexes of pseudo elements associated with this node in the `domNodes` array returned by -`getSnapshot`, if any. + * Id of the node at given coordinates, only when enabled and requested document. */ - pseudoElementIndexes?: number[]; + nodeId?: NodeId; + } + /** + * Returns the query container of the given node based on container query +conditions: containerName, physical and logical axes, and whether it queries +scroll-state or anchored elements. If no axes are provided and +queriesScrollState is false, the style container is returned, which is the +direct parent or the closest element with a matching container-name. + */ + export type getContainerForNodeParameters = { + nodeId: NodeId; + containerName?: string; + physicalAxes?: PhysicalAxes; + logicalAxes?: LogicalAxes; + queriesScrollState?: boolean; + queriesAnchored?: boolean; + } + export type getContainerForNodeReturnValue = { /** - * The index of the node's related layout tree node in the `layoutTreeNodes` array returned by -`getSnapshot`, if any. + * The container node for the given node, or null if not found. */ - layoutNodeIndex?: number; + nodeId?: NodeId; + } + /** + * Returns the descendants of a container query container that have +container queries against this container. + */ + export type getQueryingDescendantsForContainerParameters = { /** - * Document URL that `Document` or `FrameOwner` node points to. + * Id of the container node to find querying descendants from. */ - documentURL?: string; + nodeId: NodeId; + } + export type getQueryingDescendantsForContainerReturnValue = { /** - * Base URL that `Document` or `FrameOwner` node uses for URL completion. + * Descendant nodes with container queries against the given container. */ - baseURL?: string; + nodeIds: NodeId[]; + } + /** + * Returns the target anchor element of the given anchor query according to +https://www.w3.org/TR/css-anchor-position-1/#target. + */ + export type getAnchorElementParameters = { /** - * Only set for documents, contains the document's content language. + * Id of the positioned element from which to find the anchor. */ - contentLanguage?: string; + nodeId: NodeId; /** - * Only set for documents, contains the document's character set encoding. + * An optional anchor specifier, as defined in +https://www.w3.org/TR/css-anchor-position-1/#anchor-specifier. +If not provided, it will return the implicit anchor element for +the given positioned element. */ - documentEncoding?: string; + anchorSpecifier?: string; + } + export type getAnchorElementReturnValue = { /** - * `DocumentType` node's publicId. + * The anchor element of the given anchor query. */ - publicId?: string; + nodeId: NodeId; + } + /** + * When enabling, this API force-opens the popover identified by nodeId +and keeps it open until disabled. + */ + export type forceShowPopoverParameters = { /** - * `DocumentType` node's systemId. + * Id of the popover HTMLElement */ - systemId?: string; + nodeId: NodeId; /** - * Frame ID for frame owner elements and also for the document node. + * If true, opens the popover and keeps it open. If false, closes the +popover if it was previously force-opened. */ - frameId?: Page.FrameId; + enable: boolean; + } + export type forceShowPopoverReturnValue = { /** - * The index of a frame owner element's content document in the `domNodes` array returned by -`getSnapshot`, if any. + * List of popovers that were closed in order to respect popover stacking order. */ - contentDocumentIndex?: number; + nodeIds: NodeId[]; + } + } + + /** + * DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript +execution will stop on these operations as if there was a regular breakpoint set. + */ + export module DOMDebugger { + /** + * DOM breakpoint type. + */ + export type DOMBreakpointType = "subtree-modified"|"attribute-modified"|"node-removed"; + /** + * CSP Violation type. + */ + export type CSPViolationType = "trustedtype-sink-violation"|"trustedtype-policy-violation"; + /** + * Object event listener. + */ + export interface EventListener { /** - * Type of a pseudo element node. + * `EventListener`'s type. */ - pseudoType?: DOM.PseudoType; + type: string; /** - * Shadow root type. + * `EventListener`'s useCapture. */ - shadowRootType?: DOM.ShadowRootType; + useCapture: boolean; /** - * Whether this DOM node responds to mouse clicks. This includes nodes that have had click -event listeners attached via JavaScript as well as anchor tags that naturally navigate when -clicked. + * `EventListener`'s passive flag. */ - isClickable?: boolean; + passive: boolean; /** - * Details of the node's event listeners, if any. + * `EventListener`'s once flag. */ - eventListeners?: DOMDebugger.EventListener[]; + once: boolean; /** - * The selected url for nodes with a srcset attribute. + * Script id of the handler code. */ - currentSourceURL?: string; + scriptId: Runtime.ScriptId; /** - * The url of the script (if any) that generates this node. + * Line number in the script (0-based). */ - originURL?: string; + lineNumber: number; /** - * Scroll offsets, set when this node is a Document. + * Column number in the script (0-based). */ - scrollOffsetX?: number; - scrollOffsetY?: number; - } - /** - * Details of post layout rendered text positions. The exact layout should not be regarded as -stable and may change between versions. - */ - export interface InlineTextBox { + columnNumber: number; /** - * The bounding box in document coordinates. Note that scroll offset of the document is ignored. + * Event handler function value. */ - boundingBox: DOM.Rect; + handler?: Runtime.RemoteObject; /** - * The starting index in characters, for this post layout textbox substring. Characters that -would be represented as a surrogate pair in UTF-16 have length 2. + * Event original handler function value. */ - startCharacterIndex: number; + originalHandler?: Runtime.RemoteObject; /** - * The number of characters in this post layout textbox substring. Characters that would be -represented as a surrogate pair in UTF-16 have length 2. + * Node the listener is added to (if any). */ - numCharacters: number; + backendNodeId?: DOM.BackendNodeId; } + + /** - * Details of an element in the DOM tree with a LayoutObject. + * Returns event listeners of the given object. */ - export interface LayoutTreeNode { - /** - * The index of the related DOM node in the `domNodes` array returned by `getSnapshot`. - */ - domNodeIndex: number; - /** - * The bounding box in document coordinates. Note that scroll offset of the document is ignored. - */ - boundingBox: DOM.Rect; - /** - * Contents of the LayoutText, if any. - */ - layoutText?: string; + export type getEventListenersParameters = { /** - * The post-layout inline text nodes, if any. + * Identifier of the object to return listeners for. */ - inlineTextNodes?: InlineTextBox[]; + objectId: Runtime.RemoteObjectId; /** - * Index into the `computedStyles` array returned by `getSnapshot`. + * The maximum depth at which Node children should be retrieved, defaults to 1. Use -1 for the +entire subtree or provide an integer larger than 0. */ - styleIndex?: number; + depth?: number; /** - * Global paint order index, which is determined by the stacking order of the nodes. Nodes -that are painted together will have the same index. Only provided if includePaintOrder in -getSnapshot was true. + * Whether or not iframes and shadow roots should be traversed when returning the subtree +(default is false). Reports listeners for all contexts if pierce is enabled. */ - paintOrder?: number; + pierce?: boolean; + } + export type getEventListenersReturnValue = { /** - * Set to true to indicate the element begins a new stacking context. + * Array of relevant listeners. */ - isStackingContext?: boolean; + listeners: EventListener[]; } /** - * A subset of the full ComputedStyle as defined by the request whitelist. + * Removes DOM breakpoint that was set using `setDOMBreakpoint`. */ - export interface ComputedStyle { + export type removeDOMBreakpointParameters = { /** - * Name/value pairs of computed style properties. + * Identifier of the node to remove breakpoint from. */ - properties: NameValue[]; + nodeId: DOM.NodeId; + /** + * Type of the breakpoint to remove. + */ + type: DOMBreakpointType; + } + export type removeDOMBreakpointReturnValue = { } /** - * A name/value pair. + * Removes breakpoint on particular DOM event. */ - export interface NameValue { + export type removeEventListenerBreakpointParameters = { /** - * Attribute/property name. + * Event name. */ - name: string; + eventName: string; /** - * Attribute/property value. + * EventTarget interface name. */ - value: string; + targetName?: string; + } + export type removeEventListenerBreakpointReturnValue = { } /** - * Index of the string in the strings table. + * Removes breakpoint on particular native event. */ - export type StringIndex = number; - /** - * Index of the string in the strings table. - */ - export type ArrayOfStrings = StringIndex[]; + export type removeInstrumentationBreakpointParameters = { + /** + * Instrumentation name to stop on. + */ + eventName: string; + } + export type removeInstrumentationBreakpointReturnValue = { + } /** - * Data that is only present on rare nodes. + * Removes breakpoint from XMLHttpRequest. */ - export interface RareStringData { - index: number[]; - value: StringIndex[]; - } - export interface RareBooleanData { - index: number[]; + export type removeXHRBreakpointParameters = { + /** + * Resource URL substring. + */ + url: string; } - export interface RareIntegerData { - index: number[]; - value: number[]; + export type removeXHRBreakpointReturnValue = { } - export type Rectangle = number[]; /** - * Document snapshot. + * Sets breakpoint on particular CSP violations. */ - export interface DocumentSnapshot { + export type setBreakOnCSPViolationParameters = { /** - * Document URL that `Document` or `FrameOwner` node points to. + * CSP Violations to stop upon. */ - documentURL: StringIndex; + violationTypes: CSPViolationType[]; + } + export type setBreakOnCSPViolationReturnValue = { + } + /** + * Sets breakpoint on particular operation with DOM. + */ + export type setDOMBreakpointParameters = { /** - * Document title. + * Identifier of the node to set breakpoint on. */ - title: StringIndex; + nodeId: DOM.NodeId; /** - * Base URL that `Document` or `FrameOwner` node uses for URL completion. + * Type of the operation to stop upon. */ - baseURL: StringIndex; + type: DOMBreakpointType; + } + export type setDOMBreakpointReturnValue = { + } + /** + * Sets breakpoint on particular DOM event. + */ + export type setEventListenerBreakpointParameters = { /** - * Contains the document's content language. + * DOM Event name to stop on (any DOM event will do). */ - contentLanguage: StringIndex; + eventName: string; /** - * Contains the document's character set encoding. + * EventTarget interface name to stop on. If equal to `"*"` or not provided, will stop on any +EventTarget. */ - encodingName: StringIndex; + targetName?: string; + } + export type setEventListenerBreakpointReturnValue = { + } + /** + * Sets breakpoint on particular native event. + */ + export type setInstrumentationBreakpointParameters = { /** - * `DocumentType` node's publicId. + * Instrumentation name to stop on. */ - publicId: StringIndex; + eventName: string; + } + export type setInstrumentationBreakpointReturnValue = { + } + /** + * Sets breakpoint on XMLHttpRequest. + */ + export type setXHRBreakpointParameters = { /** - * `DocumentType` node's systemId. + * Resource URL substring. All XHRs having this substring in the URL will get stopped upon. */ - systemId: StringIndex; + url: string; + } + export type setXHRBreakpointReturnValue = { + } + } + + /** + * This domain facilitates obtaining document snapshots with DOM, layout, and style information. + */ + export module DOMSnapshot { + /** + * A Node in the DOM tree. + */ + export interface DOMNode { /** - * Frame ID for frame owner elements and also for the document node. + * `Node`'s nodeType. */ - frameId: StringIndex; + nodeType: number; /** - * A table with dom nodes. + * `Node`'s nodeName. */ - nodes: NodeTreeSnapshot; + nodeName: string; /** - * The nodes in the layout tree. + * `Node`'s nodeValue. */ - layout: LayoutTreeSnapshot; + nodeValue: string; /** - * The post-layout inline text nodes. + * Only set for textarea elements, contains the text value. */ - textBoxes: TextBoxSnapshot; + textValue?: string; /** - * Horizontal scroll offset. + * Only set for input elements, contains the input's associated text value. */ - scrollOffsetX?: number; + inputValue?: string; /** - * Vertical scroll offset. + * Only set for radio and checkbox input elements, indicates if the element has been checked */ - scrollOffsetY?: number; + inputChecked?: boolean; /** - * Document content width. + * Only set for option elements, indicates if the element has been selected */ - contentWidth?: number; + optionSelected?: boolean; /** - * Document content height. + * `Node`'s id, corresponds to DOM.Node.backendNodeId. */ - contentHeight?: number; - } - /** - * Table containing nodes. - */ - export interface NodeTreeSnapshot { + backendNodeId: DOM.BackendNodeId; /** - * Parent node index. + * The indexes of the node's child nodes in the `domNodes` array returned by `getSnapshot`, if +any. */ - parentIndex?: number[]; + childNodeIndexes?: number[]; /** - * `Node`'s nodeType. + * Attributes of an `Element` node. */ - nodeType?: number[]; + attributes?: NameValue[]; /** - * Type of the shadow root the `Node` is in. String values are equal to the `ShadowRootType` enum. + * Indexes of pseudo elements associated with this node in the `domNodes` array returned by +`getSnapshot`, if any. */ - shadowRootType?: RareStringData; + pseudoElementIndexes?: number[]; /** - * `Node`'s nodeName. + * The index of the node's related layout tree node in the `layoutTreeNodes` array returned by +`getSnapshot`, if any. */ - nodeName?: StringIndex[]; + layoutNodeIndex?: number; /** - * `Node`'s nodeValue. + * Document URL that `Document` or `FrameOwner` node points to. */ - nodeValue?: StringIndex[]; + documentURL?: string; /** - * `Node`'s id, corresponds to DOM.Node.backendNodeId. + * Base URL that `Document` or `FrameOwner` node uses for URL completion. */ - backendNodeId?: DOM.BackendNodeId[]; + baseURL?: string; /** - * Attributes of an `Element` node. Flatten name, value pairs. + * Only set for documents, contains the document's content language. */ - attributes?: ArrayOfStrings[]; + contentLanguage?: string; /** - * Only set for textarea elements, contains the text value. + * Only set for documents, contains the document's character set encoding. */ - textValue?: RareStringData; + documentEncoding?: string; /** - * Only set for input elements, contains the input's associated text value. + * `DocumentType` node's publicId. */ - inputValue?: RareStringData; + publicId?: string; /** - * Only set for radio and checkbox input elements, indicates if the element has been checked + * `DocumentType` node's systemId. */ - inputChecked?: RareBooleanData; + systemId?: string; /** - * Only set for option elements, indicates if the element has been selected + * Frame ID for frame owner elements and also for the document node. */ - optionSelected?: RareBooleanData; + frameId?: Page.FrameId; /** - * The index of the document in the list of the snapshot documents. + * The index of a frame owner element's content document in the `domNodes` array returned by +`getSnapshot`, if any. */ - contentDocumentIndex?: RareIntegerData; + contentDocumentIndex?: number; /** * Type of a pseudo element node. */ - pseudoType?: RareStringData; + pseudoType?: DOM.PseudoType; /** - * Pseudo element identifier for this node. Only present if there is a -valid pseudoType. + * Shadow root type. */ - pseudoIdentifier?: RareStringData; + shadowRootType?: DOM.ShadowRootType; /** * Whether this DOM node responds to mouse clicks. This includes nodes that have had click event listeners attached via JavaScript as well as anchor tags that naturally navigate when clicked. */ - isClickable?: RareBooleanData; + isClickable?: boolean; + /** + * Details of the node's event listeners, if any. + */ + eventListeners?: DOMDebugger.EventListener[]; /** * The selected url for nodes with a srcset attribute. */ - currentSourceURL?: RareStringData; + currentSourceURL?: string; /** * The url of the script (if any) that generates this node. */ - originURL?: RareStringData; + originURL?: string; + /** + * Scroll offsets, set when this node is a Document. + */ + scrollOffsetX?: number; + scrollOffsetY?: number; } /** - * Table of details of an element in the DOM tree with a LayoutObject. + * Details of post layout rendered text positions. The exact layout should not be regarded as +stable and may change between versions. */ - export interface LayoutTreeSnapshot { + export interface InlineTextBox { /** - * Index of the corresponding node in the `NodeTreeSnapshot` array returned by `captureSnapshot`. + * The bounding box in document coordinates. Note that scroll offset of the document is ignored. */ - nodeIndex: number[]; + boundingBox: DOM.Rect; /** - * Array of indexes specifying computed style strings, filtered according to the `computedStyles` parameter passed to `captureSnapshot`. + * The starting index in characters, for this post layout textbox substring. Characters that +would be represented as a surrogate pair in UTF-16 have length 2. */ - styles: ArrayOfStrings[]; + startCharacterIndex: number; /** - * The absolute position bounding box. + * The number of characters in this post layout textbox substring. Characters that would be +represented as a surrogate pair in UTF-16 have length 2. */ - bounds: Rectangle[]; + numCharacters: number; + } + /** + * Details of an element in the DOM tree with a LayoutObject. + */ + export interface LayoutTreeNode { /** - * Contents of the LayoutText, if any. + * The index of the related DOM node in the `domNodes` array returned by `getSnapshot`. */ - text: StringIndex[]; + domNodeIndex: number; /** - * Stacking context information. + * The bounding box in document coordinates. Note that scroll offset of the document is ignored. */ - stackingContexts: RareBooleanData; + boundingBox: DOM.Rect; /** - * Global paint order index, which is determined by the stacking order of the nodes. Nodes -that are painted together will have the same index. Only provided if includePaintOrder in -captureSnapshot was true. + * Contents of the LayoutText, if any. */ - paintOrders?: number[]; + layoutText?: string; /** - * The offset rect of nodes. Only available when includeDOMRects is set to true + * The post-layout inline text nodes, if any. */ - offsetRects?: Rectangle[]; + inlineTextNodes?: InlineTextBox[]; + /** + * Index into the `computedStyles` array returned by `getSnapshot`. + */ + styleIndex?: number; + /** + * Global paint order index, which is determined by the stacking order of the nodes. Nodes +that are painted together will have the same index. Only provided if includePaintOrder in +getSnapshot was true. + */ + paintOrder?: number; + /** + * Set to true to indicate the element begins a new stacking context. + */ + isStackingContext?: boolean; + } + /** + * A subset of the full ComputedStyle as defined by the request whitelist. + */ + export interface ComputedStyle { + /** + * Name/value pairs of computed style properties. + */ + properties: NameValue[]; + } + /** + * A name/value pair. + */ + export interface NameValue { + /** + * Attribute/property name. + */ + name: string; + /** + * Attribute/property value. + */ + value: string; + } + /** + * Index of the string in the strings table. + */ + export type StringIndex = number; + /** + * Index of the string in the strings table. + */ + export type ArrayOfStrings = StringIndex[]; + /** + * Data that is only present on rare nodes. + */ + export interface RareStringData { + index: number[]; + value: StringIndex[]; + } + export interface RareBooleanData { + index: number[]; + } + export interface RareIntegerData { + index: number[]; + value: number[]; + } + export type Rectangle = number[]; + /** + * Document snapshot. + */ + export interface DocumentSnapshot { + /** + * Document URL that `Document` or `FrameOwner` node points to. + */ + documentURL: StringIndex; + /** + * Document title. + */ + title: StringIndex; + /** + * Base URL that `Document` or `FrameOwner` node uses for URL completion. + */ + baseURL: StringIndex; + /** + * Contains the document's content language. + */ + contentLanguage: StringIndex; + /** + * Contains the document's character set encoding. + */ + encodingName: StringIndex; + /** + * `DocumentType` node's publicId. + */ + publicId: StringIndex; + /** + * `DocumentType` node's systemId. + */ + systemId: StringIndex; + /** + * Frame ID for frame owner elements and also for the document node. + */ + frameId: StringIndex; + /** + * A table with dom nodes. + */ + nodes: NodeTreeSnapshot; + /** + * The nodes in the layout tree. + */ + layout: LayoutTreeSnapshot; + /** + * The post-layout inline text nodes. + */ + textBoxes: TextBoxSnapshot; + /** + * Horizontal scroll offset. + */ + scrollOffsetX?: number; + /** + * Vertical scroll offset. + */ + scrollOffsetY?: number; + /** + * Document content width. + */ + contentWidth?: number; + /** + * Document content height. + */ + contentHeight?: number; + } + /** + * Table containing nodes. + */ + export interface NodeTreeSnapshot { + /** + * Parent node index. + */ + parentIndex?: number[]; + /** + * `Node`'s nodeType. + */ + nodeType?: number[]; + /** + * Type of the shadow root the `Node` is in. String values are equal to the `ShadowRootType` enum. + */ + shadowRootType?: RareStringData; + /** + * `Node`'s nodeName. + */ + nodeName?: StringIndex[]; + /** + * `Node`'s nodeValue. + */ + nodeValue?: StringIndex[]; + /** + * `Node`'s id, corresponds to DOM.Node.backendNodeId. + */ + backendNodeId?: DOM.BackendNodeId[]; + /** + * Attributes of an `Element` node. Flatten name, value pairs. + */ + attributes?: ArrayOfStrings[]; + /** + * Only set for textarea elements, contains the text value. + */ + textValue?: RareStringData; + /** + * Only set for input elements, contains the input's associated text value. + */ + inputValue?: RareStringData; + /** + * Only set for radio and checkbox input elements, indicates if the element has been checked + */ + inputChecked?: RareBooleanData; + /** + * Only set for option elements, indicates if the element has been selected + */ + optionSelected?: RareBooleanData; + /** + * The index of the document in the list of the snapshot documents. + */ + contentDocumentIndex?: RareIntegerData; + /** + * Type of a pseudo element node. + */ + pseudoType?: RareStringData; + /** + * Pseudo element identifier for this node. Only present if there is a +valid pseudoType. + */ + pseudoIdentifier?: RareStringData; + /** + * Whether this DOM node responds to mouse clicks. This includes nodes that have had click +event listeners attached via JavaScript as well as anchor tags that naturally navigate when +clicked. + */ + isClickable?: RareBooleanData; + /** + * The selected url for nodes with a srcset attribute. + */ + currentSourceURL?: RareStringData; + /** + * The url of the script (if any) that generates this node. + */ + originURL?: RareStringData; + } + /** + * Table of details of an element in the DOM tree with a LayoutObject. + */ + export interface LayoutTreeSnapshot { + /** + * Index of the corresponding node in the `NodeTreeSnapshot` array returned by `captureSnapshot`. + */ + nodeIndex: number[]; + /** + * Array of indexes specifying computed style strings, filtered according to the `computedStyles` parameter passed to `captureSnapshot`. + */ + styles: ArrayOfStrings[]; + /** + * The absolute position bounding box. + */ + bounds: Rectangle[]; + /** + * Contents of the LayoutText, if any. + */ + text: StringIndex[]; + /** + * Stacking context information. + */ + stackingContexts: RareBooleanData; + /** + * Global paint order index, which is determined by the stacking order of the nodes. Nodes +that are painted together will have the same index. Only provided if includePaintOrder in +captureSnapshot was true. + */ + paintOrders?: number[]; + /** + * The offset rect of nodes. Only available when includeDOMRects is set to true + */ + offsetRects?: Rectangle[]; /** * The scroll rect of nodes. Only available when includeDOMRects is set to true */ @@ -6203,22 +6353,84 @@ The final text color opacity is computed based on the opacity of all overlapping } } - export module DeviceOrientation { - - + export module DeviceAccess { /** - * Clears the overridden Device Orientation. + * Device request id. */ - export type clearDeviceOrientationOverrideParameters = { - } - export type clearDeviceOrientationOverrideReturnValue = { - } + export type RequestId = string; /** - * Overrides the Device Orientation. + * A device id. */ - export type setDeviceOrientationOverrideParameters = { + export type DeviceId = string; + /** + * Device information displayed in a user prompt to select a device. + */ + export interface PromptDevice { + id: DeviceId; /** - * Mock alpha + * Display name as it appears in a device request user prompt. + */ + name: string; + } + + /** + * A device request opened a user prompt to select a device. Respond with the +selectPrompt or cancelPrompt command. + */ + export type deviceRequestPromptedPayload = { + id: RequestId; + devices: PromptDevice[]; + } + + /** + * Enable events in this domain. + */ + export type enableParameters = { + } + export type enableReturnValue = { + } + /** + * Disable events in this domain. + */ + export type disableParameters = { + } + export type disableReturnValue = { + } + /** + * Select a device in response to a DeviceAccess.deviceRequestPrompted event. + */ + export type selectPromptParameters = { + id: RequestId; + deviceId: DeviceId; + } + export type selectPromptReturnValue = { + } + /** + * Cancel a prompt in response to a DeviceAccess.deviceRequestPrompted event. + */ + export type cancelPromptParameters = { + id: RequestId; + } + export type cancelPromptReturnValue = { + } + } + + export module DeviceOrientation { + + + /** + * Clears the overridden Device Orientation. + */ + export type clearDeviceOrientationOverrideParameters = { + } + export type clearDeviceOrientationOverrideReturnValue = { + } + /** + * Overrides the Device Orientation. + */ + export type setDeviceOrientationOverrideParameters = { + /** + * Mock alpha */ alpha: number; /** @@ -6387,6 +6599,95 @@ See https://w3c.github.io/sensors/#automation for more information. export interface PressureMetadata { available?: boolean; } + export interface WorkAreaInsets { + /** + * Work area top inset in pixels. Default is 0; + */ + top?: number; + /** + * Work area left inset in pixels. Default is 0; + */ + left?: number; + /** + * Work area bottom inset in pixels. Default is 0; + */ + bottom?: number; + /** + * Work area right inset in pixels. Default is 0; + */ + right?: number; + } + export type ScreenId = string; + /** + * Screen information similar to the one returned by window.getScreenDetails() method, +see https://w3c.github.io/window-management/#screendetailed. + */ + export interface ScreenInfo { + /** + * Offset of the left edge of the screen. + */ + left: number; + /** + * Offset of the top edge of the screen. + */ + top: number; + /** + * Width of the screen. + */ + width: number; + /** + * Height of the screen. + */ + height: number; + /** + * Offset of the left edge of the available screen area. + */ + availLeft: number; + /** + * Offset of the top edge of the available screen area. + */ + availTop: number; + /** + * Width of the available screen area. + */ + availWidth: number; + /** + * Height of the available screen area. + */ + availHeight: number; + /** + * Specifies the screen's device pixel ratio. + */ + devicePixelRatio: number; + /** + * Specifies the screen's orientation. + */ + orientation: ScreenOrientation; + /** + * Specifies the screen's color depth in bits. + */ + colorDepth: number; + /** + * Indicates whether the device has multiple screens. + */ + isExtended: boolean; + /** + * Indicates whether the screen is internal to the device or external, attached to the device. + */ + isInternal: boolean; + /** + * Indicates whether the screen is set as the the operating system primary screen. + */ + isPrimary: boolean; + /** + * Specifies the descriptive label for the screen. + */ + label: string; + /** + * Specifies the unique identifier of the screen. + */ + id: ScreenId; + } /** * Enum of image types that can be disabled. */ @@ -6974,3243 +7275,3768 @@ of size 100lvh. } export type setSmallViewportHeightDifferenceOverrideReturnValue = { } - } - - /** - * This domain provides experimental commands only supported in headless mode. - */ - export module HeadlessExperimental { /** - * Encoding options for a screenshot. + * Returns device's screen configuration. */ - export interface ScreenshotParams { + export type getScreenInfosParameters = { + } + export type getScreenInfosReturnValue = { + screenInfos: ScreenInfo[]; + } + /** + * Add a new screen to the device. Only supported in headless mode. + */ + export type addScreenParameters = { /** - * Image compression format (defaults to png). + * Offset of the left edge of the screen in pixels. */ - format?: "jpeg"|"png"|"webp"; + left: number; /** - * Compression quality from range [0..100] (jpeg and webp only). + * Offset of the top edge of the screen in pixels. */ - quality?: number; + top: number; /** - * Optimize image encoding for speed, not for resulting size (defaults to false) + * The width of the screen in pixels. */ - optimizeForSpeed?: boolean; - } - - - /** - * Sends a BeginFrame to the target and returns when the frame was completed. Optionally captures a -screenshot from the resulting frame. Requires that the target was created with enabled -BeginFrameControl. Designed for use with --run-all-compositor-stages-before-draw, see also -https://goo.gle/chrome-headless-rendering for more background. - */ - export type beginFrameParameters = { + width: number; /** - * Timestamp of this BeginFrame in Renderer TimeTicks (milliseconds of uptime). If not set, -the current time will be used. + * The height of the screen in pixels. */ - frameTimeTicks?: number; + height: number; /** - * The interval between BeginFrames that is reported to the compositor, in milliseconds. -Defaults to a 60 frames/second interval, i.e. about 16.666 milliseconds. + * Specifies the screen's work area. Default is entire screen. */ - interval?: number; + workAreaInsets?: WorkAreaInsets; /** - * Whether updates should not be committed and drawn onto the display. False by default. If -true, only side effects of the BeginFrame will be run, such as layout and animations, but -any visual updates may not be visible on the display or in screenshots. + * Specifies the screen's device pixel ratio. Default is 1. */ - noDisplayUpdates?: boolean; + devicePixelRatio?: number; /** - * If set, a screenshot of the frame will be captured and returned in the response. Otherwise, -no screenshot will be captured. Note that capturing a screenshot can fail, for example, -during renderer initialization. In such a case, no screenshot data will be returned. + * Specifies the screen's rotation angle. Available values are 0, 90, 180 and 270. Default is 0. */ - screenshot?: ScreenshotParams; - } - export type beginFrameReturnValue = { + rotation?: number; /** - * Whether the BeginFrame resulted in damage and, thus, a new frame was committed to the -display. Reported for diagnostic uses, may be removed in the future. + * Specifies the screen's color depth in bits. Default is 24. */ - hasDamage: boolean; + colorDepth?: number; /** - * Base64-encoded image data of the screenshot, if one was requested and successfully taken. + * Specifies the descriptive label for the screen. Default is none. */ - screenshotData?: binary; - } - /** - * Disables headless events for the target. - */ - export type disableParameters = { + label?: string; + /** + * Indicates whether the screen is internal to the device or external, attached to the device. Default is false. + */ + isInternal?: boolean; } - export type disableReturnValue = { + export type addScreenReturnValue = { + screenInfo: ScreenInfo; } /** - * Enables headless events for the target. + * Remove screen from the device. Only supported in headless mode. */ - export type enableParameters = { + export type removeScreenParameters = { + screenId: ScreenId; } - export type enableReturnValue = { + export type removeScreenReturnValue = { } } /** - * Input/Output operations for streams produced by DevTools. + * EventBreakpoints permits setting JavaScript breakpoints on operations and events +occurring in native code invoked from JavaScript. Once breakpoint is hit, it is +reported through Debugger domain, similarly to regular breakpoints being hit. */ - export module IO { - /** - * This is either obtained from another method or specified as `blob:` where -`` is an UUID of a Blob. - */ - export type StreamHandle = string; + export module EventBreakpoints { /** - * Close the stream, discard any temporary backing storage. + * Sets breakpoint on particular native event. */ - export type closeParameters = { + export type setInstrumentationBreakpointParameters = { /** - * Handle of the stream to close. + * Instrumentation name to stop on. */ - handle: StreamHandle; + eventName: string; } - export type closeReturnValue = { + export type setInstrumentationBreakpointReturnValue = { } /** - * Read a chunk of the stream + * Removes breakpoint on particular native event. */ - export type readParameters = { - /** - * Handle of the stream to read. - */ - handle: StreamHandle; - /** - * Seek to the specified offset before reading (if not specified, proceed with offset -following the last read). Some types of streams may only support sequential reads. - */ - offset?: number; + export type removeInstrumentationBreakpointParameters = { /** - * Maximum number of bytes to read (left upon the agent discretion if not specified). + * Instrumentation name to stop on. */ - size?: number; + eventName: string; } - export type readReturnValue = { - /** - * Set if the data is base64-encoded - */ - base64Encoded?: boolean; - /** - * Data that were read. - */ - data: string; - /** - * Set if the end-of-file condition occurred while reading. - */ - eof: boolean; + export type removeInstrumentationBreakpointReturnValue = { } /** - * Return UUID of Blob object specified by a remote object id. + * Removes all breakpoints */ - export type resolveBlobParameters = { - /** - * Object id of a Blob object wrapper. - */ - objectId: Runtime.RemoteObjectId; + export type disableParameters = { } - export type resolveBlobReturnValue = { - /** - * UUID of the specified Blob. - */ - uuid: string; + export type disableReturnValue = { } } - export module FileSystem { - export interface File { - name: string; - /** - * Timestamp - */ - lastModified: Network.TimeSinceEpoch; - /** - * Size in bytes - */ - size: number; - type: string; - } - export interface Directory { - name: string; - nestedDirectories: string[]; - /** - * Files that are directly nested under this directory. - */ - nestedFiles: File[]; - } - export interface BucketFileSystemLocator { - /** - * Storage key - */ - storageKey: Storage.SerializedStorageKey; - /** - * Bucket name. Not passing a `bucketName` will retrieve the default Bucket. (https://developer.mozilla.org/en-US/docs/Web/API/Storage_API#storage_buckets) - */ - bucketName?: string; - /** - * Path to the directory using each path component as an array item. - */ - pathComponents: string[]; - } + /** + * Defines commands and events for browser extensions. + */ + export module Extensions { + /** + * Storage areas. + */ + export type StorageArea = "session"|"local"|"sync"|"managed"; - export type getDirectoryParameters = { - bucketFileSystemLocator: BucketFileSystemLocator; - } - export type getDirectoryReturnValue = { - /** - * Returns the directory object at the path. - */ - directory: Directory; - } - } - - export module IndexedDB { /** - * Database with an array of object stores. + * Installs an unpacked extension from the filesystem similar to +--load-extension CLI flags. Returns extension ID once the extension +has been installed. Available if the client is connected using the +--remote-debugging-pipe flag and the --enable-unsafe-extension-debugging +flag is set. */ - export interface DatabaseWithObjectStores { - /** - * Database name. - */ - name: string; + export type loadUnpackedParameters = { /** - * Database version (type is not 'integer', as the standard -requires the version number to be 'unsigned long long') + * Absolute file path. */ - version: number; + path: string; + } + export type loadUnpackedReturnValue = { /** - * Object stores in this database. + * Extension id. */ - objectStores: ObjectStore[]; + id: string; } /** - * Object store. + * Uninstalls an unpacked extension (others not supported) from the profile. +Available if the client is connected using the --remote-debugging-pipe flag +and the --enable-unsafe-extension-debugging. */ - export interface ObjectStore { - /** - * Object store name. - */ - name: string; - /** - * Object store key path. - */ - keyPath: KeyPath; - /** - * If true, object store has auto increment flag set. - */ - autoIncrement: boolean; + export type uninstallParameters = { /** - * Indexes in this object store. + * Extension id. */ - indexes: ObjectStoreIndex[]; + id: string; + } + export type uninstallReturnValue = { } /** - * Object store index. + * Gets data from extension storage in the given `storageArea`. If `keys` is +specified, these are used to filter the result. */ - export interface ObjectStoreIndex { - /** - * Index name. - */ - name: string; + export type getStorageItemsParameters = { /** - * Index key path. + * ID of extension. */ - keyPath: KeyPath; + id: string; /** - * If true, index is unique. + * StorageArea to retrieve data from. */ - unique: boolean; + storageArea: StorageArea; /** - * If true, index allows multiple entries for a key. + * Keys to retrieve. */ - multiEntry: boolean; + keys?: string[]; + } + export type getStorageItemsReturnValue = { + data: { [key: string]: string }; } /** - * Key. + * Removes `keys` from extension storage in the given `storageArea`. */ - export interface Key { - /** - * Key type. - */ - type: "number"|"string"|"date"|"array"; - /** - * Number value. - */ - number?: number; + export type removeStorageItemsParameters = { /** - * String value. + * ID of extension. */ - string?: string; + id: string; /** - * Date value. + * StorageArea to remove data from. */ - date?: number; + storageArea: StorageArea; /** - * Array value. + * Keys to remove. */ - array?: Key[]; + keys: string[]; + } + export type removeStorageItemsReturnValue = { } /** - * Key range. + * Clears extension storage in the given `storageArea`. */ - export interface KeyRange { - /** - * Lower bound. - */ - lower?: Key; - /** - * Upper bound. - */ - upper?: Key; + export type clearStorageItemsParameters = { /** - * If true lower bound is open. + * ID of extension. */ - lowerOpen: boolean; + id: string; /** - * If true upper bound is open. + * StorageArea to remove data from. */ - upperOpen: boolean; + storageArea: StorageArea; + } + export type clearStorageItemsReturnValue = { } /** - * Data entry. + * Sets `values` in extension storage in the given `storageArea`. The provided `values` +will be merged with existing values in the storage area. */ - export interface DataEntry { + export type setStorageItemsParameters = { /** - * Key object. + * ID of extension. */ - key: Runtime.RemoteObject; + id: string; /** - * Primary key object. + * StorageArea to set data in. */ - primaryKey: Runtime.RemoteObject; + storageArea: StorageArea; /** - * Value object. + * Values to set. */ - value: Runtime.RemoteObject; + values: { [key: string]: string }; + } + export type setStorageItemsReturnValue = { } + } + + /** + * This domain allows interacting with the FedCM dialog. + */ + export module FedCm { /** - * Key path. + * Whether this is a sign-up or sign-in action for this account, i.e. +whether this account has ever been used to sign in to this RP before. */ - export interface KeyPath { - /** - * Key path type. - */ - type: "null"|"string"|"array"; + export type LoginState = "SignIn"|"SignUp"; + /** + * The types of FedCM dialogs. + */ + export type DialogType = "AccountChooser"|"AutoReauthn"|"ConfirmIdpLogin"|"Error"; + /** + * The buttons on the FedCM dialog. + */ + export type DialogButton = "ConfirmIdpLoginContinue"|"ErrorGotIt"|"ErrorMoreDetails"; + /** + * The URLs that each account has + */ + export type AccountUrlType = "TermsOfService"|"PrivacyPolicy"; + /** + * Corresponds to IdentityRequestAccount + */ + export interface Account { + accountId: string; + email: string; + name: string; + givenName: string; + pictureUrl: string; + idpConfigUrl: string; + idpLoginUrl: string; + loginState: LoginState; /** - * String value. + * These two are only set if the loginState is signUp */ - string?: string; + termsOfServiceUrl?: string; + privacyPolicyUrl?: string; + } + + export type dialogShownPayload = { + dialogId: string; + dialogType: DialogType; + accounts: Account[]; /** - * Array value. + * These exist primarily so that the caller can verify the +RP context was used appropriately. */ - array?: string[]; + title: string; + subtitle?: string; } - - /** - * Clears all entries from an object store. + * Triggered when a dialog is closed, either by user action, JS abort, +or a command below. */ - export type clearObjectStoreParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; + export type dialogClosedPayload = { + dialogId: string; + } + + export type enableParameters = { /** - * Storage key. + * Allows callers to disable the promise rejection delay that would +normally happen, if this is unimportant to what's being tested. +(step 4 of https://fedidcg.github.io/FedCM/#browser-api-rp-sign-in) */ - storageKey?: string; + disableRejectionDelay?: boolean; + } + export type enableReturnValue = { + } + export type disableParameters = { + } + export type disableReturnValue = { + } + export type selectAccountParameters = { + dialogId: string; + accountIndex: number; + } + export type selectAccountReturnValue = { + } + export type clickDialogButtonParameters = { + dialogId: string; + dialogButton: DialogButton; + } + export type clickDialogButtonReturnValue = { + } + export type openUrlParameters = { + dialogId: string; + accountIndex: number; + accountUrlType: AccountUrlType; + } + export type openUrlReturnValue = { + } + export type dismissDialogParameters = { + dialogId: string; + triggerCooldown?: boolean; + } + export type dismissDialogReturnValue = { + } + /** + * Resets the cooldown time, if any, to allow the next FedCM call to show +a dialog even if one was recently dismissed by the user. + */ + export type resetCooldownParameters = { + } + export type resetCooldownReturnValue = { + } + } + + /** + * A domain for letting clients substitute browser's network layer with client code. + */ + export module Fetch { + /** + * Unique request identifier. +Note that this does not identify individual HTTP requests that are part of +a network request. + */ + export type RequestId = string; + /** + * Stages of the request to handle. Request will intercept before the request is +sent. Response will intercept after the response is received (but before response +body is received). + */ + export type RequestStage = "Request"|"Response"; + export interface RequestPattern { /** - * Storage bucket. If not specified, it uses the default bucket. + * Wildcards (`'*'` -> zero or more, `'?'` -> exactly one) are allowed. Escape character is +backslash. Omitting is equivalent to `"*"`. */ - storageBucket?: Storage.StorageBucket; + urlPattern?: string; /** - * Database name. + * If set, only requests for matching resource types will be intercepted. */ - databaseName: string; + resourceType?: Network.ResourceType; /** - * Object store name. + * Stage at which to begin intercepting requests. Default is Request. */ - objectStoreName: string; + requestStage?: RequestStage; } - export type clearObjectStoreReturnValue = { + /** + * Response HTTP header entry + */ + export interface HeaderEntry { + name: string; + value: string; } /** - * Deletes a database. + * Authorization challenge for HTTP status code 401 or 407. */ - export type deleteDatabaseParameters = { + export interface AuthChallenge { /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. + * Source of the authentication challenge. */ - securityOrigin?: string; + source?: "Server"|"Proxy"; /** - * Storage key. + * Origin of the challenger. */ - storageKey?: string; + origin: string; /** - * Storage bucket. If not specified, it uses the default bucket. + * The authentication scheme used, such as basic or digest */ - storageBucket?: Storage.StorageBucket; + scheme: string; /** - * Database name. + * The realm of the challenge. May be empty. */ - databaseName: string; - } - export type deleteDatabaseReturnValue = { + realm: string; } /** - * Delete a range of entries from an object store + * Response to an AuthChallenge. */ - export type deleteObjectStoreEntriesParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; + export interface AuthChallengeResponse { /** - * Storage key. + * The decision on what to do in response to the authorization challenge. Default means +deferring to the default behavior of the net stack, which will likely either the Cancel +authentication or display a popup dialog box. */ - storageKey?: string; + response: "Default"|"CancelAuth"|"ProvideCredentials"; /** - * Storage bucket. If not specified, it uses the default bucket. + * The username to provide, possibly empty. Should only be set if response is +ProvideCredentials. */ - storageBucket?: Storage.StorageBucket; - databaseName: string; - objectStoreName: string; + username?: string; /** - * Range of entry keys to delete + * The password to provide, possibly empty. Should only be set if response is +ProvideCredentials. */ - keyRange: KeyRange; - } - export type deleteObjectStoreEntriesReturnValue = { - } - /** - * Disables events from backend. - */ - export type disableParameters = { - } - export type disableReturnValue = { - } - /** - * Enables events from backend. - */ - export type enableParameters = { - } - export type enableReturnValue = { + password?: string; } + /** - * Requests data from object store or index. + * Issued when the domain is enabled and the request URL matches the +specified filter. The request is paused until the client responds +with one of continueRequest, failRequest or fulfillRequest. +The stage of the request can be determined by presence of responseErrorReason +and responseStatusCode -- the request is at the response stage if either +of these fields is present and in the request stage otherwise. +Redirect responses and subsequent requests are reported similarly to regular +responses and requests. Redirect responses may be distinguished by the value +of `responseStatusCode` (which is one of 301, 302, 303, 307, 308) along with +presence of the `location` header. Requests resulting from a redirect will +have `redirectedRequestId` field set. */ - export type requestDataParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; + export type requestPausedPayload = { /** - * Storage key. + * Each request the page makes will have a unique id. */ - storageKey?: string; + requestId: RequestId; /** - * Storage bucket. If not specified, it uses the default bucket. + * The details of the request. */ - storageBucket?: Storage.StorageBucket; + request: Network.Request; /** - * Database name. + * The id of the frame that initiated the request. */ - databaseName: string; + frameId: Page.FrameId; /** - * Object store name. + * How the requested resource will be used. */ - objectStoreName: string; + resourceType: Network.ResourceType; /** - * Index name, empty string for object store data requests. + * Response error if intercepted at response stage. */ - indexName: string; + responseErrorReason?: Network.ErrorReason; /** - * Number of records to skip. + * Response code if intercepted at response stage. */ - skipCount: number; + responseStatusCode?: number; /** - * Number of records to fetch. + * Response status text if intercepted at response stage. */ - pageSize: number; + responseStatusText?: string; /** - * Key range. + * Response headers if intercepted at the response stage. */ - keyRange?: KeyRange; - } - export type requestDataReturnValue = { + responseHeaders?: HeaderEntry[]; /** - * Array of object store data entries. + * If the intercepted request had a corresponding Network.requestWillBeSent event fired for it, +then this networkId will be the same as the requestId present in the requestWillBeSent event. */ - objectStoreDataEntries: DataEntry[]; + networkId?: Network.RequestId; /** - * If true, there are more entries to fetch in the given range. + * If the request is due to a redirect response from the server, the id of the request that +has caused the redirect. */ - hasMore: boolean; + redirectedRequestId?: RequestId; } /** - * Gets metadata of an object store. + * Issued when the domain is enabled with handleAuthRequests set to true. +The request is paused until client responds with continueWithAuth. */ - export type getMetadataParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; - /** - * Storage key. - */ - storageKey?: string; + export type authRequiredPayload = { /** - * Storage bucket. If not specified, it uses the default bucket. + * Each request the page makes will have a unique id. */ - storageBucket?: Storage.StorageBucket; + requestId: RequestId; /** - * Database name. + * The details of the request. */ - databaseName: string; + request: Network.Request; /** - * Object store name. + * The id of the frame that initiated the request. */ - objectStoreName: string; - } - export type getMetadataReturnValue = { + frameId: Page.FrameId; /** - * the entries count + * How the requested resource will be used. */ - entriesCount: number; + resourceType: Network.ResourceType; /** - * the current value of key generator, to become the next inserted -key into the object store. Valid if objectStore.autoIncrement -is true. + * Details of the Authorization Challenge encountered. +If this is set, client should respond with continueRequest that +contains AuthChallengeResponse. */ - keyGeneratorValue: number; + authChallenge: AuthChallenge; } + /** - * Requests database with given name in given frame. + * Disables the fetch domain. */ - export type requestDatabaseParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; - /** - * Storage key. - */ - storageKey?: string; - /** - * Storage bucket. If not specified, it uses the default bucket. - */ - storageBucket?: Storage.StorageBucket; - /** - * Database name. - */ - databaseName: string; + export type disableParameters = { } - export type requestDatabaseReturnValue = { - /** - * Database with an array of object stores. - */ - databaseWithObjectStores: DatabaseWithObjectStores; + export type disableReturnValue = { } /** - * Requests database names for given security origin. + * Enables issuing of requestPaused events. A request will be paused until client +calls one of failRequest, fulfillRequest or continueRequest/continueWithAuth. */ - export type requestDatabaseNamesParameters = { - /** - * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. -Security origin. - */ - securityOrigin?: string; + export type enableParameters = { /** - * Storage key. + * If specified, only requests matching any of these patterns will produce +fetchRequested event and will be paused until clients response. If not set, +all requests will be affected. */ - storageKey?: string; + patterns?: RequestPattern[]; /** - * Storage bucket. If not specified, it uses the default bucket. + * If true, authRequired events will be issued and requests will be paused +expecting a call to continueWithAuth. */ - storageBucket?: Storage.StorageBucket; + handleAuthRequests?: boolean; } - export type requestDatabaseNamesReturnValue = { - /** - * Database names for origin. - */ - databaseNames: string[]; + export type enableReturnValue = { } - } - - export module Input { - export interface TouchPoint { - /** - * X coordinate of the event relative to the main frame's viewport in CSS pixels. - */ - x: number; - /** - * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to -the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. - */ - y: number; - /** - * X radius of the touch area (default: 1.0). - */ - radiusX?: number; + /** + * Causes the request to fail with specified reason. + */ + export type failRequestParameters = { /** - * Y radius of the touch area (default: 1.0). + * An id the client received in requestPaused event. */ - radiusY?: number; + requestId: RequestId; /** - * Rotation angle (default: 0.0). + * Causes the request to fail with the given reason. */ - rotationAngle?: number; + errorReason: Network.ErrorReason; + } + export type failRequestReturnValue = { + } + /** + * Provides response to the request. + */ + export type fulfillRequestParameters = { /** - * Force (default: 1.0). + * An id the client received in requestPaused event. */ - force?: number; + requestId: RequestId; /** - * The normalized tangential pressure, which has a range of [-1,1] (default: 0). + * An HTTP response code. */ - tangentialPressure?: number; + responseCode: number; /** - * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0) + * Response headers. */ - tiltX?: number; + responseHeaders?: HeaderEntry[]; /** - * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). + * Alternative way of specifying response headers as a \0-separated +series of name: value pairs. Prefer the above method unless you +need to represent some non-UTF8 values that can't be transmitted +over the protocol as text. */ - tiltY?: number; + binaryResponseHeaders?: binary; /** - * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). + * A response body. If absent, original response body will be used if +the request is intercepted at the response stage and empty body +will be used if the request is intercepted at the request stage. */ - twist?: number; + body?: binary; /** - * Identifier used to track touch sources between events, must be unique within an event. + * A textual representation of responseCode. +If absent, a standard phrase matching responseCode is used. */ - id?: number; + responsePhrase?: string; + } + export type fulfillRequestReturnValue = { } - export type GestureSourceType = "default"|"touch"|"mouse"; - export type MouseButton = "none"|"left"|"middle"|"right"|"back"|"forward"; /** - * UTC time in seconds, counted from January 1, 1970. + * Continues the request, optionally modifying some of its parameters. */ - export type TimeSinceEpoch = number; - export interface DragDataItem { + export type continueRequestParameters = { /** - * Mime type of the dragged data. + * An id the client received in requestPaused event. */ - mimeType: string; + requestId: RequestId; /** - * Depending of the value of `mimeType`, it contains the dragged link, -text, HTML markup or any other data. + * If set, the request url will be modified in a way that's not observable by page. */ - data: string; + url?: string; /** - * Title associated with a link. Only valid when `mimeType` == "text/uri-list". + * If set, the request method is overridden. */ - title?: string; + method?: string; /** - * Stores the base URL for the contained markup. Only valid when `mimeType` -== "text/html". + * If set, overrides the post data in the request. */ - baseURL?: string; - } - export interface DragData { - items: DragDataItem[]; + postData?: binary; /** - * List of filenames that should be included when dropping + * If set, overrides the request headers. Note that the overrides do not +extend to subsequent redirect hops, if a redirect happens. Another override +may be applied to a different request produced by a redirect. */ - files?: string[]; + headers?: HeaderEntry[]; /** - * Bit field representing allowed drag operations. Copy = 1, Link = 2, Move = 16 + * If set, overrides response interception behavior for this request. */ - dragOperationsMask: number; + interceptResponse?: boolean; } - - /** - * Emitted only when `Input.setInterceptDrags` is enabled. Use this data with `Input.dispatchDragEvent` to -restore normal drag and drop behavior. - */ - export type dragInterceptedPayload = { - data: DragData; + export type continueRequestReturnValue = { } - /** - * Dispatches a drag event into the page. + * Continues a request supplying authChallengeResponse following authRequired event. */ - export type dispatchDragEventParameters = { - /** - * Type of the drag event. - */ - type: "dragEnter"|"dragOver"|"drop"|"dragCancel"; - /** - * X coordinate of the event relative to the main frame's viewport in CSS pixels. - */ - x: number; + export type continueWithAuthParameters = { /** - * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to -the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. + * An id the client received in authRequired event. */ - y: number; - data: DragData; + requestId: RequestId; /** - * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 -(default: 0). + * Response to with an authChallenge. */ - modifiers?: number; + authChallengeResponse: AuthChallengeResponse; } - export type dispatchDragEventReturnValue = { + export type continueWithAuthReturnValue = { } /** - * Dispatches a key event to the page. + * Continues loading of the paused response, optionally modifying the +response headers. If either responseCode or headers are modified, all of them +must be present. */ - export type dispatchKeyEventParameters = { - /** - * Type of the key event. - */ - type: "keyDown"|"keyUp"|"rawKeyDown"|"char"; + export type continueResponseParameters = { /** - * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 -(default: 0). + * An id the client received in requestPaused event. */ - modifiers?: number; + requestId: RequestId; /** - * Time at which the event occurred. + * An HTTP response code. If absent, original response code will be used. */ - timestamp?: TimeSinceEpoch; + responseCode?: number; /** - * Text as generated by processing a virtual key code with a keyboard layout. Not needed for -for `keyUp` and `rawKeyDown` events (default: "") + * A textual representation of responseCode. +If absent, a standard phrase matching responseCode is used. */ - text?: string; + responsePhrase?: string; /** - * Text that would have been generated by the keyboard if no modifiers were pressed (except for -shift). Useful for shortcut (accelerator) key handling (default: ""). + * Response headers. If absent, original response headers will be used. */ - unmodifiedText?: string; + responseHeaders?: HeaderEntry[]; /** - * Unique key identifier (e.g., 'U+0041') (default: ""). + * Alternative way of specifying response headers as a \0-separated +series of name: value pairs. Prefer the above method unless you +need to represent some non-UTF8 values that can't be transmitted +over the protocol as text. */ - keyIdentifier?: string; + binaryResponseHeaders?: binary; + } + export type continueResponseReturnValue = { + } + /** + * Causes the body of the response to be received from the server and +returned as a single string. May only be issued for a request that +is paused in the Response stage and is mutually exclusive with +takeResponseBodyForInterceptionAsStream. Calling other methods that +affect the request or disabling fetch domain before body is received +results in an undefined behavior. +Note that the response body is not available for redirects. Requests +paused in the _redirect received_ state may be differentiated by +`responseCode` and presence of `location` response header, see +comments to `requestPaused` for details. + */ + export type getResponseBodyParameters = { /** - * Unique DOM defined string value for each physical key (e.g., 'KeyA') (default: ""). + * Identifier for the intercepted request to get body for. */ - code?: string; + requestId: RequestId; + } + export type getResponseBodyReturnValue = { /** - * Unique DOM defined string value describing the meaning of the key in the context of active -modifiers, keyboard layout, etc (e.g., 'AltGr') (default: ""). + * Response body. */ - key?: string; + body: string; /** - * Windows virtual key code (default: 0). + * True, if content was sent as base64. */ - windowsVirtualKeyCode?: number; + base64Encoded: boolean; + } + /** + * Returns a handle to the stream representing the response body. +The request must be paused in the HeadersReceived stage. +Note that after this command the request can't be continued +as is -- client either needs to cancel it or to provide the +response body. +The stream only supports sequential read, IO.read will fail if the position +is specified. +This method is mutually exclusive with getResponseBody. +Calling other methods that affect the request or disabling fetch +domain before body is received results in an undefined behavior. + */ + export type takeResponseBodyAsStreamParameters = { + requestId: RequestId; + } + export type takeResponseBodyAsStreamReturnValue = { + stream: IO.StreamHandle; + } + } + + export module FileSystem { + export interface File { + name: string; /** - * Native virtual key code (default: 0). + * Timestamp */ - nativeVirtualKeyCode?: number; + lastModified: Network.TimeSinceEpoch; /** - * Whether the event was generated from auto repeat (default: false). + * Size in bytes */ - autoRepeat?: boolean; + size: number; + type: string; + } + export interface Directory { + name: string; + nestedDirectories: string[]; /** - * Whether the event was generated from the keypad (default: false). + * Files that are directly nested under this directory. */ - isKeypad?: boolean; + nestedFiles: File[]; + } + export interface BucketFileSystemLocator { /** - * Whether the event was a system key event (default: false). + * Storage key */ - isSystemKey?: boolean; + storageKey: Storage.SerializedStorageKey; /** - * Whether the event was from the left or right side of the keyboard. 1=Left, 2=Right (default: -0). + * Bucket name. Not passing a `bucketName` will retrieve the default Bucket. (https://developer.mozilla.org/en-US/docs/Web/API/Storage_API#storage_buckets) */ - location?: number; + bucketName?: string; /** - * Editing commands to send with the key event (e.g., 'selectAll') (default: []). -These are related to but not equal the command names used in `document.execCommand` and NSStandardKeyBindingResponding. -See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/editing/commands/editor_command_names.h for valid command names. + * Path to the directory using each path component as an array item. */ - commands?: string[]; + pathComponents: string[]; } - export type dispatchKeyEventReturnValue = { + + + export type getDirectoryParameters = { + bucketFileSystemLocator: BucketFileSystemLocator; } - /** - * This method emulates inserting text that doesn't come from a key press, -for example an emoji keyboard or an IME. - */ - export type insertTextParameters = { + export type getDirectoryReturnValue = { /** - * The text to insert. + * Returns the directory object at the path. */ - text: string; - } - export type insertTextReturnValue = { + directory: Directory; } + } + + /** + * This domain provides experimental commands only supported in headless mode. + */ + export module HeadlessExperimental { /** - * This method sets the current candidate text for IME. -Use imeCommitComposition to commit the final text. -Use imeSetComposition with empty string as text to cancel composition. + * Encoding options for a screenshot. */ - export type imeSetCompositionParameters = { - /** - * The text to insert - */ - text: string; - /** - * selection start - */ - selectionStart: number; + export interface ScreenshotParams { /** - * selection end + * Image compression format (defaults to png). */ - selectionEnd: number; + format?: "jpeg"|"png"|"webp"; /** - * replacement start + * Compression quality from range [0..100] (jpeg and webp only). */ - replacementStart?: number; + quality?: number; /** - * replacement end + * Optimize image encoding for speed, not for resulting size (defaults to false) */ - replacementEnd?: number; - } - export type imeSetCompositionReturnValue = { + optimizeForSpeed?: boolean; } + + /** - * Dispatches a mouse event to the page. + * Sends a BeginFrame to the target and returns when the frame was completed. Optionally captures a +screenshot from the resulting frame. Requires that the target was created with enabled +BeginFrameControl. Designed for use with --run-all-compositor-stages-before-draw, see also +https://goo.gle/chrome-headless-rendering for more background. */ - export type dispatchMouseEventParameters = { - /** - * Type of the mouse event. - */ - type: "mousePressed"|"mouseReleased"|"mouseMoved"|"mouseWheel"; + export type beginFrameParameters = { /** - * X coordinate of the event relative to the main frame's viewport in CSS pixels. + * Timestamp of this BeginFrame in Renderer TimeTicks (milliseconds of uptime). If not set, +the current time will be used. */ - x: number; + frameTimeTicks?: number; /** - * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to -the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. + * The interval between BeginFrames that is reported to the compositor, in milliseconds. +Defaults to a 60 frames/second interval, i.e. about 16.666 milliseconds. */ - y: number; + interval?: number; /** - * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 -(default: 0). + * Whether updates should not be committed and drawn onto the display. False by default. If +true, only side effects of the BeginFrame will be run, such as layout and animations, but +any visual updates may not be visible on the display or in screenshots. */ - modifiers?: number; + noDisplayUpdates?: boolean; /** - * Time at which the event occurred. + * If set, a screenshot of the frame will be captured and returned in the response. Otherwise, +no screenshot will be captured. Note that capturing a screenshot can fail, for example, +during renderer initialization. In such a case, no screenshot data will be returned. */ - timestamp?: TimeSinceEpoch; + screenshot?: ScreenshotParams; + } + export type beginFrameReturnValue = { /** - * Mouse button (default: "none"). + * Whether the BeginFrame resulted in damage and, thus, a new frame was committed to the +display. Reported for diagnostic uses, may be removed in the future. */ - button?: MouseButton; + hasDamage: boolean; /** - * A number indicating which buttons are pressed on the mouse when a mouse event is triggered. -Left=1, Right=2, Middle=4, Back=8, Forward=16, None=0. + * Base64-encoded image data of the screenshot, if one was requested and successfully taken. */ - buttons?: number; + screenshotData?: binary; + } + /** + * Disables headless events for the target. + */ + export type disableParameters = { + } + export type disableReturnValue = { + } + /** + * Enables headless events for the target. + */ + export type enableParameters = { + } + export type enableReturnValue = { + } + } + + /** + * Input/Output operations for streams produced by DevTools. + */ + export module IO { + /** + * This is either obtained from another method or specified as `blob:` where +`` is an UUID of a Blob. + */ + export type StreamHandle = string; + + + /** + * Close the stream, discard any temporary backing storage. + */ + export type closeParameters = { /** - * Number of times the mouse button was clicked (default: 0). + * Handle of the stream to close. */ - clickCount?: number; + handle: StreamHandle; + } + export type closeReturnValue = { + } + /** + * Read a chunk of the stream + */ + export type readParameters = { /** - * The normalized pressure, which has a range of [0,1] (default: 0). + * Handle of the stream to read. */ - force?: number; + handle: StreamHandle; /** - * The normalized tangential pressure, which has a range of [-1,1] (default: 0). + * Seek to the specified offset before reading (if not specified, proceed with offset +following the last read). Some types of streams may only support sequential reads. */ - tangentialPressure?: number; + offset?: number; /** - * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0). + * Maximum number of bytes to read (left upon the agent discretion if not specified). */ - tiltX?: number; + size?: number; + } + export type readReturnValue = { /** - * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). + * Set if the data is base64-encoded */ - tiltY?: number; + base64Encoded?: boolean; /** - * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). + * Data that were read. */ - twist?: number; + data: string; /** - * X delta in CSS pixels for mouse wheel event (default: 0). + * Set if the end-of-file condition occurred while reading. */ - deltaX?: number; + eof: boolean; + } + /** + * Return UUID of Blob object specified by a remote object id. + */ + export type resolveBlobParameters = { /** - * Y delta in CSS pixels for mouse wheel event (default: 0). + * Object id of a Blob object wrapper. */ - deltaY?: number; + objectId: Runtime.RemoteObjectId; + } + export type resolveBlobReturnValue = { /** - * Pointer type (default: "mouse"). + * UUID of the specified Blob. */ - pointerType?: "mouse"|"pen"; - } - export type dispatchMouseEventReturnValue = { + uuid: string; } + } + + export module IndexedDB { /** - * Dispatches a touch event to the page. + * Database with an array of object stores. */ - export type dispatchTouchEventParameters = { + export interface DatabaseWithObjectStores { /** - * Type of the touch event. TouchEnd and TouchCancel must not contain any touch points, while -TouchStart and TouchMove must contains at least one. - */ - type: "touchStart"|"touchEnd"|"touchMove"|"touchCancel"; - /** - * Active touch points on the touch device. One event per any changed point (compared to -previous touch event in a sequence) is generated, emulating pressing/moving/releasing points -one by one. + * Database name. */ - touchPoints: TouchPoint[]; + name: string; /** - * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 -(default: 0). + * Database version (type is not 'integer', as the standard +requires the version number to be 'unsigned long long') */ - modifiers?: number; + version: number; /** - * Time at which the event occurred. + * Object stores in this database. */ - timestamp?: TimeSinceEpoch; - } - export type dispatchTouchEventReturnValue = { + objectStores: ObjectStore[]; } /** - * Cancels any active dragging in the page. + * Object store. */ - export type cancelDraggingParameters = { - } - export type cancelDraggingReturnValue = { + export interface ObjectStore { + /** + * Object store name. + */ + name: string; + /** + * Object store key path. + */ + keyPath: KeyPath; + /** + * If true, object store has auto increment flag set. + */ + autoIncrement: boolean; + /** + * Indexes in this object store. + */ + indexes: ObjectStoreIndex[]; } /** - * Emulates touch event from the mouse event parameters. + * Object store index. */ - export type emulateTouchFromMouseEventParameters = { + export interface ObjectStoreIndex { /** - * Type of the mouse event. + * Index name. */ - type: "mousePressed"|"mouseReleased"|"mouseMoved"|"mouseWheel"; + name: string; /** - * X coordinate of the mouse pointer in DIP. + * Index key path. */ - x: number; + keyPath: KeyPath; /** - * Y coordinate of the mouse pointer in DIP. + * If true, index is unique. */ - y: number; + unique: boolean; /** - * Mouse button. Only "none", "left", "right" are supported. + * If true, index allows multiple entries for a key. */ - button: MouseButton; + multiEntry: boolean; + } + /** + * Key. + */ + export interface Key { /** - * Time at which the event occurred (default: current time). + * Key type. */ - timestamp?: TimeSinceEpoch; + type: "number"|"string"|"date"|"array"; /** - * X delta in DIP for mouse wheel event (default: 0). + * Number value. */ - deltaX?: number; + number?: number; /** - * Y delta in DIP for mouse wheel event (default: 0). + * String value. */ - deltaY?: number; + string?: string; /** - * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 -(default: 0). + * Date value. */ - modifiers?: number; + date?: number; /** - * Number of times the mouse button was clicked (default: 0). + * Array value. */ - clickCount?: number; - } - export type emulateTouchFromMouseEventReturnValue = { + array?: Key[]; } /** - * Ignores input events (useful while auditing page). + * Key range. */ - export type setIgnoreInputEventsParameters = { + export interface KeyRange { /** - * Ignores input events processing when set to true. + * Lower bound. */ - ignore: boolean; - } - export type setIgnoreInputEventsReturnValue = { - } - /** - * Prevents default drag and drop behavior and instead emits `Input.dragIntercepted` events. -Drag and drop behavior can be directly controlled via `Input.dispatchDragEvent`. - */ - export type setInterceptDragsParameters = { - enabled: boolean; - } - export type setInterceptDragsReturnValue = { - } - /** - * Synthesizes a pinch gesture over a time period by issuing appropriate touch events. - */ - export type synthesizePinchGestureParameters = { + lower?: Key; /** - * X coordinate of the start of the gesture in CSS pixels. + * Upper bound. */ - x: number; + upper?: Key; /** - * Y coordinate of the start of the gesture in CSS pixels. + * If true lower bound is open. */ - y: number; + lowerOpen: boolean; /** - * Relative scale factor after zooming (>1.0 zooms in, <1.0 zooms out). + * If true upper bound is open. */ - scaleFactor: number; + upperOpen: boolean; + } + /** + * Data entry. + */ + export interface DataEntry { /** - * Relative pointer speed in pixels per second (default: 800). + * Key object. */ - relativeSpeed?: number; + key: Runtime.RemoteObject; /** - * Which type of input events to be generated (default: 'default', which queries the platform -for the preferred input type). + * Primary key object. */ - gestureSourceType?: GestureSourceType; - } - export type synthesizePinchGestureReturnValue = { + primaryKey: Runtime.RemoteObject; + /** + * Value object. + */ + value: Runtime.RemoteObject; } /** - * Synthesizes a scroll gesture over a time period by issuing appropriate touch events. + * Key path. */ - export type synthesizeScrollGestureParameters = { + export interface KeyPath { /** - * X coordinate of the start of the gesture in CSS pixels. + * Key path type. */ - x: number; + type: "null"|"string"|"array"; /** - * Y coordinate of the start of the gesture in CSS pixels. + * String value. */ - y: number; + string?: string; /** - * The distance to scroll along the X axis (positive to scroll left). + * Array value. */ - xDistance?: number; + array?: string[]; + } + + + /** + * Clears all entries from an object store. + */ + export type clearObjectStoreParameters = { /** - * The distance to scroll along the Y axis (positive to scroll up). + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - yDistance?: number; + securityOrigin?: string; /** - * The number of additional pixels to scroll back along the X axis, in addition to the given -distance. + * Storage key. */ - xOverscroll?: number; + storageKey?: string; /** - * The number of additional pixels to scroll back along the Y axis, in addition to the given -distance. + * Storage bucket. If not specified, it uses the default bucket. */ - yOverscroll?: number; + storageBucket?: Storage.StorageBucket; /** - * Prevent fling (default: true). + * Database name. */ - preventFling?: boolean; + databaseName: string; /** - * Swipe speed in pixels per second (default: 800). + * Object store name. */ - speed?: number; + objectStoreName: string; + } + export type clearObjectStoreReturnValue = { + } + /** + * Deletes a database. + */ + export type deleteDatabaseParameters = { /** - * Which type of input events to be generated (default: 'default', which queries the platform -for the preferred input type). + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - gestureSourceType?: GestureSourceType; + securityOrigin?: string; /** - * The number of times to repeat the gesture (default: 0). + * Storage key. */ - repeatCount?: number; + storageKey?: string; /** - * The number of milliseconds delay between each repeat. (default: 250). + * Storage bucket. If not specified, it uses the default bucket. */ - repeatDelayMs?: number; + storageBucket?: Storage.StorageBucket; /** - * The name of the interaction markers to generate, if not empty (default: ""). + * Database name. */ - interactionMarkerName?: string; + databaseName: string; } - export type synthesizeScrollGestureReturnValue = { + export type deleteDatabaseReturnValue = { } /** - * Synthesizes a tap gesture over a time period by issuing appropriate touch events. + * Delete a range of entries from an object store */ - export type synthesizeTapGestureParameters = { - /** - * X coordinate of the start of the gesture in CSS pixels. - */ - x: number; + export type deleteObjectStoreEntriesParameters = { /** - * Y coordinate of the start of the gesture in CSS pixels. + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - y: number; + securityOrigin?: string; /** - * Duration between touchdown and touchup events in ms (default: 50). + * Storage key. */ - duration?: number; + storageKey?: string; /** - * Number of times to perform the tap (e.g. 2 for double tap, default: 1). + * Storage bucket. If not specified, it uses the default bucket. */ - tapCount?: number; + storageBucket?: Storage.StorageBucket; + databaseName: string; + objectStoreName: string; /** - * Which type of input events to be generated (default: 'default', which queries the platform -for the preferred input type). + * Range of entry keys to delete */ - gestureSourceType?: GestureSourceType; + keyRange: KeyRange; } - export type synthesizeTapGestureReturnValue = { + export type deleteObjectStoreEntriesReturnValue = { } - } - - export module Inspector { - /** - * Fired when remote debugging connection is about to be terminated. Contains detach reason. - */ - export type detachedPayload = { - /** - * The reason why connection has been terminated. - */ - reason: string; - } - /** - * Fired when debugging target has crashed - */ - export type targetCrashedPayload = void; - /** - * Fired when debugging target has reloaded after crash - */ - export type targetReloadedAfterCrashPayload = void; - - /** - * Disables inspector domain notifications. + * Disables events from backend. */ export type disableParameters = { } export type disableReturnValue = { } /** - * Enables inspector domain notifications. + * Enables events from backend. */ export type enableParameters = { } export type enableReturnValue = { } - } - - export module LayerTree { - /** - * Unique Layer identifier. - */ - export type LayerId = string; - /** - * Unique snapshot identifier. - */ - export type SnapshotId = string; /** - * Rectangle where scrolling happens on the main thread. + * Requests data from object store or index. */ - export interface ScrollRect { + export type requestDataParameters = { /** - * Rectangle itself. + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - rect: DOM.Rect; + securityOrigin?: string; /** - * Reason for rectangle to force scrolling on the main thread + * Storage key. */ - type: "RepaintsOnScroll"|"TouchEventHandler"|"WheelEventHandler"; - } - /** - * Sticky position constraints. - */ - export interface StickyPositionConstraint { + storageKey?: string; /** - * Layout rectangle of the sticky element before being shifted + * Storage bucket. If not specified, it uses the default bucket. */ - stickyBoxRect: DOM.Rect; + storageBucket?: Storage.StorageBucket; /** - * Layout rectangle of the containing block of the sticky element + * Database name. */ - containingBlockRect: DOM.Rect; + databaseName: string; /** - * The nearest sticky layer that shifts the sticky box + * Object store name. */ - nearestLayerShiftingStickyBox?: LayerId; + objectStoreName: string; /** - * The nearest sticky layer that shifts the containing block + * Index name, empty string for object store data requests. */ - nearestLayerShiftingContainingBlock?: LayerId; - } - /** - * Serialized fragment of layer picture along with its offset within the layer. - */ - export interface PictureTile { + indexName: string; /** - * Offset from owning layer left boundary + * Number of records to skip. */ - x: number; + skipCount: number; /** - * Offset from owning layer top boundary + * Number of records to fetch. */ - y: number; + pageSize: number; /** - * Base64-encoded snapshot data. + * Key range. */ - picture: binary; + keyRange?: KeyRange; } - /** - * Information about a compositing layer. - */ - export interface Layer { - /** - * The unique id for this layer. - */ - layerId: LayerId; - /** - * The id of parent (not present for root). - */ - parentLayerId?: LayerId; + export type requestDataReturnValue = { /** - * The backend id for the node associated with this layer. + * Array of object store data entries. */ - backendNodeId?: DOM.BackendNodeId; + objectStoreDataEntries: DataEntry[]; /** - * Offset from parent layer, X coordinate. + * If true, there are more entries to fetch in the given range. */ - offsetX: number; + hasMore: boolean; + } + /** + * Gets metadata of an object store. + */ + export type getMetadataParameters = { /** - * Offset from parent layer, Y coordinate. + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - offsetY: number; + securityOrigin?: string; /** - * Layer width. + * Storage key. */ - width: number; + storageKey?: string; /** - * Layer height. + * Storage bucket. If not specified, it uses the default bucket. */ - height: number; + storageBucket?: Storage.StorageBucket; /** - * Transformation matrix for layer, default is identity matrix + * Database name. */ - transform?: number[]; + databaseName: string; /** - * Transform anchor point X, absent if no transform specified + * Object store name. */ - anchorX?: number; + objectStoreName: string; + } + export type getMetadataReturnValue = { /** - * Transform anchor point Y, absent if no transform specified + * the entries count */ - anchorY?: number; + entriesCount: number; /** - * Transform anchor point Z, absent if no transform specified + * the current value of key generator, to become the next inserted +key into the object store. Valid if objectStore.autoIncrement +is true. */ - anchorZ?: number; + keyGeneratorValue: number; + } + /** + * Requests database with given name in given frame. + */ + export type requestDatabaseParameters = { /** - * Indicates how many time this layer has painted. + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - paintCount: number; + securityOrigin?: string; /** - * Indicates whether this layer hosts any content, rather than being used for -transform/scrolling purposes only. + * Storage key. */ - drawsContent: boolean; + storageKey?: string; /** - * Set if layer is not visible. + * Storage bucket. If not specified, it uses the default bucket. */ - invisible?: boolean; + storageBucket?: Storage.StorageBucket; /** - * Rectangles scrolling on main thread only. + * Database name. */ - scrollRects?: ScrollRect[]; + databaseName: string; + } + export type requestDatabaseReturnValue = { /** - * Sticky position constraint information + * Database with an array of object stores. */ - stickyPositionConstraint?: StickyPositionConstraint; + databaseWithObjectStores: DatabaseWithObjectStores; } /** - * Array of timings, one per paint step. + * Requests database names for given security origin. */ - export type PaintProfile = number[]; - - export type layerPaintedPayload = { + export type requestDatabaseNamesParameters = { /** - * The id of the painted layer. + * At least and at most one of securityOrigin, storageKey, or storageBucket must be specified. +Security origin. */ - layerId: LayerId; + securityOrigin?: string; /** - * Clip rectangle. + * Storage key. */ - clip: DOM.Rect; - } - export type layerTreeDidChangePayload = { + storageKey?: string; /** - * Layer tree, absent if not in the compositing mode. + * Storage bucket. If not specified, it uses the default bucket. */ - layers?: Layer[]; + storageBucket?: Storage.StorageBucket; } - - /** - * Provides the reasons why the given layer was composited. - */ - export type compositingReasonsParameters = { + export type requestDatabaseNamesReturnValue = { /** - * The id of the layer for which we want to get the reasons it was composited. + * Database names for origin. */ - layerId: LayerId; + databaseNames: string[]; } - export type compositingReasonsReturnValue = { + } + + export module Input { + export interface TouchPoint { /** - * A list of strings specifying reasons for the given layer to become composited. + * X coordinate of the event relative to the main frame's viewport in CSS pixels. */ - compositingReasons: string[]; + x: number; /** - * A list of strings specifying reason IDs for the given layer to become composited. + * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to +the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. */ - compositingReasonIds: string[]; - } - /** - * Disables compositing tree inspection. - */ - export type disableParameters = { - } - export type disableReturnValue = { - } - /** - * Enables compositing tree inspection. - */ - export type enableParameters = { - } - export type enableReturnValue = { - } - /** - * Returns the snapshot identifier. - */ - export type loadSnapshotParameters = { + y: number; /** - * An array of tiles composing the snapshot. + * X radius of the touch area (default: 1.0). */ - tiles: PictureTile[]; - } - export type loadSnapshotReturnValue = { + radiusX?: number; /** - * The id of the snapshot. + * Y radius of the touch area (default: 1.0). */ - snapshotId: SnapshotId; - } - /** - * Returns the layer snapshot identifier. - */ - export type makeSnapshotParameters = { + radiusY?: number; /** - * The id of the layer. + * Rotation angle (default: 0.0). */ - layerId: LayerId; - } - export type makeSnapshotReturnValue = { + rotationAngle?: number; /** - * The id of the layer snapshot. + * Force (default: 1.0). */ - snapshotId: SnapshotId; - } - export type profileSnapshotParameters = { + force?: number; /** - * The id of the layer snapshot. + * The normalized tangential pressure, which has a range of [-1,1] (default: 0). */ - snapshotId: SnapshotId; + tangentialPressure?: number; /** - * The maximum number of times to replay the snapshot (1, if not specified). + * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0) */ - minRepeatCount?: number; + tiltX?: number; /** - * The minimum duration (in seconds) to replay the snapshot. + * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). */ - minDuration?: number; + tiltY?: number; /** - * The clip rectangle to apply when replaying the snapshot. + * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). */ - clipRect?: DOM.Rect; - } - export type profileSnapshotReturnValue = { + twist?: number; /** - * The array of paint profiles, one per run. + * Identifier used to track touch sources between events, must be unique within an event. */ - timings: PaintProfile[]; + id?: number; } + export type GestureSourceType = "default"|"touch"|"mouse"; + export type MouseButton = "none"|"left"|"middle"|"right"|"back"|"forward"; /** - * Releases layer snapshot captured by the back-end. + * UTC time in seconds, counted from January 1, 1970. */ - export type releaseSnapshotParameters = { + export type TimeSinceEpoch = number; + export interface DragDataItem { /** - * The id of the layer snapshot. + * Mime type of the dragged data. */ - snapshotId: SnapshotId; - } - export type releaseSnapshotReturnValue = { - } - /** - * Replays the layer snapshot and returns the resulting bitmap. - */ - export type replaySnapshotParameters = { + mimeType: string; /** - * The id of the layer snapshot. + * Depending of the value of `mimeType`, it contains the dragged link, +text, HTML markup or any other data. */ - snapshotId: SnapshotId; + data: string; /** - * The first step to replay from (replay from the very start if not specified). + * Title associated with a link. Only valid when `mimeType` == "text/uri-list". */ - fromStep?: number; + title?: string; /** - * The last step to replay to (replay till the end if not specified). + * Stores the base URL for the contained markup. Only valid when `mimeType` +== "text/html". */ - toStep?: number; + baseURL?: string; + } + export interface DragData { + items: DragDataItem[]; /** - * The scale to apply while replaying (defaults to 1). + * List of filenames that should be included when dropping */ - scale?: number; - } - export type replaySnapshotReturnValue = { + files?: string[]; /** - * A data: URL for resulting image. + * Bit field representing allowed drag operations. Copy = 1, Link = 2, Move = 16 */ - dataURL: string; + dragOperationsMask: number; } + /** - * Replays the layer snapshot and returns canvas log. + * Emitted only when `Input.setInterceptDrags` is enabled. Use this data with `Input.dispatchDragEvent` to +restore normal drag and drop behavior. */ - export type snapshotCommandLogParameters = { + export type dragInterceptedPayload = { + data: DragData; + } + + /** + * Dispatches a drag event into the page. + */ + export type dispatchDragEventParameters = { /** - * The id of the layer snapshot. + * Type of the drag event. */ - snapshotId: SnapshotId; - } - export type snapshotCommandLogReturnValue = { + type: "dragEnter"|"dragOver"|"drop"|"dragCancel"; /** - * The array of canvas function calls. + * X coordinate of the event relative to the main frame's viewport in CSS pixels. */ - commandLog: { [key: string]: string }[]; + x: number; + /** + * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to +the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. + */ + y: number; + data: DragData; + /** + * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 +(default: 0). + */ + modifiers?: number; + } + export type dispatchDragEventReturnValue = { } - } - - /** - * Provides access to log entries. - */ - export module Log { /** - * Log entry. + * Dispatches a key event to the page. */ - export interface LogEntry { + export type dispatchKeyEventParameters = { /** - * Log entry source. + * Type of the key event. */ - source: "xml"|"javascript"|"network"|"storage"|"appcache"|"rendering"|"security"|"deprecation"|"worker"|"violation"|"intervention"|"recommendation"|"other"; + type: "keyDown"|"keyUp"|"rawKeyDown"|"char"; /** - * Log entry severity. + * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 +(default: 0). */ - level: "verbose"|"info"|"warning"|"error"; + modifiers?: number; /** - * Logged text. + * Time at which the event occurred. */ - text: string; - category?: "cors"; + timestamp?: TimeSinceEpoch; /** - * Timestamp when this entry was added. + * Text as generated by processing a virtual key code with a keyboard layout. Not needed for +for `keyUp` and `rawKeyDown` events (default: "") */ - timestamp: Runtime.Timestamp; + text?: string; /** - * URL of the resource if known. + * Text that would have been generated by the keyboard if no modifiers were pressed (except for +shift). Useful for shortcut (accelerator) key handling (default: ""). */ - url?: string; + unmodifiedText?: string; /** - * Line number in the resource. + * Unique key identifier (e.g., 'U+0041') (default: ""). */ - lineNumber?: number; + keyIdentifier?: string; /** - * JavaScript stack trace. + * Unique DOM defined string value for each physical key (e.g., 'KeyA') (default: ""). */ - stackTrace?: Runtime.StackTrace; + code?: string; /** - * Identifier of the network request associated with this entry. + * Unique DOM defined string value describing the meaning of the key in the context of active +modifiers, keyboard layout, etc (e.g., 'AltGr') (default: ""). */ - networkRequestId?: Network.RequestId; + key?: string; /** - * Identifier of the worker associated with this entry. + * Windows virtual key code (default: 0). */ - workerId?: string; + windowsVirtualKeyCode?: number; /** - * Call arguments. + * Native virtual key code (default: 0). */ - args?: Runtime.RemoteObject[]; - } - /** - * Violation configuration setting. - */ - export interface ViolationSetting { + nativeVirtualKeyCode?: number; /** - * Violation type. + * Whether the event was generated from auto repeat (default: false). */ - name: "longTask"|"longLayout"|"blockedEvent"|"blockedParser"|"discouragedAPIUse"|"handler"|"recurringHandler"; + autoRepeat?: boolean; /** - * Time threshold to trigger upon. + * Whether the event was generated from the keypad (default: false). */ - threshold: number; - } - - /** - * Issued when new message was logged. - */ - export type entryAddedPayload = { + isKeypad?: boolean; /** - * The entry. + * Whether the event was a system key event (default: false). */ - entry: LogEntry; - } - - /** - * Clears the log. - */ - export type clearParameters = { - } - export type clearReturnValue = { - } - /** - * Disables log domain, prevents further log entries from being reported to the client. - */ - export type disableParameters = { + isSystemKey?: boolean; + /** + * Whether the event was from the left or right side of the keyboard. 1=Left, 2=Right (default: +0). + */ + location?: number; + /** + * Editing commands to send with the key event (e.g., 'selectAll') (default: []). +These are related to but not equal the command names used in `document.execCommand` and NSStandardKeyBindingResponding. +See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/editing/commands/editor_command_names.h for valid command names. + */ + commands?: string[]; } - export type disableReturnValue = { + export type dispatchKeyEventReturnValue = { } /** - * Enables log domain, sends the entries collected so far to the client by means of the -`entryAdded` notification. + * This method emulates inserting text that doesn't come from a key press, +for example an emoji keyboard or an IME. */ - export type enableParameters = { + export type insertTextParameters = { + /** + * The text to insert. + */ + text: string; } - export type enableReturnValue = { + export type insertTextReturnValue = { } /** - * start violation reporting. + * This method sets the current candidate text for IME. +Use imeCommitComposition to commit the final text. +Use imeSetComposition with empty string as text to cancel composition. */ - export type startViolationsReportParameters = { + export type imeSetCompositionParameters = { /** - * Configuration for violations. + * The text to insert */ - config: ViolationSetting[]; + text: string; + /** + * selection start + */ + selectionStart: number; + /** + * selection end + */ + selectionEnd: number; + /** + * replacement start + */ + replacementStart?: number; + /** + * replacement end + */ + replacementEnd?: number; } - export type startViolationsReportReturnValue = { + export type imeSetCompositionReturnValue = { } /** - * Stop violation reporting. + * Dispatches a mouse event to the page. */ - export type stopViolationsReportParameters = { - } - export type stopViolationsReportReturnValue = { - } - } - - export module Memory { - /** - * Memory pressure level. - */ - export type PressureLevel = "moderate"|"critical"; - /** - * Heap profile sample. - */ - export interface SamplingProfileNode { + export type dispatchMouseEventParameters = { /** - * Size of the sampled allocation. + * Type of the mouse event. */ - size: number; + type: "mousePressed"|"mouseReleased"|"mouseMoved"|"mouseWheel"; /** - * Total bytes attributed to this sample. + * X coordinate of the event relative to the main frame's viewport in CSS pixels. */ - total: number; + x: number; /** - * Execution stack at the point of allocation. + * Y coordinate of the event relative to the main frame's viewport in CSS pixels. 0 refers to +the top of the viewport and Y increases as it proceeds towards the bottom of the viewport. */ - stack: string[]; - } - /** - * Array of heap profile samples. - */ - export interface SamplingProfile { - samples: SamplingProfileNode[]; - modules: Module[]; - } - /** - * Executable module information - */ - export interface Module { + y: number; /** - * Name of the module. + * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 +(default: 0). */ - name: string; + modifiers?: number; /** - * UUID of the module. + * Time at which the event occurred. */ - uuid: string; + timestamp?: TimeSinceEpoch; /** - * Base address where the module is loaded into memory. Encoded as a decimal -or hexadecimal (0x prefixed) string. + * Mouse button (default: "none"). */ - baseAddress: string; + button?: MouseButton; /** - * Size of the module in bytes. + * A number indicating which buttons are pressed on the mouse when a mouse event is triggered. +Left=1, Right=2, Middle=4, Back=8, Forward=16, None=0. */ - size: number; - } - /** - * DOM object counter data. - */ - export interface DOMCounter { + buttons?: number; /** - * Object name. Note: object names should be presumed volatile and clients should not expect -the returned names to be consistent across runs. + * Number of times the mouse button was clicked (default: 0). */ - name: string; + clickCount?: number; /** - * Object count. + * The normalized pressure, which has a range of [0,1] (default: 0). */ - count: number; - } - - - /** - * Retruns current DOM object counters. - */ - export type getDOMCountersParameters = { + force?: number; + /** + * The normalized tangential pressure, which has a range of [-1,1] (default: 0). + */ + tangentialPressure?: number; + /** + * The plane angle between the Y-Z plane and the plane containing both the stylus axis and the Y axis, in degrees of the range [-90,90], a positive tiltX is to the right (default: 0). + */ + tiltX?: number; + /** + * The plane angle between the X-Z plane and the plane containing both the stylus axis and the X axis, in degrees of the range [-90,90], a positive tiltY is towards the user (default: 0). + */ + tiltY?: number; + /** + * The clockwise rotation of a pen stylus around its own major axis, in degrees in the range [0,359] (default: 0). + */ + twist?: number; + /** + * X delta in CSS pixels for mouse wheel event (default: 0). + */ + deltaX?: number; + /** + * Y delta in CSS pixels for mouse wheel event (default: 0). + */ + deltaY?: number; + /** + * Pointer type (default: "mouse"). + */ + pointerType?: "mouse"|"pen"; } - export type getDOMCountersReturnValue = { - documents: number; - nodes: number; - jsEventListeners: number; + export type dispatchMouseEventReturnValue = { } /** - * Retruns DOM object counters after preparing renderer for leak detection. + * Dispatches a touch event to the page. */ - export type getDOMCountersForLeakDetectionParameters = { - } - export type getDOMCountersForLeakDetectionReturnValue = { + export type dispatchTouchEventParameters = { /** - * DOM object counters. + * Type of the touch event. TouchEnd and TouchCancel must not contain any touch points, while +TouchStart and TouchMove must contains at least one. */ - counters: DOMCounter[]; - } - /** - * Prepares for leak detection by terminating workers, stopping spellcheckers, -dropping non-essential internal caches, running garbage collections, etc. - */ - export type prepareForLeakDetectionParameters = { + type: "touchStart"|"touchEnd"|"touchMove"|"touchCancel"; + /** + * Active touch points on the touch device. One event per any changed point (compared to +previous touch event in a sequence) is generated, emulating pressing/moving/releasing points +one by one. + */ + touchPoints: TouchPoint[]; + /** + * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 +(default: 0). + */ + modifiers?: number; + /** + * Time at which the event occurred. + */ + timestamp?: TimeSinceEpoch; } - export type prepareForLeakDetectionReturnValue = { + export type dispatchTouchEventReturnValue = { } /** - * Simulate OomIntervention by purging V8 memory. + * Cancels any active dragging in the page. */ - export type forciblyPurgeJavaScriptMemoryParameters = { + export type cancelDraggingParameters = { } - export type forciblyPurgeJavaScriptMemoryReturnValue = { + export type cancelDraggingReturnValue = { } /** - * Enable/disable suppressing memory pressure notifications in all processes. + * Emulates touch event from the mouse event parameters. */ - export type setPressureNotificationsSuppressedParameters = { + export type emulateTouchFromMouseEventParameters = { /** - * If true, memory pressure notifications will be suppressed. + * Type of the mouse event. */ - suppressed: boolean; - } - export type setPressureNotificationsSuppressedReturnValue = { - } - /** - * Simulate a memory pressure notification in all processes. - */ - export type simulatePressureNotificationParameters = { + type: "mousePressed"|"mouseReleased"|"mouseMoved"|"mouseWheel"; /** - * Memory pressure level of the notification. + * X coordinate of the mouse pointer in DIP. */ - level: PressureLevel; + x: number; + /** + * Y coordinate of the mouse pointer in DIP. + */ + y: number; + /** + * Mouse button. Only "none", "left", "right" are supported. + */ + button: MouseButton; + /** + * Time at which the event occurred (default: current time). + */ + timestamp?: TimeSinceEpoch; + /** + * X delta in DIP for mouse wheel event (default: 0). + */ + deltaX?: number; + /** + * Y delta in DIP for mouse wheel event (default: 0). + */ + deltaY?: number; + /** + * Bit field representing pressed modifier keys. Alt=1, Ctrl=2, Meta/Command=4, Shift=8 +(default: 0). + */ + modifiers?: number; + /** + * Number of times the mouse button was clicked (default: 0). + */ + clickCount?: number; } - export type simulatePressureNotificationReturnValue = { + export type emulateTouchFromMouseEventReturnValue = { } /** - * Start collecting native memory profile. + * Ignores input events (useful while auditing page). */ - export type startSamplingParameters = { - /** - * Average number of bytes between samples. - */ - samplingInterval?: number; + export type setIgnoreInputEventsParameters = { /** - * Do not randomize intervals between samples. + * Ignores input events processing when set to true. */ - suppressRandomness?: boolean; + ignore: boolean; } - export type startSamplingReturnValue = { + export type setIgnoreInputEventsReturnValue = { } /** - * Stop collecting native memory profile. + * Prevents default drag and drop behavior and instead emits `Input.dragIntercepted` events. +Drag and drop behavior can be directly controlled via `Input.dispatchDragEvent`. */ - export type stopSamplingParameters = { + export type setInterceptDragsParameters = { + enabled: boolean; } - export type stopSamplingReturnValue = { + export type setInterceptDragsReturnValue = { } /** - * Retrieve native memory allocations profile -collected since renderer process startup. + * Synthesizes a pinch gesture over a time period by issuing appropriate touch events. */ - export type getAllTimeSamplingProfileParameters = { + export type synthesizePinchGestureParameters = { + /** + * X coordinate of the start of the gesture in CSS pixels. + */ + x: number; + /** + * Y coordinate of the start of the gesture in CSS pixels. + */ + y: number; + /** + * Relative scale factor after zooming (>1.0 zooms in, <1.0 zooms out). + */ + scaleFactor: number; + /** + * Relative pointer speed in pixels per second (default: 800). + */ + relativeSpeed?: number; + /** + * Which type of input events to be generated (default: 'default', which queries the platform +for the preferred input type). + */ + gestureSourceType?: GestureSourceType; } - export type getAllTimeSamplingProfileReturnValue = { - profile: SamplingProfile; + export type synthesizePinchGestureReturnValue = { } /** - * Retrieve native memory allocations profile -collected since browser process startup. + * Synthesizes a scroll gesture over a time period by issuing appropriate touch events. */ - export type getBrowserSamplingProfileParameters = { + export type synthesizeScrollGestureParameters = { + /** + * X coordinate of the start of the gesture in CSS pixels. + */ + x: number; + /** + * Y coordinate of the start of the gesture in CSS pixels. + */ + y: number; + /** + * The distance to scroll along the X axis (positive to scroll left). + */ + xDistance?: number; + /** + * The distance to scroll along the Y axis (positive to scroll up). + */ + yDistance?: number; + /** + * The number of additional pixels to scroll back along the X axis, in addition to the given +distance. + */ + xOverscroll?: number; + /** + * The number of additional pixels to scroll back along the Y axis, in addition to the given +distance. + */ + yOverscroll?: number; + /** + * Prevent fling (default: true). + */ + preventFling?: boolean; + /** + * Swipe speed in pixels per second (default: 800). + */ + speed?: number; + /** + * Which type of input events to be generated (default: 'default', which queries the platform +for the preferred input type). + */ + gestureSourceType?: GestureSourceType; + /** + * The number of times to repeat the gesture (default: 0). + */ + repeatCount?: number; + /** + * The number of milliseconds delay between each repeat. (default: 250). + */ + repeatDelayMs?: number; + /** + * The name of the interaction markers to generate, if not empty (default: ""). + */ + interactionMarkerName?: string; } - export type getBrowserSamplingProfileReturnValue = { - profile: SamplingProfile; + export type synthesizeScrollGestureReturnValue = { } /** - * Retrieve native memory allocations profile collected since last -`startSampling` call. + * Synthesizes a tap gesture over a time period by issuing appropriate touch events. */ - export type getSamplingProfileParameters = { + export type synthesizeTapGestureParameters = { + /** + * X coordinate of the start of the gesture in CSS pixels. + */ + x: number; + /** + * Y coordinate of the start of the gesture in CSS pixels. + */ + y: number; + /** + * Duration between touchdown and touchup events in ms (default: 50). + */ + duration?: number; + /** + * Number of times to perform the tap (e.g. 2 for double tap, default: 1). + */ + tapCount?: number; + /** + * Which type of input events to be generated (default: 'default', which queries the platform +for the preferred input type). + */ + gestureSourceType?: GestureSourceType; } - export type getSamplingProfileReturnValue = { - profile: SamplingProfile; + export type synthesizeTapGestureReturnValue = { } } - /** - * Network domain allows tracking network activities of the page. It exposes information about http, -file, data and other requests and responses, their headers, bodies, timing, etc. - */ - export module Network { - /** - * Resource type as it was perceived by the rendering engine. - */ - export type ResourceType = "Document"|"Stylesheet"|"Image"|"Media"|"Font"|"Script"|"TextTrack"|"XHR"|"Fetch"|"Prefetch"|"EventSource"|"WebSocket"|"Manifest"|"SignedExchange"|"Ping"|"CSPViolationReport"|"Preflight"|"FedCM"|"Other"; - /** - * Unique loader identifier. - */ - export type LoaderId = string; - /** - * Unique network request identifier. -Note that this does not identify individual HTTP requests that are part of -a network request. - */ - export type RequestId = string; - /** - * Unique intercepted request identifier. - */ - export type InterceptionId = string; - /** - * Network level fetch failure reason. - */ - export type ErrorReason = "Failed"|"Aborted"|"TimedOut"|"AccessDenied"|"ConnectionClosed"|"ConnectionReset"|"ConnectionRefused"|"ConnectionAborted"|"ConnectionFailed"|"NameNotResolved"|"InternetDisconnected"|"AddressUnreachable"|"BlockedByClient"|"BlockedByResponse"; + export module Inspector { + /** - * UTC time in seconds, counted from January 1, 1970. + * Fired when remote debugging connection is about to be terminated. Contains detach reason. */ - export type TimeSinceEpoch = number; + export type detachedPayload = { + /** + * The reason why connection has been terminated. + */ + reason: string; + } /** - * Monotonically increasing time in seconds since an arbitrary point in the past. + * Fired when debugging target has crashed */ - export type MonotonicTime = number; + export type targetCrashedPayload = void; /** - * Request / response headers as keys / values of JSON object. + * Fired when debugging target has reloaded after crash */ - export type Headers = { [key: string]: string }; + export type targetReloadedAfterCrashPayload = void; + /** - * The underlying connection technology that the browser is supposedly using. + * Disables inspector domain notifications. */ - export type ConnectionType = "none"|"cellular2g"|"cellular3g"|"cellular4g"|"bluetooth"|"ethernet"|"wifi"|"wimax"|"other"; + export type disableParameters = { + } + export type disableReturnValue = { + } /** - * Represents the cookie's 'SameSite' status: -https://tools.ietf.org/html/draft-west-first-party-cookies + * Enables inspector domain notifications. */ - export type CookieSameSite = "Strict"|"Lax"|"None"; + export type enableParameters = { + } + export type enableReturnValue = { + } + } + + export module LayerTree { /** - * Represents the cookie's 'Priority' status: -https://tools.ietf.org/html/draft-west-cookie-priority-00 + * Unique Layer identifier. */ - export type CookiePriority = "Low"|"Medium"|"High"; + export type LayerId = string; /** - * Represents the source scheme of the origin that originally set the cookie. -A value of "Unset" allows protocol clients to emulate legacy cookie scope for the scheme. -This is a temporary ability and it will be removed in the future. + * Unique snapshot identifier. */ - export type CookieSourceScheme = "Unset"|"NonSecure"|"Secure"; + export type SnapshotId = string; /** - * Timing information for the request. + * Rectangle where scrolling happens on the main thread. */ - export interface ResourceTiming { + export interface ScrollRect { /** - * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in -milliseconds relatively to this requestTime. + * Rectangle itself. */ - requestTime: number; + rect: DOM.Rect; /** - * Started resolving proxy. + * Reason for rectangle to force scrolling on the main thread */ - proxyStart: number; + type: "RepaintsOnScroll"|"TouchEventHandler"|"WheelEventHandler"; + } + /** + * Sticky position constraints. + */ + export interface StickyPositionConstraint { /** - * Finished resolving proxy. + * Layout rectangle of the sticky element before being shifted */ - proxyEnd: number; + stickyBoxRect: DOM.Rect; /** - * Started DNS address resolve. + * Layout rectangle of the containing block of the sticky element */ - dnsStart: number; + containingBlockRect: DOM.Rect; /** - * Finished DNS address resolve. + * The nearest sticky layer that shifts the sticky box */ - dnsEnd: number; + nearestLayerShiftingStickyBox?: LayerId; /** - * Started connecting to the remote host. + * The nearest sticky layer that shifts the containing block */ - connectStart: number; + nearestLayerShiftingContainingBlock?: LayerId; + } + /** + * Serialized fragment of layer picture along with its offset within the layer. + */ + export interface PictureTile { /** - * Connected to the remote host. + * Offset from owning layer left boundary */ - connectEnd: number; + x: number; /** - * Started SSL handshake. + * Offset from owning layer top boundary */ - sslStart: number; + y: number; /** - * Finished SSL handshake. + * Base64-encoded snapshot data. */ - sslEnd: number; + picture: binary; + } + /** + * Information about a compositing layer. + */ + export interface Layer { /** - * Started running ServiceWorker. + * The unique id for this layer. */ - workerStart: number; + layerId: LayerId; /** - * Finished Starting ServiceWorker. + * The id of parent (not present for root). */ - workerReady: number; + parentLayerId?: LayerId; /** - * Started fetch event. + * The backend id for the node associated with this layer. */ - workerFetchStart: number; + backendNodeId?: DOM.BackendNodeId; /** - * Settled fetch event respondWith promise. + * Offset from parent layer, X coordinate. */ - workerRespondWithSettled: number; + offsetX: number; /** - * Started ServiceWorker static routing source evaluation. + * Offset from parent layer, Y coordinate. */ - workerRouterEvaluationStart?: number; + offsetY: number; /** - * Started cache lookup when the source was evaluated to `cache`. + * Layer width. */ - workerCacheLookupStart?: number; + width: number; /** - * Started sending request. + * Layer height. */ - sendStart: number; + height: number; /** - * Finished sending request. + * Transformation matrix for layer, default is identity matrix */ - sendEnd: number; + transform?: number[]; /** - * Time the server started pushing request. + * Transform anchor point X, absent if no transform specified */ - pushStart: number; + anchorX?: number; /** - * Time the server finished pushing request. + * Transform anchor point Y, absent if no transform specified */ - pushEnd: number; - /** - * Started receiving response headers. - */ - receiveHeadersStart: number; + anchorY?: number; /** - * Finished receiving response headers. + * Transform anchor point Z, absent if no transform specified */ - receiveHeadersEnd: number; - } - /** - * Loading priority of a resource request. - */ - export type ResourcePriority = "VeryLow"|"Low"|"Medium"|"High"|"VeryHigh"; - /** - * Post data entry for HTTP request - */ - export interface PostDataEntry { - bytes?: binary; - } - /** - * HTTP request data. - */ - export interface Request { + anchorZ?: number; /** - * Request URL (without fragment). + * Indicates how many time this layer has painted. */ - url: string; + paintCount: number; /** - * Fragment of the requested URL starting with hash, if present. + * Indicates whether this layer hosts any content, rather than being used for +transform/scrolling purposes only. */ - urlFragment?: string; + drawsContent: boolean; /** - * HTTP request method. + * Set if layer is not visible. */ - method: string; + invisible?: boolean; /** - * HTTP request headers. + * Rectangles scrolling on main thread only. */ - headers: Headers; + scrollRects?: ScrollRect[]; /** - * HTTP POST request data. -Use postDataEntries instead. + * Sticky position constraint information */ - postData?: string; + stickyPositionConstraint?: StickyPositionConstraint; + } + /** + * Array of timings, one per paint step. + */ + export type PaintProfile = number[]; + + export type layerPaintedPayload = { /** - * True when the request has POST data. Note that postData might still be omitted when this flag is true when the data is too long. + * The id of the painted layer. */ - hasPostData?: boolean; + layerId: LayerId; /** - * Request body elements (post data broken into individual entries). + * Clip rectangle. */ - postDataEntries?: PostDataEntry[]; + clip: DOM.Rect; + } + export type layerTreeDidChangePayload = { /** - * The mixed content type of the request. + * Layer tree, absent if not in the compositing mode. */ - mixedContentType?: Security.MixedContentType; + layers?: Layer[]; + } + + /** + * Provides the reasons why the given layer was composited. + */ + export type compositingReasonsParameters = { /** - * Priority of the resource request at the time request is sent. + * The id of the layer for which we want to get the reasons it was composited. */ - initialPriority: ResourcePriority; + layerId: LayerId; + } + export type compositingReasonsReturnValue = { /** - * The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/ + * A list of strings specifying reasons for the given layer to become composited. */ - referrerPolicy: "unsafe-url"|"no-referrer-when-downgrade"|"no-referrer"|"origin"|"origin-when-cross-origin"|"same-origin"|"strict-origin"|"strict-origin-when-cross-origin"; + compositingReasons: string[]; /** - * Whether is loaded via link preload. + * A list of strings specifying reason IDs for the given layer to become composited. */ - isLinkPreload?: boolean; + compositingReasonIds: string[]; + } + /** + * Disables compositing tree inspection. + */ + export type disableParameters = { + } + export type disableReturnValue = { + } + /** + * Enables compositing tree inspection. + */ + export type enableParameters = { + } + export type enableReturnValue = { + } + /** + * Returns the snapshot identifier. + */ + export type loadSnapshotParameters = { /** - * Set for requests when the TrustToken API is used. Contains the parameters -passed by the developer (e.g. via "fetch") as understood by the backend. + * An array of tiles composing the snapshot. */ - trustTokenParams?: TrustTokenParams; + tiles: PictureTile[]; + } + export type loadSnapshotReturnValue = { /** - * True if this resource request is considered to be the 'same site' as the -request corresponding to the main frame. + * The id of the snapshot. */ - isSameSite?: boolean; + snapshotId: SnapshotId; } /** - * Details of a signed certificate timestamp (SCT). + * Returns the layer snapshot identifier. */ - export interface SignedCertificateTimestamp { + export type makeSnapshotParameters = { /** - * Validation status. + * The id of the layer. */ - status: string; + layerId: LayerId; + } + export type makeSnapshotReturnValue = { /** - * Origin. + * The id of the layer snapshot. */ - origin: string; + snapshotId: SnapshotId; + } + export type profileSnapshotParameters = { /** - * Log name / description. + * The id of the layer snapshot. */ - logDescription: string; + snapshotId: SnapshotId; /** - * Log ID. + * The maximum number of times to replay the snapshot (1, if not specified). */ - logId: string; + minRepeatCount?: number; /** - * Issuance date. Unlike TimeSinceEpoch, this contains the number of -milliseconds since January 1, 1970, UTC, not the number of seconds. + * The minimum duration (in seconds) to replay the snapshot. */ - timestamp: number; + minDuration?: number; /** - * Hash algorithm. + * The clip rectangle to apply when replaying the snapshot. */ - hashAlgorithm: string; + clipRect?: DOM.Rect; + } + export type profileSnapshotReturnValue = { /** - * Signature algorithm. + * The array of paint profiles, one per run. */ - signatureAlgorithm: string; + timings: PaintProfile[]; + } + /** + * Releases layer snapshot captured by the back-end. + */ + export type releaseSnapshotParameters = { /** - * Signature data. + * The id of the layer snapshot. */ - signatureData: string; + snapshotId: SnapshotId; + } + export type releaseSnapshotReturnValue = { } /** - * Security details about a request. + * Replays the layer snapshot and returns the resulting bitmap. */ - export interface SecurityDetails { + export type replaySnapshotParameters = { /** - * Protocol name (e.g. "TLS 1.2" or "QUIC"). + * The id of the layer snapshot. */ - protocol: string; + snapshotId: SnapshotId; /** - * Key Exchange used by the connection, or the empty string if not applicable. + * The first step to replay from (replay from the very start if not specified). */ - keyExchange: string; + fromStep?: number; /** - * (EC)DH group used by the connection, if applicable. + * The last step to replay to (replay till the end if not specified). */ - keyExchangeGroup?: string; + toStep?: number; /** - * Cipher name. + * The scale to apply while replaying (defaults to 1). */ - cipher: string; + scale?: number; + } + export type replaySnapshotReturnValue = { /** - * TLS MAC. Note that AEAD ciphers do not have separate MACs. + * A data: URL for resulting image. */ - mac?: string; + dataURL: string; + } + /** + * Replays the layer snapshot and returns canvas log. + */ + export type snapshotCommandLogParameters = { /** - * Certificate ID value. + * The id of the layer snapshot. */ - certificateId: Security.CertificateId; + snapshotId: SnapshotId; + } + export type snapshotCommandLogReturnValue = { /** - * Certificate subject name. + * The array of canvas function calls. */ - subjectName: string; + commandLog: { [key: string]: string }[]; + } + } + + /** + * Provides access to log entries. + */ + export module Log { + /** + * Log entry. + */ + export interface LogEntry { /** - * Subject Alternative Name (SAN) DNS names and IP addresses. + * Log entry source. */ - sanList: string[]; + source: "xml"|"javascript"|"network"|"storage"|"appcache"|"rendering"|"security"|"deprecation"|"worker"|"violation"|"intervention"|"recommendation"|"other"; /** - * Name of the issuing CA. + * Log entry severity. */ - issuer: string; + level: "verbose"|"info"|"warning"|"error"; /** - * Certificate valid from date. + * Logged text. */ - validFrom: TimeSinceEpoch; + text: string; + category?: "cors"; /** - * Certificate valid to (expiration) date + * Timestamp when this entry was added. */ - validTo: TimeSinceEpoch; + timestamp: Runtime.Timestamp; /** - * List of signed certificate timestamps (SCTs). + * URL of the resource if known. */ - signedCertificateTimestampList: SignedCertificateTimestamp[]; + url?: string; /** - * Whether the request complied with Certificate Transparency policy + * Line number in the resource. */ - certificateTransparencyCompliance: CertificateTransparencyCompliance; + lineNumber?: number; /** - * The signature algorithm used by the server in the TLS server signature, -represented as a TLS SignatureScheme code point. Omitted if not -applicable or not known. + * JavaScript stack trace. */ - serverSignatureAlgorithm?: number; + stackTrace?: Runtime.StackTrace; /** - * Whether the connection used Encrypted ClientHello + * Identifier of the network request associated with this entry. */ - encryptedClientHello: boolean; - } - /** - * Whether the request complied with Certificate Transparency policy. + networkRequestId?: Network.RequestId; + /** + * Identifier of the worker associated with this entry. + */ + workerId?: string; + /** + * Call arguments. + */ + args?: Runtime.RemoteObject[]; + } + /** + * Violation configuration setting. */ - export type CertificateTransparencyCompliance = "unknown"|"not-compliant"|"compliant"; + export interface ViolationSetting { + /** + * Violation type. + */ + name: "longTask"|"longLayout"|"blockedEvent"|"blockedParser"|"discouragedAPIUse"|"handler"|"recurringHandler"; + /** + * Time threshold to trigger upon. + */ + threshold: number; + } + /** - * The reason why request was blocked. + * Issued when new message was logged. */ - export type BlockedReason = "other"|"csp"|"mixed-content"|"origin"|"inspector"|"integrity"|"subresource-filter"|"content-type"|"coep-frame-resource-needs-coep-header"|"coop-sandboxed-iframe-cannot-navigate-to-coop-page"|"corp-not-same-origin"|"corp-not-same-origin-after-defaulted-to-same-origin-by-coep"|"corp-not-same-origin-after-defaulted-to-same-origin-by-dip"|"corp-not-same-origin-after-defaulted-to-same-origin-by-coep-and-dip"|"corp-not-same-site"|"sri-message-signature-mismatch"; + export type entryAddedPayload = { + /** + * The entry. + */ + entry: LogEntry; + } + /** - * The reason why request was blocked. + * Clears the log. */ - export type CorsError = "DisallowedByMode"|"InvalidResponse"|"WildcardOriginNotAllowed"|"MissingAllowOriginHeader"|"MultipleAllowOriginValues"|"InvalidAllowOriginValue"|"AllowOriginMismatch"|"InvalidAllowCredentials"|"CorsDisabledScheme"|"PreflightInvalidStatus"|"PreflightDisallowedRedirect"|"PreflightWildcardOriginNotAllowed"|"PreflightMissingAllowOriginHeader"|"PreflightMultipleAllowOriginValues"|"PreflightInvalidAllowOriginValue"|"PreflightAllowOriginMismatch"|"PreflightInvalidAllowCredentials"|"PreflightMissingAllowExternal"|"PreflightInvalidAllowExternal"|"PreflightMissingAllowPrivateNetwork"|"PreflightInvalidAllowPrivateNetwork"|"InvalidAllowMethodsPreflightResponse"|"InvalidAllowHeadersPreflightResponse"|"MethodDisallowedByPreflightResponse"|"HeaderDisallowedByPreflightResponse"|"RedirectContainsCredentials"|"InsecurePrivateNetwork"|"InvalidPrivateNetworkAccess"|"UnexpectedPrivateNetworkAccess"|"NoCorsRedirectModeNotFollow"|"PreflightMissingPrivateNetworkAccessId"|"PreflightMissingPrivateNetworkAccessName"|"PrivateNetworkAccessPermissionUnavailable"|"PrivateNetworkAccessPermissionDenied"|"LocalNetworkAccessPermissionDenied"; - export interface CorsErrorStatus { - corsError: CorsError; - failedParameter: string; + export type clearParameters = { + } + export type clearReturnValue = { } /** - * Source of serviceworker response. + * Disables log domain, prevents further log entries from being reported to the client. */ - export type ServiceWorkerResponseSource = "cache-storage"|"http-cache"|"fallback-code"|"network"; + export type disableParameters = { + } + export type disableReturnValue = { + } /** - * Determines what type of Trust Token operation is executed and -depending on the type, some additional parameters. The values -are specified in third_party/blink/renderer/core/fetch/trust_token.idl. + * Enables log domain, sends the entries collected so far to the client by means of the +`entryAdded` notification. */ - export interface TrustTokenParams { - operation: TrustTokenOperationType; + export type enableParameters = { + } + export type enableReturnValue = { + } + /** + * start violation reporting. + */ + export type startViolationsReportParameters = { /** - * Only set for "token-redemption" operation and determine whether -to request a fresh SRR or use a still valid cached SRR. + * Configuration for violations. */ - refreshPolicy: "UseCached"|"Refresh"; + config: ViolationSetting[]; + } + export type startViolationsReportReturnValue = { + } + /** + * Stop violation reporting. + */ + export type stopViolationsReportParameters = { + } + export type stopViolationsReportReturnValue = { + } + } + + /** + * This domain allows detailed inspection of media elements. + */ + export module Media { + /** + * Players will get an ID that is unique within the agent context. + */ + export type PlayerId = string; + export type Timestamp = number; + /** + * Have one type per entry in MediaLogRecord::Type +Corresponds to kMessage + */ + export interface PlayerMessage { /** - * Origins of issuers from whom to request tokens or redemption -records. + * Keep in sync with MediaLogMessageLevel +We are currently keeping the message level 'error' separate from the +PlayerError type because right now they represent different things, +this one being a DVLOG(ERROR) style log message that gets printed +based on what log level is selected in the UI, and the other is a +representation of a media::PipelineStatus object. Soon however we're +going to be moving away from using PipelineStatus for errors and +introducing a new error type which should hopefully let us integrate +the error log level into the PlayerError type. */ - issuers?: string[]; + level: "error"|"warning"|"info"|"debug"; + message: string; } - export type TrustTokenOperationType = "Issuance"|"Redemption"|"Signing"; /** - * The reason why Chrome uses a specific transport protocol for HTTP semantics. + * Corresponds to kMediaPropertyChange */ - export type AlternateProtocolUsage = "alternativeJobWonWithoutRace"|"alternativeJobWonRace"|"mainJobWonRace"|"mappingMissing"|"broken"|"dnsAlpnH3JobWonWithoutRace"|"dnsAlpnH3JobWonRace"|"unspecifiedReason"; + export interface PlayerProperty { + name: string; + value: string; + } /** - * Source of service worker router. + * Corresponds to kMediaEventTriggered */ - export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler"|"race-network-and-cache"; - export interface ServiceWorkerRouterInfo { + export interface PlayerEvent { + timestamp: Timestamp; + value: string; + } + /** + * Represents logged source line numbers reported in an error. +NOTE: file and line are from chromium c++ implementation code, not js. + */ + export interface PlayerErrorSourceLocation { + file: string; + line: number; + } + /** + * Corresponds to kMediaError + */ + export interface PlayerError { + errorType: string; /** - * ID of the rule matched. If there is a matched rule, this field will -be set, otherwiser no value will be set. + * Code is the numeric enum entry for a specific set of error codes, such +as PipelineStatusCodes in media/base/pipeline_status.h */ - ruleIdMatched?: number; + code: number; /** - * The router source of the matched rule. If there is a matched rule, this -field will be set, otherwise no value will be set. + * A trace of where this error was caused / where it passed through. */ - matchedSourceType?: ServiceWorkerRouterSource; + stack: PlayerErrorSourceLocation[]; /** - * The actual router source used. + * Errors potentially have a root cause error, ie, a DecoderError might be +caused by an WindowsError */ - actualSourceType?: ServiceWorkerRouterSource; + cause: PlayerError[]; + /** + * Extra data attached to an error, such as an HRESULT, Video Codec, etc. + */ + data: { [key: string]: string }; + } + export interface Player { + playerId: PlayerId; + domNodeId?: DOM.BackendNodeId; } + /** - * HTTP response data. + * This can be called multiple times, and can be used to set / override / +remove player properties. A null propValue indicates removal. */ - export interface Response { + export type playerPropertiesChangedPayload = { + playerId: PlayerId; + properties: PlayerProperty[]; + } + /** + * Send events as a list, allowing them to be batched on the browser for less +congestion. If batched, events must ALWAYS be in chronological order. + */ + export type playerEventsAddedPayload = { + playerId: PlayerId; + events: PlayerEvent[]; + } + /** + * Send a list of any messages that need to be delivered. + */ + export type playerMessagesLoggedPayload = { + playerId: PlayerId; + messages: PlayerMessage[]; + } + /** + * Send a list of any errors that need to be delivered. + */ + export type playerErrorsRaisedPayload = { + playerId: PlayerId; + errors: PlayerError[]; + } + /** + * Called whenever a player is created, or when a new agent joins and receives +a list of active players. If an agent is restored, it will receive one +event for each active player. + */ + export type playerCreatedPayload = { + player: Player; + } + + /** + * Enables the Media domain + */ + export type enableParameters = { + } + export type enableReturnValue = { + } + /** + * Disables the Media domain. + */ + export type disableParameters = { + } + export type disableReturnValue = { + } + } + + export module Memory { + /** + * Memory pressure level. + */ + export type PressureLevel = "moderate"|"critical"; + /** + * Heap profile sample. + */ + export interface SamplingProfileNode { /** - * Response URL. This URL can be different from CachedResource.url in case of redirect. + * Size of the sampled allocation. */ - url: string; + size: number; /** - * HTTP response status code. + * Total bytes attributed to this sample. */ - status: number; + total: number; /** - * HTTP response status text. + * Execution stack at the point of allocation. */ - statusText: string; + stack: string[]; + } + /** + * Array of heap profile samples. + */ + export interface SamplingProfile { + samples: SamplingProfileNode[]; + modules: Module[]; + } + /** + * Executable module information + */ + export interface Module { /** - * HTTP response headers. + * Name of the module. */ - headers: Headers; + name: string; /** - * HTTP response headers text. This has been replaced by the headers in Network.responseReceivedExtraInfo. + * UUID of the module. */ - headersText?: string; + uuid: string; /** - * Resource mimeType as determined by the browser. - */ - mimeType: string; - /** - * Resource charset as determined by the browser (if applicable). + * Base address where the module is loaded into memory. Encoded as a decimal +or hexadecimal (0x prefixed) string. */ - charset: string; + baseAddress: string; /** - * Refined HTTP request headers that were actually transmitted over the network. + * Size of the module in bytes. */ - requestHeaders?: Headers; + size: number; + } + /** + * DOM object counter data. + */ + export interface DOMCounter { /** - * HTTP request headers text. This has been replaced by the headers in Network.requestWillBeSentExtraInfo. + * Object name. Note: object names should be presumed volatile and clients should not expect +the returned names to be consistent across runs. */ - requestHeadersText?: string; + name: string; /** - * Specifies whether physical connection was actually reused for this request. + * Object count. */ - connectionReused: boolean; + count: number; + } + + + /** + * Retruns current DOM object counters. + */ + export type getDOMCountersParameters = { + } + export type getDOMCountersReturnValue = { + documents: number; + nodes: number; + jsEventListeners: number; + } + /** + * Retruns DOM object counters after preparing renderer for leak detection. + */ + export type getDOMCountersForLeakDetectionParameters = { + } + export type getDOMCountersForLeakDetectionReturnValue = { /** - * Physical connection id that was actually used for this request. + * DOM object counters. */ - connectionId: number; + counters: DOMCounter[]; + } + /** + * Prepares for leak detection by terminating workers, stopping spellcheckers, +dropping non-essential internal caches, running garbage collections, etc. + */ + export type prepareForLeakDetectionParameters = { + } + export type prepareForLeakDetectionReturnValue = { + } + /** + * Simulate OomIntervention by purging V8 memory. + */ + export type forciblyPurgeJavaScriptMemoryParameters = { + } + export type forciblyPurgeJavaScriptMemoryReturnValue = { + } + /** + * Enable/disable suppressing memory pressure notifications in all processes. + */ + export type setPressureNotificationsSuppressedParameters = { /** - * Remote IP address. + * If true, memory pressure notifications will be suppressed. */ - remoteIPAddress?: string; + suppressed: boolean; + } + export type setPressureNotificationsSuppressedReturnValue = { + } + /** + * Simulate a memory pressure notification in all processes. + */ + export type simulatePressureNotificationParameters = { /** - * Remote port. + * Memory pressure level of the notification. */ - remotePort?: number; + level: PressureLevel; + } + export type simulatePressureNotificationReturnValue = { + } + /** + * Start collecting native memory profile. + */ + export type startSamplingParameters = { /** - * Specifies that the request was served from the disk cache. + * Average number of bytes between samples. */ - fromDiskCache?: boolean; + samplingInterval?: number; /** - * Specifies that the request was served from the ServiceWorker. + * Do not randomize intervals between samples. */ - fromServiceWorker?: boolean; + suppressRandomness?: boolean; + } + export type startSamplingReturnValue = { + } + /** + * Stop collecting native memory profile. + */ + export type stopSamplingParameters = { + } + export type stopSamplingReturnValue = { + } + /** + * Retrieve native memory allocations profile +collected since renderer process startup. + */ + export type getAllTimeSamplingProfileParameters = { + } + export type getAllTimeSamplingProfileReturnValue = { + profile: SamplingProfile; + } + /** + * Retrieve native memory allocations profile +collected since browser process startup. + */ + export type getBrowserSamplingProfileParameters = { + } + export type getBrowserSamplingProfileReturnValue = { + profile: SamplingProfile; + } + /** + * Retrieve native memory allocations profile collected since last +`startSampling` call. + */ + export type getSamplingProfileParameters = { + } + export type getSamplingProfileReturnValue = { + profile: SamplingProfile; + } + } + + /** + * Network domain allows tracking network activities of the page. It exposes information about http, +file, data and other requests and responses, their headers, bodies, timing, etc. + */ + export module Network { + /** + * Resource type as it was perceived by the rendering engine. + */ + export type ResourceType = "Document"|"Stylesheet"|"Image"|"Media"|"Font"|"Script"|"TextTrack"|"XHR"|"Fetch"|"Prefetch"|"EventSource"|"WebSocket"|"Manifest"|"SignedExchange"|"Ping"|"CSPViolationReport"|"Preflight"|"FedCM"|"Other"; + /** + * Unique loader identifier. + */ + export type LoaderId = string; + /** + * Unique network request identifier. +Note that this does not identify individual HTTP requests that are part of +a network request. + */ + export type RequestId = string; + /** + * Unique intercepted request identifier. + */ + export type InterceptionId = string; + /** + * Network level fetch failure reason. + */ + export type ErrorReason = "Failed"|"Aborted"|"TimedOut"|"AccessDenied"|"ConnectionClosed"|"ConnectionReset"|"ConnectionRefused"|"ConnectionAborted"|"ConnectionFailed"|"NameNotResolved"|"InternetDisconnected"|"AddressUnreachable"|"BlockedByClient"|"BlockedByResponse"; + /** + * UTC time in seconds, counted from January 1, 1970. + */ + export type TimeSinceEpoch = number; + /** + * Monotonically increasing time in seconds since an arbitrary point in the past. + */ + export type MonotonicTime = number; + /** + * Request / response headers as keys / values of JSON object. + */ + export type Headers = { [key: string]: string }; + /** + * The underlying connection technology that the browser is supposedly using. + */ + export type ConnectionType = "none"|"cellular2g"|"cellular3g"|"cellular4g"|"bluetooth"|"ethernet"|"wifi"|"wimax"|"other"; + /** + * Represents the cookie's 'SameSite' status: +https://tools.ietf.org/html/draft-west-first-party-cookies + */ + export type CookieSameSite = "Strict"|"Lax"|"None"; + /** + * Represents the cookie's 'Priority' status: +https://tools.ietf.org/html/draft-west-cookie-priority-00 + */ + export type CookiePriority = "Low"|"Medium"|"High"; + /** + * Represents the source scheme of the origin that originally set the cookie. +A value of "Unset" allows protocol clients to emulate legacy cookie scope for the scheme. +This is a temporary ability and it will be removed in the future. + */ + export type CookieSourceScheme = "Unset"|"NonSecure"|"Secure"; + /** + * Timing information for the request. + */ + export interface ResourceTiming { /** - * Specifies that the request was served from the prefetch cache. + * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in +milliseconds relatively to this requestTime. */ - fromPrefetchCache?: boolean; + requestTime: number; /** - * Specifies that the request was served from the prefetch cache. + * Started resolving proxy. */ - fromEarlyHints?: boolean; + proxyStart: number; /** - * Information about how ServiceWorker Static Router API was used. If this -field is set with `matchedSourceType` field, a matching rule is found. -If this field is set without `matchedSource`, no matching rule is found. -Otherwise, the API is not used. + * Finished resolving proxy. */ - serviceWorkerRouterInfo?: ServiceWorkerRouterInfo; + proxyEnd: number; /** - * Total number of bytes received for this request so far. + * Started DNS address resolve. */ - encodedDataLength: number; + dnsStart: number; /** - * Timing information for the given request. + * Finished DNS address resolve. */ - timing?: ResourceTiming; + dnsEnd: number; /** - * Response source of response from ServiceWorker. + * Started connecting to the remote host. */ - serviceWorkerResponseSource?: ServiceWorkerResponseSource; + connectStart: number; /** - * The time at which the returned response was generated. + * Connected to the remote host. */ - responseTime?: TimeSinceEpoch; + connectEnd: number; /** - * Cache Storage Cache Name. + * Started SSL handshake. */ - cacheStorageCacheName?: string; + sslStart: number; /** - * Protocol used to fetch this request. + * Finished SSL handshake. */ - protocol?: string; + sslEnd: number; /** - * The reason why Chrome uses a specific transport protocol for HTTP semantics. + * Started running ServiceWorker. */ - alternateProtocolUsage?: AlternateProtocolUsage; + workerStart: number; /** - * Security state of the request resource. + * Finished Starting ServiceWorker. */ - securityState: Security.SecurityState; + workerReady: number; /** - * Security details for the request. + * Started fetch event. */ - securityDetails?: SecurityDetails; + workerFetchStart: number; /** - * Indicates whether the request was sent through IP Protection proxies. If -set to true, the request used the IP Protection privacy feature. + * Settled fetch event respondWith promise. */ - isIpProtectionUsed?: boolean; + workerRespondWithSettled: number; + /** + * Started ServiceWorker static routing source evaluation. + */ + workerRouterEvaluationStart?: number; + /** + * Started cache lookup when the source was evaluated to `cache`. + */ + workerCacheLookupStart?: number; + /** + * Started sending request. + */ + sendStart: number; + /** + * Finished sending request. + */ + sendEnd: number; + /** + * Time the server started pushing request. + */ + pushStart: number; + /** + * Time the server finished pushing request. + */ + pushEnd: number; + /** + * Started receiving response headers. + */ + receiveHeadersStart: number; + /** + * Finished receiving response headers. + */ + receiveHeadersEnd: number; } /** - * WebSocket request data. + * Loading priority of a resource request. */ - export interface WebSocketRequest { - /** - * HTTP request headers. - */ - headers: Headers; + export type ResourcePriority = "VeryLow"|"Low"|"Medium"|"High"|"VeryHigh"; + /** + * Post data entry for HTTP request + */ + export interface PostDataEntry { + bytes?: binary; } /** - * WebSocket response data. + * HTTP request data. */ - export interface WebSocketResponse { + export interface Request { /** - * HTTP response status code. + * Request URL (without fragment). */ - status: number; + url: string; /** - * HTTP response status text. + * Fragment of the requested URL starting with hash, if present. */ - statusText: string; + urlFragment?: string; /** - * HTTP response headers. + * HTTP request method. */ - headers: Headers; + method: string; /** - * HTTP response headers text. + * HTTP request headers. */ - headersText?: string; + headers: Headers; /** - * HTTP request headers. + * HTTP POST request data. +Use postDataEntries instead. */ - requestHeaders?: Headers; + postData?: string; /** - * HTTP request headers text. + * True when the request has POST data. Note that postData might still be omitted when this flag is true when the data is too long. */ - requestHeadersText?: string; - } - /** - * WebSocket message data. This represents an entire WebSocket message, not just a fragmented frame as the name suggests. - */ - export interface WebSocketFrame { + hasPostData?: boolean; /** - * WebSocket message opcode. + * Request body elements (post data broken into individual entries). */ - opcode: number; + postDataEntries?: PostDataEntry[]; /** - * WebSocket message mask. + * The mixed content type of the request. */ - mask: boolean; + mixedContentType?: Security.MixedContentType; /** - * WebSocket message payload data. -If the opcode is 1, this is a text message and payloadData is a UTF-8 string. -If the opcode isn't 1, then payloadData is a base64 encoded string representing binary data. + * Priority of the resource request at the time request is sent. */ - payloadData: string; - } - /** - * Information about the cached resource. - */ - export interface CachedResource { + initialPriority: ResourcePriority; /** - * Resource URL. This is the url of the original network request. + * The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/ */ - url: string; + referrerPolicy: "unsafe-url"|"no-referrer-when-downgrade"|"no-referrer"|"origin"|"origin-when-cross-origin"|"same-origin"|"strict-origin"|"strict-origin-when-cross-origin"; /** - * Type of this resource. + * Whether is loaded via link preload. */ - type: ResourceType; + isLinkPreload?: boolean; /** - * Cached response data. + * Set for requests when the TrustToken API is used. Contains the parameters +passed by the developer (e.g. via "fetch") as understood by the backend. */ - response?: Response; + trustTokenParams?: TrustTokenParams; /** - * Cached response body size. + * True if this resource request is considered to be the 'same site' as the +request corresponding to the main frame. */ - bodySize: number; + isSameSite?: boolean; } /** - * Information about the request initiator. + * Details of a signed certificate timestamp (SCT). */ - export interface Initiator { + export interface SignedCertificateTimestamp { /** - * Type of this initiator. + * Validation status. */ - type: "parser"|"script"|"preload"|"SignedExchange"|"preflight"|"other"; + status: string; /** - * Initiator JavaScript stack trace, set for Script only. -Requires the Debugger domain to be enabled. + * Origin. */ - stack?: Runtime.StackTrace; + origin: string; /** - * Initiator URL, set for Parser type or for Script type (when script is importing module) or for SignedExchange type. + * Log name / description. */ - url?: string; + logDescription: string; /** - * Initiator line number, set for Parser type or for Script type (when script is importing -module) (0-based). + * Log ID. */ - lineNumber?: number; + logId: string; /** - * Initiator column number, set for Parser type or for Script type (when script is importing -module) (0-based). + * Issuance date. Unlike TimeSinceEpoch, this contains the number of +milliseconds since January 1, 1970, UTC, not the number of seconds. */ - columnNumber?: number; + timestamp: number; /** - * Set if another request triggered this request (e.g. preflight). + * Hash algorithm. */ - requestId?: RequestId; - } - /** - * cookiePartitionKey object -The representation of the components of the key that are created by the cookiePartitionKey class contained in net/cookies/cookie_partition_key.h. - */ - export interface CookiePartitionKey { + hashAlgorithm: string; /** - * The site of the top-level URL the browser was visiting at the start -of the request to the endpoint that set the cookie. + * Signature algorithm. */ - topLevelSite: string; + signatureAlgorithm: string; /** - * Indicates if the cookie has any ancestors that are cross-site to the topLevelSite. + * Signature data. */ - hasCrossSiteAncestor: boolean; + signatureData: string; } /** - * Cookie object + * Security details about a request. */ - export interface Cookie { - /** - * Cookie name. - */ - name: string; + export interface SecurityDetails { /** - * Cookie value. + * Protocol name (e.g. "TLS 1.2" or "QUIC"). */ - value: string; + protocol: string; /** - * Cookie domain. + * Key Exchange used by the connection, or the empty string if not applicable. */ - domain: string; + keyExchange: string; /** - * Cookie path. + * (EC)DH group used by the connection, if applicable. */ - path: string; + keyExchangeGroup?: string; /** - * Cookie expiration date as the number of seconds since the UNIX epoch. + * Cipher name. */ - expires: number; + cipher: string; /** - * Cookie size. + * TLS MAC. Note that AEAD ciphers do not have separate MACs. */ - size: number; + mac?: string; /** - * True if cookie is http-only. + * Certificate ID value. */ - httpOnly: boolean; + certificateId: Security.CertificateId; /** - * True if cookie is secure. + * Certificate subject name. */ - secure: boolean; + subjectName: string; /** - * True in case of session cookie. + * Subject Alternative Name (SAN) DNS names and IP addresses. */ - session: boolean; + sanList: string[]; /** - * Cookie SameSite type. + * Name of the issuing CA. */ - sameSite?: CookieSameSite; + issuer: string; /** - * Cookie Priority + * Certificate valid from date. */ - priority: CookiePriority; + validFrom: TimeSinceEpoch; /** - * True if cookie is SameParty. + * Certificate valid to (expiration) date */ - sameParty: boolean; + validTo: TimeSinceEpoch; /** - * Cookie source scheme type. + * List of signed certificate timestamps (SCTs). */ - sourceScheme: CookieSourceScheme; + signedCertificateTimestampList: SignedCertificateTimestamp[]; /** - * Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. -An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. -This is a temporary ability and it will be removed in the future. + * Whether the request complied with Certificate Transparency policy */ - sourcePort: number; + certificateTransparencyCompliance: CertificateTransparencyCompliance; /** - * Cookie partition key. + * The signature algorithm used by the server in the TLS server signature, +represented as a TLS SignatureScheme code point. Omitted if not +applicable or not known. */ - partitionKey?: CookiePartitionKey; + serverSignatureAlgorithm?: number; /** - * True if cookie partition key is opaque. + * Whether the connection used Encrypted ClientHello */ - partitionKeyOpaque?: boolean; + encryptedClientHello: boolean; } /** - * Types of reasons why a cookie may not be stored from a response. + * Whether the request complied with Certificate Transparency policy. */ - export type SetCookieBlockedReason = "SecureOnly"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"SyntaxError"|"SchemeNotSupported"|"OverwriteSecure"|"InvalidDomain"|"InvalidPrefix"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"SamePartyConflictsWithOtherAttributes"|"NameValuePairExceedsMaxSize"|"DisallowedCharacter"|"NoCookieContent"; + export type CertificateTransparencyCompliance = "unknown"|"not-compliant"|"compliant"; /** - * Types of reasons why a cookie may not be sent with a request. + * The reason why request was blocked. */ - export type CookieBlockedReason = "SecureOnly"|"NotOnPath"|"DomainMismatch"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"NameValuePairExceedsMaxSize"|"PortMismatch"|"SchemeMismatch"|"AnonymousContext"; + export type BlockedReason = "other"|"csp"|"mixed-content"|"origin"|"inspector"|"integrity"|"subresource-filter"|"content-type"|"coep-frame-resource-needs-coep-header"|"coop-sandboxed-iframe-cannot-navigate-to-coop-page"|"corp-not-same-origin"|"corp-not-same-origin-after-defaulted-to-same-origin-by-coep"|"corp-not-same-origin-after-defaulted-to-same-origin-by-dip"|"corp-not-same-origin-after-defaulted-to-same-origin-by-coep-and-dip"|"corp-not-same-site"|"sri-message-signature-mismatch"; /** - * Types of reasons why a cookie should have been blocked by 3PCD but is exempted for the request. + * Sets Controls for IP Proxy of requests. +Page reload is required before the new behavior will be observed. */ - export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TopLevelTPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"Scheme"|"SameSiteNoneCookiesInSandbox"; + export type IpProxyStatus = "Available"|"FeatureNotEnabled"|"MaskedDomainListNotEnabled"|"MaskedDomainListNotPopulated"|"AuthTokensUnavailable"|"Unavailable"|"BypassedByDevTools"; /** - * A cookie which was not stored from a response with the corresponding reason. + * The reason why request was blocked. */ - export interface BlockedSetCookieWithReason { - /** - * The reason(s) this cookie was blocked. - */ - blockedReasons: SetCookieBlockedReason[]; - /** - * The string representing this individual cookie as it would appear in the header. -This is not the entire "cookie" or "set-cookie" header which could have multiple cookies. + export type CorsError = "DisallowedByMode"|"InvalidResponse"|"WildcardOriginNotAllowed"|"MissingAllowOriginHeader"|"MultipleAllowOriginValues"|"InvalidAllowOriginValue"|"AllowOriginMismatch"|"InvalidAllowCredentials"|"CorsDisabledScheme"|"PreflightInvalidStatus"|"PreflightDisallowedRedirect"|"PreflightWildcardOriginNotAllowed"|"PreflightMissingAllowOriginHeader"|"PreflightMultipleAllowOriginValues"|"PreflightInvalidAllowOriginValue"|"PreflightAllowOriginMismatch"|"PreflightInvalidAllowCredentials"|"PreflightMissingAllowExternal"|"PreflightInvalidAllowExternal"|"PreflightMissingAllowPrivateNetwork"|"PreflightInvalidAllowPrivateNetwork"|"InvalidAllowMethodsPreflightResponse"|"InvalidAllowHeadersPreflightResponse"|"MethodDisallowedByPreflightResponse"|"HeaderDisallowedByPreflightResponse"|"RedirectContainsCredentials"|"InsecurePrivateNetwork"|"InvalidPrivateNetworkAccess"|"UnexpectedPrivateNetworkAccess"|"NoCorsRedirectModeNotFollow"|"PreflightMissingPrivateNetworkAccessId"|"PreflightMissingPrivateNetworkAccessName"|"PrivateNetworkAccessPermissionUnavailable"|"PrivateNetworkAccessPermissionDenied"|"LocalNetworkAccessPermissionDenied"; + export interface CorsErrorStatus { + corsError: CorsError; + failedParameter: string; + } + /** + * Source of serviceworker response. + */ + export type ServiceWorkerResponseSource = "cache-storage"|"http-cache"|"fallback-code"|"network"; + /** + * Determines what type of Trust Token operation is executed and +depending on the type, some additional parameters. The values +are specified in third_party/blink/renderer/core/fetch/trust_token.idl. + */ + export interface TrustTokenParams { + operation: TrustTokenOperationType; + /** + * Only set for "token-redemption" operation and determine whether +to request a fresh SRR or use a still valid cached SRR. */ - cookieLine: string; + refreshPolicy: "UseCached"|"Refresh"; /** - * The cookie object which represents the cookie which was not stored. It is optional because -sometimes complete cookie information is not available, such as in the case of parsing -errors. + * Origins of issuers from whom to request tokens or redemption +records. */ - cookie?: Cookie; + issuers?: string[]; } + export type TrustTokenOperationType = "Issuance"|"Redemption"|"Signing"; /** - * A cookie should have been blocked by 3PCD but is exempted and stored from a response with the -corresponding reason. A cookie could only have at most one exemption reason. + * The reason why Chrome uses a specific transport protocol for HTTP semantics. */ - export interface ExemptedSetCookieWithReason { + export type AlternateProtocolUsage = "alternativeJobWonWithoutRace"|"alternativeJobWonRace"|"mainJobWonRace"|"mappingMissing"|"broken"|"dnsAlpnH3JobWonWithoutRace"|"dnsAlpnH3JobWonRace"|"unspecifiedReason"; + /** + * Source of service worker router. + */ + export type ServiceWorkerRouterSource = "network"|"cache"|"fetch-event"|"race-network-and-fetch-handler"|"race-network-and-cache"; + export interface ServiceWorkerRouterInfo { /** - * The reason the cookie was exempted. + * ID of the rule matched. If there is a matched rule, this field will +be set, otherwiser no value will be set. */ - exemptionReason: CookieExemptionReason; + ruleIdMatched?: number; /** - * The string representing this individual cookie as it would appear in the header. + * The router source of the matched rule. If there is a matched rule, this +field will be set, otherwise no value will be set. */ - cookieLine: string; + matchedSourceType?: ServiceWorkerRouterSource; /** - * The cookie object representing the cookie. + * The actual router source used. */ - cookie: Cookie; + actualSourceType?: ServiceWorkerRouterSource; } /** - * A cookie associated with the request which may or may not be sent with it. -Includes the cookies itself and reasons for blocking or exemption. + * HTTP response data. */ - export interface AssociatedCookie { + export interface Response { /** - * The cookie object representing the cookie which was not sent. + * Response URL. This URL can be different from CachedResource.url in case of redirect. */ - cookie: Cookie; + url: string; /** - * The reason(s) the cookie was blocked. If empty means the cookie is included. + * HTTP response status code. */ - blockedReasons: CookieBlockedReason[]; + status: number; /** - * The reason the cookie should have been blocked by 3PCD but is exempted. A cookie could -only have at most one exemption reason. + * HTTP response status text. */ - exemptionReason?: CookieExemptionReason; - } - /** - * Cookie parameter object - */ - export interface CookieParam { + statusText: string; /** - * Cookie name. + * HTTP response headers. */ - name: string; + headers: Headers; /** - * Cookie value. + * HTTP response headers text. This has been replaced by the headers in Network.responseReceivedExtraInfo. */ - value: string; + headersText?: string; /** - * The request-URI to associate with the setting of the cookie. This value can affect the -default domain, path, source port, and source scheme values of the created cookie. + * Resource mimeType as determined by the browser. */ - url?: string; + mimeType: string; /** - * Cookie domain. + * Resource charset as determined by the browser (if applicable). */ - domain?: string; + charset: string; /** - * Cookie path. + * Refined HTTP request headers that were actually transmitted over the network. */ - path?: string; + requestHeaders?: Headers; /** - * True if cookie is secure. + * HTTP request headers text. This has been replaced by the headers in Network.requestWillBeSentExtraInfo. */ - secure?: boolean; + requestHeadersText?: string; /** - * True if cookie is http-only. + * Specifies whether physical connection was actually reused for this request. */ - httpOnly?: boolean; + connectionReused: boolean; /** - * Cookie SameSite type. + * Physical connection id that was actually used for this request. */ - sameSite?: CookieSameSite; + connectionId: number; /** - * Cookie expiration date, session cookie if not set + * Remote IP address. */ - expires?: TimeSinceEpoch; + remoteIPAddress?: string; /** - * Cookie Priority. + * Remote port. */ - priority?: CookiePriority; + remotePort?: number; /** - * True if cookie is SameParty. + * Specifies that the request was served from the disk cache. */ - sameParty?: boolean; + fromDiskCache?: boolean; /** - * Cookie source scheme type. + * Specifies that the request was served from the ServiceWorker. */ - sourceScheme?: CookieSourceScheme; + fromServiceWorker?: boolean; /** - * Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. -An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. -This is a temporary ability and it will be removed in the future. + * Specifies that the request was served from the prefetch cache. */ - sourcePort?: number; + fromPrefetchCache?: boolean; /** - * Cookie partition key. If not set, the cookie will be set as not partitioned. + * Specifies that the request was served from the prefetch cache. */ - partitionKey?: CookiePartitionKey; - } - /** - * Authorization challenge for HTTP status code 401 or 407. - */ - export interface AuthChallenge { + fromEarlyHints?: boolean; /** - * Source of the authentication challenge. + * Information about how ServiceWorker Static Router API was used. If this +field is set with `matchedSourceType` field, a matching rule is found. +If this field is set without `matchedSource`, no matching rule is found. +Otherwise, the API is not used. */ - source?: "Server"|"Proxy"; + serviceWorkerRouterInfo?: ServiceWorkerRouterInfo; /** - * Origin of the challenger. + * Total number of bytes received for this request so far. */ - origin: string; + encodedDataLength: number; /** - * The authentication scheme used, such as basic or digest + * Timing information for the given request. */ - scheme: string; + timing?: ResourceTiming; /** - * The realm of the challenge. May be empty. + * Response source of response from ServiceWorker. */ - realm: string; - } - /** - * Response to an AuthChallenge. - */ - export interface AuthChallengeResponse { + serviceWorkerResponseSource?: ServiceWorkerResponseSource; /** - * The decision on what to do in response to the authorization challenge. Default means -deferring to the default behavior of the net stack, which will likely either the Cancel -authentication or display a popup dialog box. + * The time at which the returned response was generated. */ - response: "Default"|"CancelAuth"|"ProvideCredentials"; + responseTime?: TimeSinceEpoch; /** - * The username to provide, possibly empty. Should only be set if response is -ProvideCredentials. + * Cache Storage Cache Name. */ - username?: string; + cacheStorageCacheName?: string; /** - * The password to provide, possibly empty. Should only be set if response is -ProvideCredentials. + * Protocol used to fetch this request. */ - password?: string; - } - /** - * Stages of the interception to begin intercepting. Request will intercept before the request is -sent. Response will intercept after the response is received. - */ - export type InterceptionStage = "Request"|"HeadersReceived"; - /** - * Request pattern for interception. - */ - export interface RequestPattern { + protocol?: string; /** - * Wildcards (`'*'` -> zero or more, `'?'` -> exactly one) are allowed. Escape character is -backslash. Omitting is equivalent to `"*"`. + * The reason why Chrome uses a specific transport protocol for HTTP semantics. */ - urlPattern?: string; + alternateProtocolUsage?: AlternateProtocolUsage; /** - * If set, only requests for matching resource types will be intercepted. + * Security state of the request resource. */ - resourceType?: ResourceType; + securityState: Security.SecurityState; /** - * Stage at which to begin intercepting requests. Default is Request. + * Security details for the request. */ - interceptionStage?: InterceptionStage; + securityDetails?: SecurityDetails; + /** + * Indicates whether the request was sent through IP Protection proxies. If +set to true, the request used the IP Protection privacy feature. + */ + isIpProtectionUsed?: boolean; } /** - * Information about a signed exchange signature. -https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#rfc.section.3.1 + * WebSocket request data. */ - export interface SignedExchangeSignature { + export interface WebSocketRequest { /** - * Signed exchange signature label. + * HTTP request headers. */ - label: string; + headers: Headers; + } + /** + * WebSocket response data. + */ + export interface WebSocketResponse { /** - * The hex string of signed exchange signature. + * HTTP response status code. */ - signature: string; + status: number; /** - * Signed exchange signature integrity. + * HTTP response status text. */ - integrity: string; + statusText: string; /** - * Signed exchange signature cert Url. + * HTTP response headers. */ - certUrl?: string; + headers: Headers; /** - * The hex string of signed exchange signature cert sha256. + * HTTP response headers text. */ - certSha256?: string; + headersText?: string; /** - * Signed exchange signature validity Url. + * HTTP request headers. */ - validityUrl: string; + requestHeaders?: Headers; /** - * Signed exchange signature date. + * HTTP request headers text. */ - date: number; + requestHeadersText?: string; + } + /** + * WebSocket message data. This represents an entire WebSocket message, not just a fragmented frame as the name suggests. + */ + export interface WebSocketFrame { /** - * Signed exchange signature expires. + * WebSocket message opcode. */ - expires: number; + opcode: number; /** - * The encoded certificates. + * WebSocket message mask. */ - certificates?: string[]; + mask: boolean; + /** + * WebSocket message payload data. +If the opcode is 1, this is a text message and payloadData is a UTF-8 string. +If the opcode isn't 1, then payloadData is a base64 encoded string representing binary data. + */ + payloadData: string; } /** - * Information about a signed exchange header. -https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#cbor-representation + * Information about the cached resource. */ - export interface SignedExchangeHeader { - /** - * Signed exchange request URL. - */ - requestUrl: string; + export interface CachedResource { /** - * Signed exchange response code. + * Resource URL. This is the url of the original network request. */ - responseCode: number; + url: string; /** - * Signed exchange response headers. + * Type of this resource. */ - responseHeaders: Headers; + type: ResourceType; /** - * Signed exchange response signature. + * Cached response data. */ - signatures: SignedExchangeSignature[]; + response?: Response; /** - * Signed exchange header integrity hash in the form of `sha256-`. + * Cached response body size. */ - headerIntegrity: string; + bodySize: number; } /** - * Field type for a signed exchange related error. - */ - export type SignedExchangeErrorField = "signatureSig"|"signatureIntegrity"|"signatureCertUrl"|"signatureCertSha256"|"signatureValidityUrl"|"signatureTimestamps"; - /** - * Information about a signed exchange response. + * Information about the request initiator. */ - export interface SignedExchangeError { + export interface Initiator { /** - * Error message. + * Type of this initiator. */ - message: string; + type: "parser"|"script"|"preload"|"SignedExchange"|"preflight"|"other"; /** - * The index of the signature which caused the error. + * Initiator JavaScript stack trace, set for Script only. +Requires the Debugger domain to be enabled. */ - signatureIndex?: number; + stack?: Runtime.StackTrace; /** - * The field which caused the error. + * Initiator URL, set for Parser type or for Script type (when script is importing module) or for SignedExchange type. */ - errorField?: SignedExchangeErrorField; - } - /** - * Information about a signed exchange response. - */ - export interface SignedExchangeInfo { + url?: string; /** - * The outer response of signed HTTP exchange which was received from network. + * Initiator line number, set for Parser type or for Script type (when script is importing +module) (0-based). */ - outerResponse: Response; + lineNumber?: number; /** - * Whether network response for the signed exchange was accompanied by -extra headers. + * Initiator column number, set for Parser type or for Script type (when script is importing +module) (0-based). */ - hasExtraInfo: boolean; + columnNumber?: number; /** - * Information about the signed exchange header. + * Set if another request triggered this request (e.g. preflight). */ - header?: SignedExchangeHeader; + requestId?: RequestId; + } + /** + * cookiePartitionKey object +The representation of the components of the key that are created by the cookiePartitionKey class contained in net/cookies/cookie_partition_key.h. + */ + export interface CookiePartitionKey { /** - * Security details for the signed exchange header. + * The site of the top-level URL the browser was visiting at the start +of the request to the endpoint that set the cookie. */ - securityDetails?: SecurityDetails; + topLevelSite: string; /** - * Errors occurred while handling the signed exchange. + * Indicates if the cookie has any ancestors that are cross-site to the topLevelSite. */ - errors?: SignedExchangeError[]; + hasCrossSiteAncestor: boolean; } /** - * List of content encodings supported by the backend. + * Cookie object */ - export type ContentEncoding = "deflate"|"gzip"|"br"|"zstd"; - export type DirectSocketDnsQueryType = "ipv4"|"ipv6"; - export interface DirectTCPSocketOptions { + export interface Cookie { /** - * TCP_NODELAY option + * Cookie name. */ - noDelay: boolean; + name: string; /** - * Expected to be unsigned integer. + * Cookie value. */ - keepAliveDelay?: number; + value: string; /** - * Expected to be unsigned integer. + * Cookie domain. */ - sendBufferSize?: number; + domain: string; /** - * Expected to be unsigned integer. + * Cookie path. */ - receiveBufferSize?: number; - dnsQueryType?: DirectSocketDnsQueryType; - } - export interface DirectUDPSocketOptions { - remoteAddr?: string; + path: string; /** - * Unsigned int 16. + * Cookie expiration date as the number of seconds since the UNIX epoch. */ - remotePort?: number; - localAddr?: string; + expires: number; /** - * Unsigned int 16. + * Cookie size. */ - localPort?: number; - dnsQueryType?: DirectSocketDnsQueryType; + size: number; /** - * Expected to be unsigned integer. + * True if cookie is http-only. */ - sendBufferSize?: number; + httpOnly: boolean; /** - * Expected to be unsigned integer. + * True if cookie is secure. */ - receiveBufferSize?: number; - } - export interface DirectUDPMessage { - data: binary; + secure: boolean; /** - * Null for connected mode. + * True in case of session cookie. */ - remoteAddr?: string; + session: boolean; /** - * Null for connected mode. -Expected to be unsigned integer. + * Cookie SameSite type. */ - remotePort?: number; - } - export type PrivateNetworkRequestPolicy = "Allow"|"BlockFromInsecureToMorePrivate"|"WarnFromInsecureToMorePrivate"|"PreflightBlock"|"PreflightWarn"|"PermissionBlock"|"PermissionWarn"; - export type IPAddressSpace = "Loopback"|"Local"|"Public"|"Unknown"; - export interface ConnectTiming { + sameSite?: CookieSameSite; /** - * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in -milliseconds relatively to this requestTime. Matches ResourceTiming's requestTime for -the same request (but not for redirected requests). + * Cookie Priority */ - requestTime: number; - } - export interface ClientSecurityState { - initiatorIsSecureContext: boolean; - initiatorIPAddressSpace: IPAddressSpace; - privateNetworkRequestPolicy: PrivateNetworkRequestPolicy; - } - export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"RestrictProperties"|"UnsafeNone"|"SameOriginPlusCoep"|"RestrictPropertiesPlusCoep"|"NoopenerAllowPopups"; - export interface CrossOriginOpenerPolicyStatus { - value: CrossOriginOpenerPolicyValue; - reportOnlyValue: CrossOriginOpenerPolicyValue; - reportingEndpoint?: string; - reportOnlyReportingEndpoint?: string; - } - export type CrossOriginEmbedderPolicyValue = "None"|"Credentialless"|"RequireCorp"; - export interface CrossOriginEmbedderPolicyStatus { - value: CrossOriginEmbedderPolicyValue; - reportOnlyValue: CrossOriginEmbedderPolicyValue; - reportingEndpoint?: string; - reportOnlyReportingEndpoint?: string; - } - export type ContentSecurityPolicySource = "HTTP"|"Meta"; - export interface ContentSecurityPolicyStatus { - effectiveDirectives: string; - isEnforced: boolean; - source: ContentSecurityPolicySource; - } - export interface SecurityIsolationStatus { - coop?: CrossOriginOpenerPolicyStatus; - coep?: CrossOriginEmbedderPolicyStatus; - csp?: ContentSecurityPolicyStatus[]; - } - /** - * The status of a Reporting API report. - */ - export type ReportStatus = "Queued"|"Pending"|"MarkedForRemoval"|"Success"; - export type ReportId = string; - /** - * An object representing a report generated by the Reporting API. - */ - export interface ReportingApiReport { - id: ReportId; + priority: CookiePriority; /** - * The URL of the document that triggered the report. + * True if cookie is SameParty. */ - initiatorUrl: string; + sameParty: boolean; /** - * The name of the endpoint group that should be used to deliver the report. + * Cookie source scheme type. */ - destination: string; + sourceScheme: CookieSourceScheme; /** - * The type of the report (specifies the set of data that is contained in the report body). + * Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. +An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. +This is a temporary ability and it will be removed in the future. */ - type: string; + sourcePort: number; /** - * When the report was generated. + * Cookie partition key. */ - timestamp: Network.TimeSinceEpoch; + partitionKey?: CookiePartitionKey; /** - * How many uploads deep the related request was. + * True if cookie partition key is opaque. */ - depth: number; + partitionKeyOpaque?: boolean; + } + /** + * Types of reasons why a cookie may not be stored from a response. + */ + export type SetCookieBlockedReason = "SecureOnly"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"SyntaxError"|"SchemeNotSupported"|"OverwriteSecure"|"InvalidDomain"|"InvalidPrefix"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"SamePartyConflictsWithOtherAttributes"|"NameValuePairExceedsMaxSize"|"DisallowedCharacter"|"NoCookieContent"; + /** + * Types of reasons why a cookie may not be sent with a request. + */ + export type CookieBlockedReason = "SecureOnly"|"NotOnPath"|"DomainMismatch"|"SameSiteStrict"|"SameSiteLax"|"SameSiteUnspecifiedTreatedAsLax"|"SameSiteNoneInsecure"|"UserPreferences"|"ThirdPartyPhaseout"|"ThirdPartyBlockedInFirstPartySet"|"UnknownError"|"SchemefulSameSiteStrict"|"SchemefulSameSiteLax"|"SchemefulSameSiteUnspecifiedTreatedAsLax"|"SamePartyFromCrossPartyContext"|"NameValuePairExceedsMaxSize"|"PortMismatch"|"SchemeMismatch"|"AnonymousContext"; + /** + * Types of reasons why a cookie should have been blocked by 3PCD but is exempted for the request. + */ + export type CookieExemptionReason = "None"|"UserSetting"|"TPCDMetadata"|"TPCDDeprecationTrial"|"TopLevelTPCDDeprecationTrial"|"TPCDHeuristics"|"EnterprisePolicy"|"StorageAccess"|"TopLevelStorageAccess"|"Scheme"|"SameSiteNoneCookiesInSandbox"; + /** + * A cookie which was not stored from a response with the corresponding reason. + */ + export interface BlockedSetCookieWithReason { /** - * The number of delivery attempts made so far, not including an active attempt. + * The reason(s) this cookie was blocked. */ - completedAttempts: number; - body: { [key: string]: string }; - status: ReportStatus; - } - export interface ReportingApiEndpoint { + blockedReasons: SetCookieBlockedReason[]; /** - * The URL of the endpoint to which reports may be delivered. + * The string representing this individual cookie as it would appear in the header. +This is not the entire "cookie" or "set-cookie" header which could have multiple cookies. */ - url: string; + cookieLine: string; /** - * Name of the endpoint group. + * The cookie object which represents the cookie which was not stored. It is optional because +sometimes complete cookie information is not available, such as in the case of parsing +errors. */ - groupName: string; + cookie?: Cookie; } /** - * An object providing the result of a network resource load. + * A cookie should have been blocked by 3PCD but is exempted and stored from a response with the +corresponding reason. A cookie could only have at most one exemption reason. */ - export interface LoadNetworkResourcePageResult { - success: boolean; + export interface ExemptedSetCookieWithReason { /** - * Optional values used for error reporting. + * The reason the cookie was exempted. */ - netError?: number; - netErrorName?: string; - httpStatusCode?: number; + exemptionReason: CookieExemptionReason; /** - * If successful, one of the following two fields holds the result. + * The string representing this individual cookie as it would appear in the header. */ - stream?: IO.StreamHandle; + cookieLine: string; /** - * Response headers. + * The cookie object representing the cookie. */ - headers?: Network.Headers; + cookie: Cookie; } /** - * An options object that may be extended later to better support CORS, -CORB and streaming. + * A cookie associated with the request which may or may not be sent with it. +Includes the cookies itself and reasons for blocking or exemption. */ - export interface LoadNetworkResourceOptions { - disableCache: boolean; - includeCredentials: boolean; + export interface AssociatedCookie { + /** + * The cookie object representing the cookie which was not sent. + */ + cookie: Cookie; + /** + * The reason(s) the cookie was blocked. If empty means the cookie is included. + */ + blockedReasons: CookieBlockedReason[]; + /** + * The reason the cookie should have been blocked by 3PCD but is exempted. A cookie could +only have at most one exemption reason. + */ + exemptionReason?: CookieExemptionReason; } - /** - * Fired when data chunk was received over the network. + * Cookie parameter object */ - export type dataReceivedPayload = { + export interface CookieParam { /** - * Request identifier. + * Cookie name. */ - requestId: RequestId; + name: string; /** - * Timestamp. + * Cookie value. */ - timestamp: MonotonicTime; + value: string; /** - * Data chunk length. + * The request-URI to associate with the setting of the cookie. This value can affect the +default domain, path, source port, and source scheme values of the created cookie. */ - dataLength: number; + url?: string; /** - * Actual bytes received (might be less than dataLength for compressed encodings). + * Cookie domain. */ - encodedDataLength: number; + domain?: string; /** - * Data that was received. + * Cookie path. */ - data?: binary; - } - /** - * Fired when EventSource message is received. - */ - export type eventSourceMessageReceivedPayload = { + path?: string; /** - * Request identifier. + * True if cookie is secure. */ - requestId: RequestId; + secure?: boolean; /** - * Timestamp. + * True if cookie is http-only. */ - timestamp: MonotonicTime; + httpOnly?: boolean; /** - * Message type. + * Cookie SameSite type. */ - eventName: string; + sameSite?: CookieSameSite; /** - * Message identifier. + * Cookie expiration date, session cookie if not set */ - eventId: string; + expires?: TimeSinceEpoch; /** - * Message content. + * Cookie Priority. */ - data: string; - } - /** - * Fired when HTTP request has failed to load. - */ - export type loadingFailedPayload = { + priority?: CookiePriority; /** - * Request identifier. + * True if cookie is SameParty. */ - requestId: RequestId; + sameParty?: boolean; /** - * Timestamp. + * Cookie source scheme type. */ - timestamp: MonotonicTime; + sourceScheme?: CookieSourceScheme; /** - * Resource type. + * Cookie source port. Valid values are {-1, [1, 65535]}, -1 indicates an unspecified port. +An unspecified port value allows protocol clients to emulate legacy cookie scope for the port. +This is a temporary ability and it will be removed in the future. */ - type: ResourceType; + sourcePort?: number; /** - * Error message. List of network errors: https://cs.chromium.org/chromium/src/net/base/net_error_list.h + * Cookie partition key. If not set, the cookie will be set as not partitioned. */ - errorText: string; + partitionKey?: CookiePartitionKey; + } + /** + * Authorization challenge for HTTP status code 401 or 407. + */ + export interface AuthChallenge { /** - * True if loading was canceled. + * Source of the authentication challenge. */ - canceled?: boolean; + source?: "Server"|"Proxy"; /** - * The reason why loading was blocked, if any. + * Origin of the challenger. */ - blockedReason?: BlockedReason; + origin: string; /** - * The reason why loading was blocked by CORS, if any. + * The authentication scheme used, such as basic or digest */ - corsErrorStatus?: CorsErrorStatus; + scheme: string; + /** + * The realm of the challenge. May be empty. + */ + realm: string; } /** - * Fired when HTTP request has finished loading. + * Response to an AuthChallenge. */ - export type loadingFinishedPayload = { + export interface AuthChallengeResponse { /** - * Request identifier. + * The decision on what to do in response to the authorization challenge. Default means +deferring to the default behavior of the net stack, which will likely either the Cancel +authentication or display a popup dialog box. */ - requestId: RequestId; + response: "Default"|"CancelAuth"|"ProvideCredentials"; /** - * Timestamp. + * The username to provide, possibly empty. Should only be set if response is +ProvideCredentials. */ - timestamp: MonotonicTime; + username?: string; /** - * Total number of bytes received for this request. + * The password to provide, possibly empty. Should only be set if response is +ProvideCredentials. */ - encodedDataLength: number; + password?: string; } /** - * Details of an intercepted HTTP request, which must be either allowed, blocked, modified or -mocked. -Deprecated, use Fetch.requestPaused instead. + * Stages of the interception to begin intercepting. Request will intercept before the request is +sent. Response will intercept after the response is received. */ - export type requestInterceptedPayload = { + export type InterceptionStage = "Request"|"HeadersReceived"; + /** + * Request pattern for interception. + */ + export interface RequestPattern { /** - * Each request the page makes will have a unique id, however if any redirects are encountered -while processing that fetch, they will be reported with the same id as the original fetch. -Likewise if HTTP authentication is needed then the same fetch id will be used. + * Wildcards (`'*'` -> zero or more, `'?'` -> exactly one) are allowed. Escape character is +backslash. Omitting is equivalent to `"*"`. */ - interceptionId: InterceptionId; - request: Request; + urlPattern?: string; /** - * The id of the frame that initiated the request. + * If set, only requests for matching resource types will be intercepted. */ - frameId: Page.FrameId; + resourceType?: ResourceType; /** - * How the requested resource will be used. + * Stage at which to begin intercepting requests. Default is Request. */ - resourceType: ResourceType; + interceptionStage?: InterceptionStage; + } + /** + * Information about a signed exchange signature. +https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#rfc.section.3.1 + */ + export interface SignedExchangeSignature { /** - * Whether this is a navigation request, which can abort the navigation completely. + * Signed exchange signature label. */ - isNavigationRequest: boolean; + label: string; /** - * Set if the request is a navigation that will result in a download. -Only present after response is received from the server (i.e. HeadersReceived stage). + * The hex string of signed exchange signature. */ - isDownload?: boolean; + signature: string; /** - * Redirect location, only sent if a redirect was intercepted. + * Signed exchange signature integrity. */ - redirectUrl?: string; + integrity: string; /** - * Details of the Authorization Challenge encountered. If this is set then -continueInterceptedRequest must contain an authChallengeResponse. + * Signed exchange signature cert Url. */ - authChallenge?: AuthChallenge; + certUrl?: string; /** - * Response error if intercepted at response stage or if redirect occurred while intercepting -request. + * The hex string of signed exchange signature cert sha256. */ - responseErrorReason?: ErrorReason; + certSha256?: string; /** - * Response code if intercepted at response stage or if redirect occurred while intercepting -request or auth retry occurred. + * Signed exchange signature validity Url. */ - responseStatusCode?: number; + validityUrl: string; /** - * Response headers if intercepted at the response stage or if redirect occurred while -intercepting request or auth retry occurred. + * Signed exchange signature date. */ - responseHeaders?: Headers; + date: number; /** - * If the intercepted request had a corresponding requestWillBeSent event fired for it, then -this requestId will be the same as the requestId present in the requestWillBeSent event. + * Signed exchange signature expires. */ - requestId?: RequestId; - } - /** - * Fired if request ended up loading from cache. - */ - export type requestServedFromCachePayload = { + expires: number; /** - * Request identifier. + * The encoded certificates. */ - requestId: RequestId; + certificates?: string[]; } /** - * Fired when page is about to send HTTP request. + * Information about a signed exchange header. +https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#cbor-representation */ - export type requestWillBeSentPayload = { + export interface SignedExchangeHeader { /** - * Request identifier. + * Signed exchange request URL. */ - requestId: RequestId; + requestUrl: string; /** - * Loader identifier. Empty string if the request is fetched from worker. + * Signed exchange response code. */ - loaderId: LoaderId; + responseCode: number; /** - * URL of the document this request is loaded for. + * Signed exchange response headers. */ - documentURL: string; + responseHeaders: Headers; /** - * Request data. + * Signed exchange response signature. */ - request: Request; + signatures: SignedExchangeSignature[]; /** - * Timestamp. + * Signed exchange header integrity hash in the form of `sha256-`. */ - timestamp: MonotonicTime; + headerIntegrity: string; + } + /** + * Field type for a signed exchange related error. + */ + export type SignedExchangeErrorField = "signatureSig"|"signatureIntegrity"|"signatureCertUrl"|"signatureCertSha256"|"signatureValidityUrl"|"signatureTimestamps"; + /** + * Information about a signed exchange response. + */ + export interface SignedExchangeError { /** - * Timestamp. + * Error message. */ - wallTime: TimeSinceEpoch; + message: string; /** - * Request initiator. + * The index of the signature which caused the error. */ - initiator: Initiator; + signatureIndex?: number; /** - * In the case that redirectResponse is populated, this flag indicates whether -requestWillBeSentExtraInfo and responseReceivedExtraInfo events will be or were emitted -for the request which was just redirected. + * The field which caused the error. */ - redirectHasExtraInfo: boolean; + errorField?: SignedExchangeErrorField; + } + /** + * Information about a signed exchange response. + */ + export interface SignedExchangeInfo { /** - * Redirect response data. + * The outer response of signed HTTP exchange which was received from network. */ - redirectResponse?: Response; + outerResponse: Response; /** - * Type of this resource. + * Whether network response for the signed exchange was accompanied by +extra headers. */ - type?: ResourceType; + hasExtraInfo: boolean; /** - * Frame identifier. + * Information about the signed exchange header. */ - frameId?: Page.FrameId; + header?: SignedExchangeHeader; /** - * Whether the request is initiated by a user gesture. Defaults to false. + * Security details for the signed exchange header. */ - hasUserGesture?: boolean; + securityDetails?: SecurityDetails; + /** + * Errors occurred while handling the signed exchange. + */ + errors?: SignedExchangeError[]; } /** - * Fired when resource loading priority is changed + * List of content encodings supported by the backend. */ - export type resourceChangedPriorityPayload = { + export type ContentEncoding = "deflate"|"gzip"|"br"|"zstd"; + export type DirectSocketDnsQueryType = "ipv4"|"ipv6"; + export interface DirectTCPSocketOptions { /** - * Request identifier. + * TCP_NODELAY option */ - requestId: RequestId; + noDelay: boolean; /** - * New priority + * Expected to be unsigned integer. */ - newPriority: ResourcePriority; + keepAliveDelay?: number; /** - * Timestamp. + * Expected to be unsigned integer. */ - timestamp: MonotonicTime; + sendBufferSize?: number; + /** + * Expected to be unsigned integer. + */ + receiveBufferSize?: number; + dnsQueryType?: DirectSocketDnsQueryType; } - /** - * Fired when a signed exchange was received over the network - */ - export type signedExchangeReceivedPayload = { + export interface DirectUDPSocketOptions { + remoteAddr?: string; /** - * Request identifier. + * Unsigned int 16. */ - requestId: RequestId; + remotePort?: number; + localAddr?: string; /** - * Information about the signed exchange response. + * Unsigned int 16. */ - info: SignedExchangeInfo; + localPort?: number; + dnsQueryType?: DirectSocketDnsQueryType; + /** + * Expected to be unsigned integer. + */ + sendBufferSize?: number; + /** + * Expected to be unsigned integer. + */ + receiveBufferSize?: number; } - /** - * Fired when HTTP response is available. - */ - export type responseReceivedPayload = { + export interface DirectUDPMessage { + data: binary; /** - * Request identifier. + * Null for connected mode. */ - requestId: RequestId; + remoteAddr?: string; /** - * Loader identifier. Empty string if the request is fetched from worker. + * Null for connected mode. +Expected to be unsigned integer. */ - loaderId: LoaderId; + remotePort?: number; + } + export type PrivateNetworkRequestPolicy = "Allow"|"BlockFromInsecureToMorePrivate"|"WarnFromInsecureToMorePrivate"|"PreflightBlock"|"PreflightWarn"|"PermissionBlock"|"PermissionWarn"; + export type IPAddressSpace = "Loopback"|"Local"|"Public"|"Unknown"; + export interface ConnectTiming { /** - * Timestamp. + * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in +milliseconds relatively to this requestTime. Matches ResourceTiming's requestTime for +the same request (but not for redirected requests). */ - timestamp: MonotonicTime; + requestTime: number; + } + export interface ClientSecurityState { + initiatorIsSecureContext: boolean; + initiatorIPAddressSpace: IPAddressSpace; + privateNetworkRequestPolicy: PrivateNetworkRequestPolicy; + } + export type CrossOriginOpenerPolicyValue = "SameOrigin"|"SameOriginAllowPopups"|"RestrictProperties"|"UnsafeNone"|"SameOriginPlusCoep"|"RestrictPropertiesPlusCoep"|"NoopenerAllowPopups"; + export interface CrossOriginOpenerPolicyStatus { + value: CrossOriginOpenerPolicyValue; + reportOnlyValue: CrossOriginOpenerPolicyValue; + reportingEndpoint?: string; + reportOnlyReportingEndpoint?: string; + } + export type CrossOriginEmbedderPolicyValue = "None"|"Credentialless"|"RequireCorp"; + export interface CrossOriginEmbedderPolicyStatus { + value: CrossOriginEmbedderPolicyValue; + reportOnlyValue: CrossOriginEmbedderPolicyValue; + reportingEndpoint?: string; + reportOnlyReportingEndpoint?: string; + } + export type ContentSecurityPolicySource = "HTTP"|"Meta"; + export interface ContentSecurityPolicyStatus { + effectiveDirectives: string; + isEnforced: boolean; + source: ContentSecurityPolicySource; + } + export interface SecurityIsolationStatus { + coop?: CrossOriginOpenerPolicyStatus; + coep?: CrossOriginEmbedderPolicyStatus; + csp?: ContentSecurityPolicyStatus[]; + } + /** + * The status of a Reporting API report. + */ + export type ReportStatus = "Queued"|"Pending"|"MarkedForRemoval"|"Success"; + export type ReportId = string; + /** + * An object representing a report generated by the Reporting API. + */ + export interface ReportingApiReport { + id: ReportId; /** - * Resource type. + * The URL of the document that triggered the report. */ - type: ResourceType; + initiatorUrl: string; /** - * Response data. + * The name of the endpoint group that should be used to deliver the report. */ - response: Response; + destination: string; /** - * Indicates whether requestWillBeSentExtraInfo and responseReceivedExtraInfo events will be -or were emitted for this request. + * The type of the report (specifies the set of data that is contained in the report body). */ - hasExtraInfo: boolean; + type: string; /** - * Frame identifier. + * When the report was generated. */ - frameId?: Page.FrameId; + timestamp: Network.TimeSinceEpoch; + /** + * How many uploads deep the related request was. + */ + depth: number; + /** + * The number of delivery attempts made so far, not including an active attempt. + */ + completedAttempts: number; + body: { [key: string]: string }; + status: ReportStatus; } - /** - * Fired when WebSocket is closed. - */ - export type webSocketClosedPayload = { + export interface ReportingApiEndpoint { /** - * Request identifier. + * The URL of the endpoint to which reports may be delivered. */ - requestId: RequestId; + url: string; /** - * Timestamp. + * Name of the endpoint group. */ - timestamp: MonotonicTime; + groupName: string; } /** - * Fired upon WebSocket creation. + * An object providing the result of a network resource load. */ - export type webSocketCreatedPayload = { + export interface LoadNetworkResourcePageResult { + success: boolean; /** - * Request identifier. + * Optional values used for error reporting. */ - requestId: RequestId; + netError?: number; + netErrorName?: string; + httpStatusCode?: number; /** - * WebSocket request URL. + * If successful, one of the following two fields holds the result. */ - url: string; + stream?: IO.StreamHandle; /** - * Request initiator. + * Response headers. */ - initiator?: Initiator; + headers?: Network.Headers; } /** - * Fired when WebSocket message error occurs. + * An options object that may be extended later to better support CORS, +CORB and streaming. */ - export type webSocketFrameErrorPayload = { + export interface LoadNetworkResourceOptions { + disableCache: boolean; + includeCredentials: boolean; + } + + /** + * Fired when data chunk was received over the network. + */ + export type dataReceivedPayload = { /** * Request identifier. */ @@ -10220,14 +11046,22 @@ or were emitted for this request. */ timestamp: MonotonicTime; /** - * WebSocket error message. + * Data chunk length. */ - errorMessage: string; + dataLength: number; + /** + * Actual bytes received (might be less than dataLength for compressed encodings). + */ + encodedDataLength: number; + /** + * Data that was received. + */ + data?: binary; } /** - * Fired when WebSocket message is received. + * Fired when EventSource message is received. */ - export type webSocketFrameReceivedPayload = { + export type eventSourceMessageReceivedPayload = { /** * Request identifier. */ @@ -10237,14 +11071,22 @@ or were emitted for this request. */ timestamp: MonotonicTime; /** - * WebSocket response data. + * Message type. */ - response: WebSocketFrame; + eventName: string; + /** + * Message identifier. + */ + eventId: string; + /** + * Message content. + */ + data: string; } /** - * Fired when WebSocket message is sent. + * Fired when HTTP request has failed to load. */ - export type webSocketFrameSentPayload = { + export type loadingFailedPayload = { /** * Request identifier. */ @@ -10254,14 +11096,30 @@ or were emitted for this request. */ timestamp: MonotonicTime; /** - * WebSocket response data. + * Resource type. */ - response: WebSocketFrame; + type: ResourceType; + /** + * Error message. List of network errors: https://cs.chromium.org/chromium/src/net/base/net_error_list.h + */ + errorText: string; + /** + * True if loading was canceled. + */ + canceled?: boolean; + /** + * The reason why loading was blocked, if any. + */ + blockedReason?: BlockedReason; + /** + * The reason why loading was blocked by CORS, if any. + */ + corsErrorStatus?: CorsErrorStatus; } /** - * Fired when WebSocket handshake response becomes available. + * Fired when HTTP request has finished loading. */ - export type webSocketHandshakeResponseReceivedPayload = { + export type loadingFinishedPayload = { /** * Request identifier. */ @@ -10271,111 +11129,397 @@ or were emitted for this request. */ timestamp: MonotonicTime; /** - * WebSocket response data. + * Total number of bytes received for this request. */ - response: WebSocketResponse; + encodedDataLength: number; } /** - * Fired when WebSocket is about to initiate handshake. + * Details of an intercepted HTTP request, which must be either allowed, blocked, modified or +mocked. +Deprecated, use Fetch.requestPaused instead. */ - export type webSocketWillSendHandshakeRequestPayload = { + export type requestInterceptedPayload = { + /** + * Each request the page makes will have a unique id, however if any redirects are encountered +while processing that fetch, they will be reported with the same id as the original fetch. +Likewise if HTTP authentication is needed then the same fetch id will be used. + */ + interceptionId: InterceptionId; + request: Request; + /** + * The id of the frame that initiated the request. + */ + frameId: Page.FrameId; + /** + * How the requested resource will be used. + */ + resourceType: ResourceType; + /** + * Whether this is a navigation request, which can abort the navigation completely. + */ + isNavigationRequest: boolean; + /** + * Set if the request is a navigation that will result in a download. +Only present after response is received from the server (i.e. HeadersReceived stage). + */ + isDownload?: boolean; + /** + * Redirect location, only sent if a redirect was intercepted. + */ + redirectUrl?: string; + /** + * Details of the Authorization Challenge encountered. If this is set then +continueInterceptedRequest must contain an authChallengeResponse. + */ + authChallenge?: AuthChallenge; + /** + * Response error if intercepted at response stage or if redirect occurred while intercepting +request. + */ + responseErrorReason?: ErrorReason; + /** + * Response code if intercepted at response stage or if redirect occurred while intercepting +request or auth retry occurred. + */ + responseStatusCode?: number; + /** + * Response headers if intercepted at the response stage or if redirect occurred while +intercepting request or auth retry occurred. + */ + responseHeaders?: Headers; + /** + * If the intercepted request had a corresponding requestWillBeSent event fired for it, then +this requestId will be the same as the requestId present in the requestWillBeSent event. + */ + requestId?: RequestId; + } + /** + * Fired if request ended up loading from cache. + */ + export type requestServedFromCachePayload = { + /** + * Request identifier. + */ + requestId: RequestId; + } + /** + * Fired when page is about to send HTTP request. + */ + export type requestWillBeSentPayload = { /** * Request identifier. */ requestId: RequestId; + /** + * Loader identifier. Empty string if the request is fetched from worker. + */ + loaderId: LoaderId; + /** + * URL of the document this request is loaded for. + */ + documentURL: string; + /** + * Request data. + */ + request: Request; /** * Timestamp. */ timestamp: MonotonicTime; /** - * UTC Timestamp. + * Timestamp. */ wallTime: TimeSinceEpoch; /** - * WebSocket request data. + * Request initiator. */ - request: WebSocketRequest; + initiator: Initiator; + /** + * In the case that redirectResponse is populated, this flag indicates whether +requestWillBeSentExtraInfo and responseReceivedExtraInfo events will be or were emitted +for the request which was just redirected. + */ + redirectHasExtraInfo: boolean; + /** + * Redirect response data. + */ + redirectResponse?: Response; + /** + * Type of this resource. + */ + type?: ResourceType; + /** + * Frame identifier. + */ + frameId?: Page.FrameId; + /** + * Whether the request is initiated by a user gesture. Defaults to false. + */ + hasUserGesture?: boolean; } /** - * Fired upon WebTransport creation. + * Fired when resource loading priority is changed */ - export type webTransportCreatedPayload = { + export type resourceChangedPriorityPayload = { /** - * WebTransport identifier. + * Request identifier. */ - transportId: RequestId; + requestId: RequestId; /** - * WebTransport request URL. + * New priority */ - url: string; + newPriority: ResourcePriority; /** * Timestamp. */ timestamp: MonotonicTime; + } + /** + * Fired when a signed exchange was received over the network + */ + export type signedExchangeReceivedPayload = { /** - * Request initiator. + * Request identifier. */ - initiator?: Initiator; + requestId: RequestId; + /** + * Information about the signed exchange response. + */ + info: SignedExchangeInfo; } /** - * Fired when WebTransport handshake is finished. + * Fired when HTTP response is available. */ - export type webTransportConnectionEstablishedPayload = { + export type responseReceivedPayload = { /** - * WebTransport identifier. + * Request identifier. */ - transportId: RequestId; + requestId: RequestId; + /** + * Loader identifier. Empty string if the request is fetched from worker. + */ + loaderId: LoaderId; /** * Timestamp. */ timestamp: MonotonicTime; + /** + * Resource type. + */ + type: ResourceType; + /** + * Response data. + */ + response: Response; + /** + * Indicates whether requestWillBeSentExtraInfo and responseReceivedExtraInfo events will be +or were emitted for this request. + */ + hasExtraInfo: boolean; + /** + * Frame identifier. + */ + frameId?: Page.FrameId; } /** - * Fired when WebTransport is disposed. + * Fired when WebSocket is closed. */ - export type webTransportClosedPayload = { + export type webSocketClosedPayload = { /** - * WebTransport identifier. + * Request identifier. */ - transportId: RequestId; + requestId: RequestId; /** * Timestamp. */ timestamp: MonotonicTime; } /** - * Fired upon direct_socket.TCPSocket creation. + * Fired upon WebSocket creation. */ - export type directTCPSocketCreatedPayload = { - identifier: RequestId; - remoteAddr: string; + export type webSocketCreatedPayload = { /** - * Unsigned int 16. + * Request identifier. + */ + requestId: RequestId; + /** + * WebSocket request URL. + */ + url: string; + /** + * Request initiator. */ - remotePort: number; - options: DirectTCPSocketOptions; - timestamp: MonotonicTime; initiator?: Initiator; } /** - * Fired when direct_socket.TCPSocket connection is opened. + * Fired when WebSocket message error occurs. */ - export type directTCPSocketOpenedPayload = { - identifier: RequestId; - remoteAddr: string; + export type webSocketFrameErrorPayload = { /** - * Expected to be unsigned integer. + * Request identifier. + */ + requestId: RequestId; + /** + * Timestamp. */ - remotePort: number; timestamp: MonotonicTime; - localAddr?: string; /** - * Expected to be unsigned integer. + * WebSocket error message. */ - localPort?: number; + errorMessage: string; } /** - * Fired when direct_socket.TCPSocket is aborted. + * Fired when WebSocket message is received. + */ + export type webSocketFrameReceivedPayload = { + /** + * Request identifier. + */ + requestId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + /** + * WebSocket response data. + */ + response: WebSocketFrame; + } + /** + * Fired when WebSocket message is sent. + */ + export type webSocketFrameSentPayload = { + /** + * Request identifier. + */ + requestId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + /** + * WebSocket response data. + */ + response: WebSocketFrame; + } + /** + * Fired when WebSocket handshake response becomes available. + */ + export type webSocketHandshakeResponseReceivedPayload = { + /** + * Request identifier. + */ + requestId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + /** + * WebSocket response data. + */ + response: WebSocketResponse; + } + /** + * Fired when WebSocket is about to initiate handshake. + */ + export type webSocketWillSendHandshakeRequestPayload = { + /** + * Request identifier. + */ + requestId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + /** + * UTC Timestamp. + */ + wallTime: TimeSinceEpoch; + /** + * WebSocket request data. + */ + request: WebSocketRequest; + } + /** + * Fired upon WebTransport creation. + */ + export type webTransportCreatedPayload = { + /** + * WebTransport identifier. + */ + transportId: RequestId; + /** + * WebTransport request URL. + */ + url: string; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + /** + * Request initiator. + */ + initiator?: Initiator; + } + /** + * Fired when WebTransport handshake is finished. + */ + export type webTransportConnectionEstablishedPayload = { + /** + * WebTransport identifier. + */ + transportId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + } + /** + * Fired when WebTransport is disposed. + */ + export type webTransportClosedPayload = { + /** + * WebTransport identifier. + */ + transportId: RequestId; + /** + * Timestamp. + */ + timestamp: MonotonicTime; + } + /** + * Fired upon direct_socket.TCPSocket creation. + */ + export type directTCPSocketCreatedPayload = { + identifier: RequestId; + remoteAddr: string; + /** + * Unsigned int 16. + */ + remotePort: number; + options: DirectTCPSocketOptions; + timestamp: MonotonicTime; + initiator?: Initiator; + } + /** + * Fired when direct_socket.TCPSocket connection is opened. + */ + export type directTCPSocketOpenedPayload = { + identifier: RequestId; + remoteAddr: string; + /** + * Expected to be unsigned integer. + */ + remotePort: number; + timestamp: MonotonicTime; + localAddr?: string; + /** + * Expected to be unsigned integer. + */ + localPort?: number; + } + /** + * Fired when direct_socket.TCPSocket is aborted. */ export type directTCPSocketAbortedPayload = { identifier: RequestId; @@ -10688,6 +11832,18 @@ And after 'enableReportingApi' for all existing reports. endpoints: ReportingApiEndpoint[]; } + /** + * Returns enum representing if IP Proxy of requests is available +or reason it is not active. + */ + export type getIPProtectionProxyStatusParameters = { + } + export type getIPProtectionProxyStatusReturnValue = { + /** + * Whether IP proxy is available + */ + status: IpProxyStatus; + } /** * Sets a list of content encodings that will be accepted. Empty list means no encoding is accepted. */ @@ -10894,6 +12050,12 @@ all partition key attributes match the cookie partition key attribute. * Whether DirectSocket chunk send/receive events should be reported. */ reportDirectSocketTraffic?: boolean; + /** + * Enable storing response bodies outside of renderer, so that these survive +a cross-process navigation. Requires maxTotalBufferSize to be set. +Currently defaults to false. + */ + enableDurableMessages?: boolean; } export type enableReturnValue = { } @@ -11879,6 +13041,9 @@ objectId must be specified. } /** * Highlights given rectangle. Coordinates are absolute with respect to the main frame viewport. +Issue: the method does not handle device pixel ratio (DPR) correctly. +The coordinates currently have to be adjusted by the client +if DPR is not 1 (see crbug.com/437807128). */ export type highlightRectParameters = { /** @@ -12124,53 +13289,223 @@ Backend then generates 'inspectNodeRequested' event upon element selection. } /** - * Actions and events related to the inspected page belong to the page domain. + * This domain allows interacting with the browser to control PWAs. */ - export module Page { + export module PWA { /** - * Unique frame identifier. + * The following types are the replica of +https://crsrc.org/c/chrome/browser/web_applications/proto/web_app_os_integration_state.proto;drc=9910d3be894c8f142c977ba1023f30a656bc13fc;l=67 */ - export type FrameId = string; + export interface FileHandlerAccept { + /** + * New name of the mimetype according to +https://www.iana.org/assignments/media-types/media-types.xhtml + */ + mediaType: string; + fileExtensions: string[]; + } + export interface FileHandler { + action: string; + accepts: FileHandlerAccept[]; + displayName: string; + } /** - * Indicates whether a frame has been identified as an ad. + * If user prefers opening the app in browser or an app window. */ - export type AdFrameType = "none"|"child"|"root"; - export type AdFrameExplanation = "ParentIsAd"|"CreatedByAdScript"|"MatchedBlockingRule"; + export type DisplayMode = "standalone"|"browser"; + + /** - * Indicates whether a frame has been identified as an ad and why. + * Returns the following OS state for the given manifest id. */ - export interface AdFrameStatus { - adFrameType: AdFrameType; - explanations?: AdFrameExplanation[]; + export type getOsAppStateParameters = { + /** + * The id from the webapp's manifest file, commonly it's the url of the +site installing the webapp. See +https://web.dev/learn/pwa/web-app-manifest. + */ + manifestId: string; + } + export type getOsAppStateReturnValue = { + badgeCount: number; + fileHandlers: FileHandler[]; } /** - * Identifies the script which caused a script or frame to be labelled as an -ad. + * Installs the given manifest identity, optionally using the given installUrlOrBundleUrl + +IWA-specific install description: +manifestId corresponds to isolated-app:// + web_package::SignedWebBundleId + +File installation mode: +The installUrlOrBundleUrl can be either file:// or http(s):// pointing +to a signed web bundle (.swbn). In this case SignedWebBundleId must correspond to +The .swbn file's signing key. + +Dev proxy installation mode: +installUrlOrBundleUrl must be http(s):// that serves dev mode IWA. +web_package::SignedWebBundleId must be of type dev proxy. + +The advantage of dev proxy mode is that all changes to IWA +automatically will be reflected in the running app without +reinstallation. + +To generate bundle id for proxy mode: +1. Generate 32 random bytes. +2. Add a specific suffix 0x00 at the end. +3. Encode the entire sequence using Base32 without padding. + +If Chrome is not in IWA dev +mode, the installation will fail, regardless of the state of the allowlist. */ - export interface AdScriptId { - /** - * Script Id of the script which caused a script or frame to be labelled as -an ad. - */ - scriptId: Runtime.ScriptId; + export type installParameters = { + manifestId: string; /** - * Id of scriptId's debugger. + * The location of the app or bundle overriding the one derived from the +manifestId. */ - debuggerId: Runtime.UniqueDebuggerId; + installUrlOrBundleUrl?: string; + } + export type installReturnValue = { } /** - * Encapsulates the script ancestry and the root script filterlist rule that -caused the frame to be labelled as an ad. Only created when `ancestryChain` -is not empty. + * Uninstalls the given manifest_id and closes any opened app windows. */ - export interface AdScriptAncestry { - /** - * A chain of `AdScriptId`s representing the ancestry of an ad script that -led to the creation of a frame. The chain is ordered from the script -itself (lower level) up to its root ancestor that was flagged by -filterlist. - */ - ancestryChain: AdScriptId[]; + export type uninstallParameters = { + manifestId: string; + } + export type uninstallReturnValue = { + } + /** + * Launches the installed web app, or an url in the same web app instead of the +default start url if it is provided. Returns a page Target.TargetID which +can be used to attach to via Target.attachToTarget or similar APIs. + */ + export type launchParameters = { + manifestId: string; + url?: string; + } + export type launchReturnValue = { + /** + * ID of the tab target created as a result. + */ + targetId: Target.TargetID; + } + /** + * Opens one or more local files from an installed web app identified by its +manifestId. The web app needs to have file handlers registered to process +the files. The API returns one or more page Target.TargetIDs which can be +used to attach to via Target.attachToTarget or similar APIs. +If some files in the parameters cannot be handled by the web app, they will +be ignored. If none of the files can be handled, this API returns an error. +If no files are provided as the parameter, this API also returns an error. + +According to the definition of the file handlers in the manifest file, one +Target.TargetID may represent a page handling one or more files. The order +of the returned Target.TargetIDs is not guaranteed. + +TODO(crbug.com/339454034): Check the existences of the input files. + */ + export type launchFilesInAppParameters = { + manifestId: string; + files: string[]; + } + export type launchFilesInAppReturnValue = { + /** + * IDs of the tab targets created as the result. + */ + targetIds: Target.TargetID[]; + } + /** + * Opens the current page in its web app identified by the manifest id, needs +to be called on a page target. This function returns immediately without +waiting for the app to finish loading. + */ + export type openCurrentPageInAppParameters = { + manifestId: string; + } + export type openCurrentPageInAppReturnValue = { + } + /** + * Changes user settings of the web app identified by its manifestId. If the +app was not installed, this command returns an error. Unset parameters will +be ignored; unrecognized values will cause an error. + +Unlike the ones defined in the manifest files of the web apps, these +settings are provided by the browser and controlled by the users, they +impact the way the browser handling the web apps. + +See the comment of each parameter. + */ + export type changeAppUserSettingsParameters = { + manifestId: string; + /** + * If user allows the links clicked on by the user in the app's scope, or +extended scope if the manifest has scope extensions and the flags +`DesktopPWAsLinkCapturingWithScopeExtensions` and +`WebAppEnableScopeExtensions` are enabled. + +Note, the API does not support resetting the linkCapturing to the +initial value, uninstalling and installing the web app again will reset +it. + +TODO(crbug.com/339453269): Setting this value on ChromeOS is not +supported yet. + */ + linkCapturing?: boolean; + displayMode?: DisplayMode; + } + export type changeAppUserSettingsReturnValue = { + } + } + + /** + * Actions and events related to the inspected page belong to the page domain. + */ + export module Page { + /** + * Unique frame identifier. + */ + export type FrameId = string; + /** + * Indicates whether a frame has been identified as an ad. + */ + export type AdFrameType = "none"|"child"|"root"; + export type AdFrameExplanation = "ParentIsAd"|"CreatedByAdScript"|"MatchedBlockingRule"; + /** + * Indicates whether a frame has been identified as an ad and why. + */ + export interface AdFrameStatus { + adFrameType: AdFrameType; + explanations?: AdFrameExplanation[]; + } + /** + * Identifies the script which caused a script or frame to be labelled as an +ad. + */ + export interface AdScriptId { + /** + * Script Id of the script which caused a script or frame to be labelled as +an ad. + */ + scriptId: Runtime.ScriptId; + /** + * Id of scriptId's debugger. + */ + debuggerId: Runtime.UniqueDebuggerId; + } + /** + * Encapsulates the script ancestry and the root script filterlist rule that +caused the frame to be labelled as an ad. Only created when `ancestryChain` +is not empty. + */ + export interface AdScriptAncestry { + /** + * A chain of `AdScriptId`s representing the ancestry of an ad script that +led to the creation of a frame. The chain is ordered from the script +itself (lower level) up to its root ancestor that was flagged by +filterlist. + */ + ancestryChain: AdScriptId[]; /** * The filterlist rule that caused the root (last) script in `ancestryChain` to be ad-tagged. Only populated if the rule is @@ -12192,7 +13527,7 @@ available. in services/network/public/cpp/permissions_policy/permissions_policy_features.json5. LINT.IfChange(PermissionsPolicyFeature) */ - export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"aria-notify"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-high-entropy-values"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"controlled-frame"|"cross-origin-isolated"|"deferred-fetch"|"deferred-fetch-minimal"|"device-attributes"|"digital-credentials-get"|"direct-sockets"|"direct-sockets-private"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"fenced-unpartitioned-storage-read"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"language-detector"|"language-model"|"local-fonts"|"local-network-access"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"on-device-speech-recognition"|"otp-credentials"|"payment"|"picture-in-picture"|"popins"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"record-ad-auction-events"|"rewriter"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"summarizer"|"sync-xhr"|"translator"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-app-installation"|"web-printing"|"web-share"|"window-management"|"writer"|"xr-spatial-tracking"; + export type PermissionsPolicyFeature = "accelerometer"|"all-screens-capture"|"ambient-light-sensor"|"aria-notify"|"attribution-reporting"|"autoplay"|"bluetooth"|"browsing-topics"|"camera"|"captured-surface-control"|"ch-dpr"|"ch-device-memory"|"ch-downlink"|"ch-ect"|"ch-prefers-color-scheme"|"ch-prefers-reduced-motion"|"ch-prefers-reduced-transparency"|"ch-rtt"|"ch-save-data"|"ch-ua"|"ch-ua-arch"|"ch-ua-bitness"|"ch-ua-high-entropy-values"|"ch-ua-platform"|"ch-ua-model"|"ch-ua-mobile"|"ch-ua-form-factors"|"ch-ua-full-version"|"ch-ua-full-version-list"|"ch-ua-platform-version"|"ch-ua-wow64"|"ch-viewport-height"|"ch-viewport-width"|"ch-width"|"clipboard-read"|"clipboard-write"|"compute-pressure"|"controlled-frame"|"cross-origin-isolated"|"deferred-fetch"|"deferred-fetch-minimal"|"device-attributes"|"digital-credentials-create"|"digital-credentials-get"|"direct-sockets"|"direct-sockets-private"|"display-capture"|"document-domain"|"encrypted-media"|"execution-while-out-of-viewport"|"execution-while-not-rendered"|"fenced-unpartitioned-storage-read"|"focus-without-user-activation"|"fullscreen"|"frobulate"|"gamepad"|"geolocation"|"gyroscope"|"hid"|"identity-credentials-get"|"idle-detection"|"interest-cohort"|"join-ad-interest-group"|"keyboard-map"|"language-detector"|"language-model"|"local-fonts"|"local-network-access"|"magnetometer"|"media-playback-while-not-visible"|"microphone"|"midi"|"on-device-speech-recognition"|"otp-credentials"|"payment"|"picture-in-picture"|"popins"|"private-aggregation"|"private-state-token-issuance"|"private-state-token-redemption"|"publickey-credentials-create"|"publickey-credentials-get"|"record-ad-auction-events"|"rewriter"|"run-ad-auction"|"screen-wake-lock"|"serial"|"shared-autofill"|"shared-storage"|"shared-storage-select-url"|"smart-card"|"speaker-selection"|"storage-access"|"sub-apps"|"summarizer"|"sync-xhr"|"translator"|"unload"|"usb"|"usb-unrestricted"|"vertical-scroll"|"web-app-installation"|"web-printing"|"web-share"|"window-management"|"writer"|"xr-spatial-tracking"; /** * Reason for a permissions policy feature to be disabled. */ @@ -12791,7 +14126,7 @@ https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-expl /** * List of not restored reasons for back-forward cache. */ - export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"SharedWorkerMessage"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCSticky"|"WebTransportSticky"|"WebSocketSticky"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"ContentDiscarded"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"|"PostMessageByWebViewClient"|"CacheControlNoStoreDeviceBoundSessionTerminated"|"CacheLimitPrunedOnModerateMemoryPressure"|"CacheLimitPrunedOnCriticalMemoryPressure"; + export type BackForwardCacheNotRestoredReason = "NotPrimaryMainFrame"|"BackForwardCacheDisabled"|"RelatedActiveContentsExist"|"HTTPStatusNotOK"|"SchemeNotHTTPOrHTTPS"|"Loading"|"WasGrantedMediaAccess"|"DisableForRenderFrameHostCalled"|"DomainNotAllowed"|"HTTPMethodNotGET"|"SubframeIsNavigating"|"Timeout"|"CacheLimit"|"JavaScriptExecution"|"RendererProcessKilled"|"RendererProcessCrashed"|"SchedulerTrackedFeatureUsed"|"ConflictingBrowsingInstance"|"CacheFlushed"|"ServiceWorkerVersionActivation"|"SessionRestored"|"ServiceWorkerPostMessage"|"EnteredBackForwardCacheBeforeServiceWorkerHostAdded"|"RenderFrameHostReused_SameSite"|"RenderFrameHostReused_CrossSite"|"ServiceWorkerClaim"|"IgnoreEventAndEvict"|"HaveInnerContents"|"TimeoutPuttingInCache"|"BackForwardCacheDisabledByLowMemory"|"BackForwardCacheDisabledByCommandLine"|"NetworkRequestDatapipeDrainedAsBytesConsumer"|"NetworkRequestRedirected"|"NetworkRequestTimeout"|"NetworkExceedsBufferLimit"|"NavigationCancelledWhileRestoring"|"NotMostRecentNavigationEntry"|"BackForwardCacheDisabledForPrerender"|"UserAgentOverrideDiffers"|"ForegroundCacheLimit"|"BrowsingInstanceNotSwapped"|"BackForwardCacheDisabledForDelegate"|"UnloadHandlerExistsInMainFrame"|"UnloadHandlerExistsInSubFrame"|"ServiceWorkerUnregistration"|"CacheControlNoStore"|"CacheControlNoStoreCookieModified"|"CacheControlNoStoreHTTPOnlyCookieModified"|"NoResponseHead"|"Unknown"|"ActivationNavigationsDisallowedForBug1234857"|"ErrorDocument"|"FencedFramesEmbedder"|"CookieDisabled"|"HTTPAuthRequired"|"CookieFlushed"|"BroadcastChannelOnMessage"|"WebViewSettingsChanged"|"WebViewJavaScriptObjectChanged"|"WebViewMessageListenerInjected"|"WebViewSafeBrowsingAllowlistChanged"|"WebViewDocumentStartJavascriptChanged"|"WebSocket"|"WebTransport"|"WebRTC"|"MainResourceHasCacheControlNoStore"|"MainResourceHasCacheControlNoCache"|"SubresourceHasCacheControlNoStore"|"SubresourceHasCacheControlNoCache"|"ContainsPlugins"|"DocumentLoaded"|"OutstandingNetworkRequestOthers"|"RequestedMIDIPermission"|"RequestedAudioCapturePermission"|"RequestedVideoCapturePermission"|"RequestedBackForwardCacheBlockedSensors"|"RequestedBackgroundWorkPermission"|"BroadcastChannel"|"WebXR"|"SharedWorker"|"SharedWorkerMessage"|"WebLocks"|"WebHID"|"WebShare"|"RequestedStorageAccessGrant"|"WebNfc"|"OutstandingNetworkRequestFetch"|"OutstandingNetworkRequestXHR"|"AppBanner"|"Printing"|"WebDatabase"|"PictureInPicture"|"SpeechRecognizer"|"IdleManager"|"PaymentManager"|"SpeechSynthesis"|"KeyboardLock"|"WebOTPService"|"OutstandingNetworkRequestDirectSocket"|"InjectedJavascript"|"InjectedStyleSheet"|"KeepaliveRequest"|"IndexedDBEvent"|"Dummy"|"JsNetworkRequestReceivedCacheControlNoStoreResource"|"WebRTCUsedWithCCNS"|"WebTransportUsedWithCCNS"|"WebSocketUsedWithCCNS"|"SmartCard"|"LiveMediaStreamTrack"|"UnloadHandler"|"ParserAborted"|"ContentSecurityHandler"|"ContentWebAuthenticationAPI"|"ContentFileChooser"|"ContentSerial"|"ContentFileSystemAccess"|"ContentMediaDevicesDispatcherHost"|"ContentWebBluetooth"|"ContentWebUSB"|"ContentMediaSessionService"|"ContentScreenReader"|"ContentDiscarded"|"EmbedderPopupBlockerTabHelper"|"EmbedderSafeBrowsingTriggeredPopupBlocker"|"EmbedderSafeBrowsingThreatDetails"|"EmbedderAppBannerManager"|"EmbedderDomDistillerViewerSource"|"EmbedderDomDistillerSelfDeletingRequestDelegate"|"EmbedderOomInterventionTabHelper"|"EmbedderOfflinePage"|"EmbedderChromePasswordManagerClientBindCredentialManager"|"EmbedderPermissionRequestManager"|"EmbedderModalDialog"|"EmbedderExtensions"|"EmbedderExtensionMessaging"|"EmbedderExtensionMessagingForOpenPort"|"EmbedderExtensionSentMessageToCachedFrame"|"RequestedByWebViewClient"|"PostMessageByWebViewClient"|"CacheControlNoStoreDeviceBoundSessionTerminated"|"CacheLimitPrunedOnModerateMemoryPressure"|"CacheLimitPrunedOnCriticalMemoryPressure"; /** * Types of not restored reasons for back-forward cache. */ @@ -14394,108 +15729,292 @@ Note that not all types exposed to the web platform are currently supported. } } - /** - * Security - */ - export module Security { - /** - * An internal certificate ID value. - */ - export type CertificateId = number; - /** - * A description of mixed content (HTTP resources on HTTPS pages), as defined by -https://www.w3.org/TR/mixed-content/#categories - */ - export type MixedContentType = "blockable"|"optionally-blockable"|"none"; + export module Preload { /** - * The security level of a page or resource. + * Unique id */ - export type SecurityState = "unknown"|"neutral"|"insecure"|"secure"|"info"|"insecure-broken"; + export type RuleSetId = string; /** - * Details about the security state of the page certificate. + * Corresponds to SpeculationRuleSet */ - export interface CertificateSecurityState { - /** - * Protocol name (e.g. "TLS 1.2" or "QUIC"). - */ - protocol: string; - /** - * Key Exchange used by the connection, or the empty string if not applicable. - */ - keyExchange: string; - /** - * (EC)DH group used by the connection, if applicable. - */ - keyExchangeGroup?: string; - /** - * Cipher name. - */ - cipher: string; - /** - * TLS MAC. Note that AEAD ciphers do not have separate MACs. - */ - mac?: string; - /** - * Page certificate. - */ - certificate: string[]; - /** - * Certificate subject name. - */ - subjectName: string; - /** - * Name of the issuing CA. - */ - issuer: string; - /** - * Certificate valid from date. - */ - validFrom: Network.TimeSinceEpoch; - /** - * Certificate valid to (expiration) date - */ - validTo: Network.TimeSinceEpoch; - /** - * The highest priority network error code, if the certificate has an error. - */ - certificateNetworkError?: string; - /** - * True if the certificate uses a weak signature algorithm. - */ - certificateHasWeakSignature: boolean; - /** - * True if the certificate has a SHA1 signature in the chain. - */ - certificateHasSha1Signature: boolean; + export interface RuleSet { + id: RuleSetId; /** - * True if modern SSL + * Identifies a document which the rule set is associated with. */ - modernSSL: boolean; + loaderId: Network.LoaderId; /** - * True if the connection is using an obsolete SSL protocol. + * Source text of JSON representing the rule set. If it comes from +` + + + + + `); + let dialogMessage = ''; + page.on('dialog', async dialog => { + dialogMessage = dialog.message(); + await dialog.accept(); + }); + await page.goBack(); + await expect.poll(() => dialogMessage).toBe('ready?'); + await expect(page.getByRole('button')).toHaveText('Clicked'); + }); + + const frame = await traceViewer.snapshotFrame('Expect'); + await expect(frame.getByRole('button')).toHaveCSS('color', 'rgb(255, 0, 0)'); + }); +}); diff --git a/tests/library/video.spec.ts b/tests/library/video.spec.ts index 123e7533adf7a..fc2c89a40c87d 100644 --- a/tests/library/video.spec.ts +++ b/tests/library/video.spec.ts @@ -23,8 +23,6 @@ import { registry } from '../../packages/playwright-core/lib/server'; import { expect, browserTest as it } from '../config/browserTest'; import { parseTraceRaw } from '../config/utils'; -const ffmpeg = registry.findExecutable('ffmpeg')!.executablePath('javascript'); - export class VideoPlayer { fileName: string; output: string; @@ -36,6 +34,7 @@ export class VideoPlayer { constructor(fileName: string) { this.fileName = fileName; + const ffmpeg = registry.findExecutable('ffmpeg')!.executablePathOrDie('javascript'); // Force output frame rate to 25 fps as otherwise it would produce one image per timebase unit // which is 1 / (25 * 1000). this.output = spawnSync(ffmpeg, ['-i', this.fileName, '-r', '25', `${this.fileName}-%03d.png`]).stderr.toString(); @@ -756,11 +755,11 @@ it.describe('screencast', () => { expectAll(pixels, isAlmostRed); }); - it('should capture full viewport on hidpi', async ({ browserType, browserName, headless, isWindows, isLinux, isHeadlessShell }, testInfo) => { + it('should capture full viewport on hidpi', async ({ browserType, browserName, headless, isWindows, isLinux, isHeadlessShell, channel }, testInfo) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/22411' }); it.fixme(browserName === 'chromium' && !isHeadlessShell, 'The square is not on the video'); it.fixme(browserName === 'firefox' && isWindows, 'https://github.com/microsoft/playwright/issues/14405'); - it.fixme(browserName === 'webkit' && isLinux && !headless, 'https://github.com/microsoft/playwright/issues/22617'); + it.fixme(browserName === 'webkit' && !headless && (isLinux || (isWindows && channel === 'webkit-wsl')), 'https://github.com/microsoft/playwright/issues/22617'); const size = { width: 600, height: 400 }; const browser = await browserType.launch(); diff --git a/tests/mcp/capabilities.spec.ts b/tests/mcp/capabilities.spec.ts new file mode 100644 index 0000000000000..7ec699aa6a50e --- /dev/null +++ b/tests/mcp/capabilities.spec.ts @@ -0,0 +1,106 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('test snapshot tool list', async ({ client }) => { + const { tools } = await client.listTools(); + expect(new Set(tools.map(t => t.name))).toEqual(new Set([ + 'browser_click', + 'browser_console_messages', + 'browser_drag', + 'browser_evaluate', + 'browser_file_upload', + 'browser_fill_form', + 'browser_handle_dialog', + 'browser_hover', + 'browser_select_option', + 'browser_type', + 'browser_close', + 'browser_install', + 'browser_navigate_back', + 'browser_navigate', + 'browser_network_requests', + 'browser_press_key', + 'browser_resize', + 'browser_snapshot', + 'browser_tabs', + 'browser_take_screenshot', + 'browser_wait_for', + ])); +}); + +test('test tool list proxy mode', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--connect-tool'], + }); + const { tools } = await client.listTools(); + expect(new Set(tools.map(t => t.name))).toEqual(new Set([ + 'browser_click', + 'browser_connect', // the extra tool + 'browser_console_messages', + 'browser_drag', + 'browser_evaluate', + 'browser_file_upload', + 'browser_fill_form', + 'browser_handle_dialog', + 'browser_hover', + 'browser_select_option', + 'browser_type', + 'browser_close', + 'browser_install', + 'browser_navigate_back', + 'browser_navigate', + 'browser_network_requests', + 'browser_press_key', + 'browser_resize', + 'browser_snapshot', + 'browser_tabs', + 'browser_take_screenshot', + 'browser_wait_for', + ])); +}); + +test('test capabilities (pdf)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--caps=pdf'], + }); + const { tools } = await client.listTools(); + const toolNames = tools.map(t => t.name); + expect(toolNames).toContain('browser_pdf_save'); +}); + +test('test capabilities (vision)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--caps=vision'], + }); + const { tools } = await client.listTools(); + const toolNames = tools.map(t => t.name); + expect(toolNames).toContain('browser_mouse_move_xy'); + expect(toolNames).toContain('browser_mouse_click_xy'); + expect(toolNames).toContain('browser_mouse_drag_xy'); +}); + +test('support for legacy --vision option', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--vision'], + }); + const { tools } = await client.listTools(); + const toolNames = tools.map(t => t.name); + expect(toolNames).toContain('browser_mouse_move_xy'); + expect(toolNames).toContain('browser_mouse_click_xy'); + expect(toolNames).toContain('browser_mouse_drag_xy'); +}); diff --git a/tests/mcp/cdp.spec.ts b/tests/mcp/cdp.spec.ts new file mode 100644 index 0000000000000..2bfdafb9c355c --- /dev/null +++ b/tests/mcp/cdp.spec.ts @@ -0,0 +1,109 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { spawnSync } from 'child_process'; +import { test, expect, mcpServerPath } from './fixtures'; + +test('cdp server', async ({ cdpServer, startClient, server }) => { + await cdpServer.start(); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`), + }); +}); + +test('cdp server reuse tab', async ({ cdpServer, startClient, server }) => { + const browserContext = await cdpServer.start(); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + + const [page] = browserContext.pages(); + await page.goto(server.HELLO_WORLD); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Hello, world!', + ref: 'f0', + }, + })).toHaveResponse({ + result: `Error: Ref f0 not found in the current page snapshot. Try capturing new snapshot.`, + isError: true, + }); + + expect(await client.callTool({ + name: 'browser_snapshot', + })).toHaveResponse({ + pageState: expect.stringContaining(`- Page URL: ${server.HELLO_WORLD} +- Page Title: Title +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Hello, world! +\`\`\``), + }); +}); + +test('should throw connection error and allow re-connecting', async ({ cdpServer, startClient, server }) => { + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + + server.setContent('/', ` + Title + Hello, world! + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + result: expect.stringContaining(`Error: browserType.connectOverCDP: connect ECONNREFUSED`), + isError: true, + }); + await cdpServer.start(); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`), + }); +}); + +test('does not support --device', async () => { + const result = spawnSync('node', [ + ...mcpServerPath, '--device=Pixel 5', '--cdp-endpoint=http://localhost:1234', + ]); + expect(result.error).toBeUndefined(); + expect(result.status).toBe(1); + expect(result.stderr.toString()).toContain('Device emulation is not supported with cdpEndpoint.'); +}); + +test('cdp server with headers', async ({ startClient, server }) => { + let authHeader = ''; + server.setRoute('/json/version/', (req, res) => { + authHeader = req.headers['authorization']; + res.end(); + }); + + const { client } = await startClient({ args: [`--cdp-endpoint=${server.PREFIX}`, '--cdp-header', 'Authorization: Bearer 1234567890'] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + isError: true, + }); + expect(authHeader).toBe('Bearer 1234567890'); +}); diff --git a/tests/mcp/click.spec.ts b/tests/mcp/click.spec.ts new file mode 100644 index 0000000000000..6f6a06dbd017c --- /dev/null +++ b/tests/mcp/click.spec.ts @@ -0,0 +1,155 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_click', async ({ client, server, mcpBrowser }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click();`, + pageState: expect.stringContaining(`- button "Submit" ${mcpBrowser !== 'webkit' || process.platform === 'linux' ? '[active] ' : ''}[ref=e2]`), + }); +}); + +test('browser_click (double)', async ({ client, server }) => { + server.setContent('/', ` + Title + +

Click me

+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Click me', + ref: 'e2', + doubleClick: true, + }, + })).toHaveResponse({ + code: `await page.getByRole('heading', { name: 'Click me' }).dblclick();`, + pageState: expect.stringContaining(`- heading "Double clicked" [level=1] [ref=e3]`), + }); +}); + +test('browser_click (right)', async ({ client, server }) => { + server.setContent('/', ` + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + const result = await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Menu', + ref: 'e2', + button: 'right', + }, + }); + expect(result).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Menu' }).click({ button: 'right' });`, + pageState: expect.stringContaining(`- button "Right clicked"`), + }); +}); + +test('browser_click (modifiers)', async ({ client, server, mcpBrowser }) => { + server.setContent('/', ` + Title + +
+ + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + if (process.platform !== 'darwin') { + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + modifiers: ['Control'], + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click({ modifiers: ['Control'] });`, + pageState: expect.stringContaining(`- generic [ref=e3]: ctrlKey:true metaKey:false shiftKey:false altKey:false`), + }); + } + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + modifiers: ['Shift'], + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click({ modifiers: ['Shift'] });`, + pageState: expect.stringContaining(`- generic [ref=e3]: ctrlKey:false metaKey:false shiftKey:true altKey:false`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + modifiers: ['Shift', 'Alt'], + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click({ modifiers: ['Shift', 'Alt'] });`, + pageState: expect.stringContaining(`- generic [ref=e3]: ctrlKey:false metaKey:false shiftKey:true altKey:true`), + }); +}); diff --git a/tests/mcp/clipboard.spec.ts b/tests/mcp/clipboard.spec.ts new file mode 100644 index 0000000000000..264948b924474 --- /dev/null +++ b/tests/mcp/clipboard.spec.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('clipboard write without permission dialog', async ({ startClient, server, mcpBrowser }) => { + test.skip(mcpBrowser === 'firefox' || mcpBrowser === 'webkit', 'Clipboard permissions are fully supported only in Chromium'); + const { client } = await startClient({ + args: [`--grant-permissions=clipboard-read,clipboard-write`] + }); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + const writeResult = await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: `() => navigator.clipboard.writeText('Hello from Playwright!').then( + () => 'Write successful', + e => 'Write failed: ' + e.message)`, + }, + }); + expect(writeResult).toHaveResponse({ + result: '"Write successful"', + }); + const readResult = await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: `() => navigator.clipboard.readText()`, + }, + }); + expect(readResult).toHaveResponse({ + result: '"Hello from Playwright!"', + }); +}); diff --git a/tests/mcp/config.spec.ts b/tests/mcp/config.spec.ts new file mode 100644 index 0000000000000..b13217617507d --- /dev/null +++ b/tests/mcp/config.spec.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'node:fs'; + +import { test, expect } from './fixtures'; +import { configFromCLIOptions } from '../../packages/playwright/lib/mcp/browser/config'; +import type { Config } from '../../packages/playwright/src/mcp/config'; + +test('config user data dir', async ({ startClient, server }, testInfo) => { + server.setContent('/', ` + Title + Hello, world! + `, 'text/html'); + + const config: Config = { + browser: { + userDataDir: testInfo.outputPath('user-data-dir'), + }, + }; + const configPath = testInfo.outputPath('config.json'); + await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); + + const { client } = await startClient({ args: ['--config', configPath] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Hello, world!`), + }); + + const files = await fs.promises.readdir(config.browser!.userDataDir!); + expect(files.length).toBeGreaterThan(0); +}); + +test('executable path', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ args: ['--executable-path', testInfo.outputPath('missing-executable')] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + isError: true, + result: expect.stringMatching(/Failed to launch.*missing-executable/), + }); +}); + +test.describe(() => { + test.use({ mcpBrowser: '' }); + test('browserName', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/458' } }, async ({ startClient }, testInfo) => { + const config: Config = { + browser: { + browserName: 'firefox', + }, + }; + const configPath = testInfo.outputPath('config.json'); + await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); + + const { client } = await startClient({ args: ['--config', configPath] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: 'data:text/html,' }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Firefox`), + }); + }); +}); + +test.describe('sandbox configuration', () => { + test('should enable sandbox by default (no --no-sandbox flag)', async () => { + const config = configFromCLIOptions({ sandbox: undefined }); + expect(config.browser?.launchOptions?.chromiumSandbox).toBeUndefined(); + }); + + test('should disable sandbox when --no-sandbox flag is passed', async () => { + const config = configFromCLIOptions({ sandbox: false }); + expect(config.browser?.launchOptions?.chromiumSandbox).toBe(false); + }); +}); diff --git a/tests/mcp/console.spec.ts b/tests/mcp/console.spec.ts new file mode 100644 index 0000000000000..4deec26097a99 --- /dev/null +++ b/tests/mcp/console.spec.ts @@ -0,0 +1,134 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, parseResponse } from './fixtures'; + +test('browser_console_messages', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + const resource = await client.callTool({ + name: 'browser_console_messages', + }); + expect(resource).toHaveResponse({ + result: `[LOG] Hello, world! @ ${server.PREFIX}/:4 +[ERROR] Error @ ${server.PREFIX}/:5`, + }); +}); + +test('browser_console_messages (page error)', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + const resource = await client.callTool({ + name: 'browser_console_messages', + }); + expect(resource).toHaveResponse({ + result: expect.stringContaining(`Error: Error in script`), + }); + expect(resource).toHaveResponse({ + result: expect.stringContaining(server.PREFIX), + }); +}); + +test('recent console messages', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + const response = await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Click me', + ref: 'e2', + }, + }); + + expect(response).toHaveResponse({ + consoleMessages: expect.stringContaining(`- [LOG] Hello, world! @`), + }); +}); + +test('browser_console_messages errors only', async ({ client, server }) => { + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.HELLO_WORLD, + }, + }); + + await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: `async () => { + console.log("console.log"); + console.warn("console.warn"); + console.error("console.error"); + setTimeout(() => { throw new Error("unhandled"); }, 0); + await fetch('/missing'); + }`, + }, + }); + + const response = parseResponse(await client.callTool({ + name: 'browser_console_messages', + arguments: { + onlyErrors: true, + }, + })); + expect.soft(response.result).toContain('console.error'); + expect.soft(response.result).toContain('Error: unhandled'); + expect.soft(response.result).toContain('404'); + expect.soft(response.result).not.toContain('console.log'); + expect.soft(response.result).not.toContain('console.warn'); +}); diff --git a/tests/mcp/core.spec.ts b/tests/mcp/core.spec.ts new file mode 100644 index 0000000000000..033a212245d5d --- /dev/null +++ b/tests/mcp/core.spec.ts @@ -0,0 +1,190 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_navigate', async ({ client, server }) => { + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: `await page.goto('${server.HELLO_WORLD}');`, + pageState: `- Page URL: ${server.HELLO_WORLD} +- Page Title: Title +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Hello, world! +\`\`\``, + }); +}); + +test('browser_select_option', async ({ client, server }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_select_option', + arguments: { + element: 'Select', + ref: 'e2', + values: ['bar'], + }, + })).toHaveResponse({ + code: `await page.getByRole('combobox').selectOption(['bar']);`, + pageState: `- Page URL: ${server.PREFIX}/ +- Page Title: Title +- Page Snapshot: +\`\`\`yaml +- combobox [ref=e2]: + - option "Foo" + - option "Bar" [selected] +\`\`\``, + }); +}); + +test('browser_select_option (multiple)', async ({ client, server }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_select_option', + arguments: { + element: 'Select', + ref: 'e2', + values: ['bar', 'baz'], + }, + })).toHaveResponse({ + code: `await page.getByRole('listbox').selectOption(['bar', 'baz']);`, + pageState: expect.stringContaining(` +- listbox [ref=e2]: + - option "Foo" [ref=e3] + - option "Bar" [selected] [ref=e4] + - option "Baz" [selected] [ref=e5]`), + }); +}); + +test('browser_resize', async ({ client, server }) => { + server.setContent('/', ` + Resize Test + +
Waiting for resize...
+ + + `, 'text/html'); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + const response = await client.callTool({ + name: 'browser_resize', + arguments: { + width: 390, + height: 780, + }, + }); + expect(response).toHaveResponse({ + code: `await page.setViewportSize({ width: 390, height: 780 });`, + }); + await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toHaveResponse({ + pageState: expect.stringContaining(`Window size: 390x780`), + }); +}); + +test('old locator error message', async ({ client, server }) => { + server.setContent('/', ` + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + })).toHaveResponse({ + pageState: expect.stringContaining(` + - button "Button 1" [ref=e2] + - button "Button 2" [ref=e3]`), + }); + + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button 1', + ref: 'e2', + }, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button 2', + ref: 'e3', + }, + })).toHaveResponse({ + result: expect.stringContaining(`Ref e3 not found in the current page snapshot. Try capturing new snapshot.`), + isError: true, + }); +}); + +test('visibility: hidden > visible should be shown', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/535' } }, async ({ client, server }) => { + server.setContent('/', ` +
+
+ +
+
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_snapshot' + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button"`), + }); +}); diff --git a/tests/mcp/device.spec.ts b/tests/mcp/device.spec.ts new file mode 100644 index 0000000000000..2207b5b25f049 --- /dev/null +++ b/tests/mcp/device.spec.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('--device should work', async ({ startClient, server }) => { + const { client } = await startClient({ + args: ['--device', 'iPhone 15'], + }); + + server.setRoute('/', (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(` + + + + + + `); + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + })).toHaveResponse({ + pageState: expect.stringContaining(`393x659`), + }); +}); diff --git a/tests/mcp/dialogs.spec.ts b/tests/mcp/dialogs.spec.ts new file mode 100644 index 0000000000000..4c2bf98eb5025 --- /dev/null +++ b/tests/mcp/dialogs.spec.ts @@ -0,0 +1,255 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('alert dialog', async ({ client, server }) => { + server.setContent('/', ``, 'text/html'); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Button' }).click();`, + modalState: `- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + code: undefined, + modalState: `- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`, + }); + + expect(await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + }, + })).toHaveResponse({ + modalState: undefined, + pageState: expect.stringContaining(`- button "Button"`), + }); +}); + +test('two alert dialogs', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Button' }).click();`, + modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 1"]: can be handled by the "browser_handle_dialog" tool`), + }); + + const result = await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + }, + }); + + expect(result).toHaveResponse({ + modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 2"]: can be handled by the "browser_handle_dialog" tool`), + }); + + const result2 = await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + }, + }); + + expect(result2).not.toHaveResponse({ + modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 2"]: can be handled by the "browser_handle_dialog" tool`), + }); +}); + +test('confirm dialog (true)', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + modalState: expect.stringContaining(`- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool`), + }); + + expect(await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + }, + })).toHaveResponse({ + modalState: undefined, + pageState: expect.stringContaining(`- generic [active] [ref=e1]: "true"`), + }); +}); + +test('confirm dialog (false)', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + modalState: expect.stringContaining(`- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool`), + }); + + expect(await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: false, + }, + })).toHaveResponse({ + modalState: undefined, + pageState: expect.stringContaining(`- generic [active] [ref=e1]: "false"`), + }); +}); + +test('prompt dialog', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + modalState: expect.stringContaining(`- ["prompt" dialog with message "Prompt"]: can be handled by the "browser_handle_dialog" tool`), + }); + + const result = await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + promptText: 'Answer', + }, + }); + + expect(result).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Answer`), + }); +}); + +test('alert dialog w/ race', async ({ client, server }) => { + server.setContent('/', ``, 'text/html'); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- button "Button" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Button' }).click();`, + modalState: expect.stringContaining(`- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`), + }); + + const result = await client.callTool({ + name: 'browser_handle_dialog', + arguments: { + accept: true, + }, + }); + + expect(result).toHaveResponse({ + modalState: undefined, + pageState: expect.stringContaining(`- Page URL: ${server.PREFIX}/ +- Page Title: +- Page Snapshot: +\`\`\`yaml +- button "Button"`), + }); +}); diff --git a/tests/mcp/evaluate.spec.ts b/tests/mcp/evaluate.spec.ts new file mode 100644 index 0000000000000..68c8a39a398db --- /dev/null +++ b/tests/mcp/evaluate.spec.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_evaluate', async ({ client, server }) => { + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- Page Title: Title`), + }); + + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => document.title', + }, + })).toHaveResponse({ + result: `"Title"`, + code: `await page.evaluate('() => document.title');`, + }); +}); + +test('browser_evaluate (element)', async ({ client, server }) => { + server.setContent('/', ` + Hello, world! + `, 'text/html'); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: 'element => element.style.backgroundColor', + element: 'body', + ref: 'e1', + }, + })).toHaveResponse({ + result: `"red"`, + code: `await page.getByText('Hello, world!').evaluate('element => element.style.backgroundColor');`, + }); +}); + +test('browser_evaluate object', async ({ client, server }) => { + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- Page Title: Title`), + }); + + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => ({ title: document.title, url: document.URL })', + }, + })).toHaveResponse({ + result: JSON.stringify({ title: 'Title', url: server.HELLO_WORLD }, null, 2), + code: `await page.evaluate('() => ({ title: document.title, url: document.URL })');`, + }); +}); + +test('browser_evaluate (error)', async ({ client, server }) => { + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- Page Title: Title`), + }); + + const result = await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => nonExistentVariable', + }, + }); + + expect(result.isError).toBe(true); + expect(result.content?.[0]?.text).toContain('nonExistentVariable'); + // Check for common error patterns across browsers + const errorText = result.content?.[0]?.text || ''; + expect(errorText).toMatch(/not defined|Can't find variable/); +}); diff --git a/tests/mcp/files.spec.ts b/tests/mcp/files.spec.ts new file mode 100644 index 0000000000000..6677a44027a8c --- /dev/null +++ b/tests/mcp/files.spec.ts @@ -0,0 +1,151 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs/promises'; +import { test, expect } from './fixtures'; + +test('browser_file_upload', async ({ client, server }, testInfo) => { + server.setContent('/', ` + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: + - button "Choose File" [ref=e2] + - button "Button" [ref=e3]`), + }); + + { + expect(await client.callTool({ + name: 'browser_file_upload', + arguments: { paths: [] }, + })).toHaveResponse({ + isError: true, + result: expect.stringContaining(`The tool "browser_file_upload" can only be used when there is related modal state present.`), + modalState: expect.stringContaining(`- There is no modal state present`), + }); + } + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Textbox', + ref: 'e2', + }, + })).toHaveResponse({ + modalState: expect.stringContaining(`- [File chooser]: can be handled by the "browser_file_upload" tool`), + }); + + const filePath = testInfo.outputPath('test.txt'); + await fs.writeFile(filePath, 'Hello, world!'); + + { + const response = await client.callTool({ + name: 'browser_file_upload', + arguments: { + paths: [filePath], + }, + }); + + expect(response).toHaveResponse({ + code: expect.stringContaining(`await fileChooser.setFiles(`), + modalState: undefined, + }); + } + + { + const response = await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Textbox', + ref: 'e2', + }, + }); + + expect(response).toHaveResponse({ + modalState: `- [File chooser]: can be handled by the "browser_file_upload" tool`, + }); + } + + { + const response = await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button', + ref: 'e3', + }, + }); + + expect(response).toHaveResponse({ + result: `Error: Tool "browser_click" does not handle the modal state.`, + modalState: expect.stringContaining(`- [File chooser]: can be handled by the "browser_file_upload" tool`), + }); + } +}); + +test('clicking on download link emits download', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + + server.setContent('/', `Download`, 'text/html'); + server.setContent('/download', 'Data', 'text/plain'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- link "Download" [ref=e2]`), + }); + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Download link', + ref: 'e2', + }, + }); + await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toHaveResponse({ + downloads: `- Downloaded file test.txt to ${testInfo.outputPath('output', 'test.txt')}`, + }); +}); + +test('navigating to download link emits download', async ({ startClient, server, mcpBrowser }, testInfo) => { + test.skip(mcpBrowser !== 'chromium', 'This test is racy'); + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + + server.setRoute('/download', (req, res) => { + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Disposition': 'attachment; filename=test.txt', + }); + res.end('Hello world!'); + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX + '/download', + }, + })).toHaveResponse({ + downloads: expect.stringContaining(`- Downloaded file test.txt to`), + }); +}); diff --git a/tests/mcp/fixtures.ts b/tests/mcp/fixtures.ts new file mode 100644 index 0000000000000..28e296e324c90 --- /dev/null +++ b/tests/mcp/fixtures.ts @@ -0,0 +1,310 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; +import { chromium } from 'playwright'; + +import { test as baseTest, expect as baseExpect } from '@playwright/test'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { ListRootsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; +import { TestServer } from '../config/testserver'; +import { serverFixtures } from '../config/serverFixtures'; +import { parseResponse } from '../../packages/playwright/lib/mcp/browser/response'; + +import type { Config } from '../../packages/playwright/src/mcp/config'; +import type { BrowserContext } from 'playwright'; +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; +import type { Stream } from 'stream'; +import type { ServerFixtures, ServerWorkerOptions } from '../config/serverFixtures'; + +export { parseResponse }; + +export type TestOptions = { + mcpArgs: string[] | undefined; + mcpBrowser: string | undefined; + mcpServerType: 'mcp' | 'test-mcp'; +}; + +type CDPServer = { + endpoint: string; + start: () => Promise; +}; + +export type StartClient = (options?: { + clientName?: string, + args?: string[], + config?: Config, + roots?: { name: string, uri: string }[], + rootsResponseDelay?: number, + env?: NodeJS.ProcessEnv, +}) => Promise<{ client: Client, stderr: () => string }>; + + +type TestFixtures = { + client: Client; + startClient: StartClient; + wsEndpoint: string; + cdpServer: CDPServer; + server: TestServer; + httpsServer: TestServer; + mcpHeadless: boolean; +}; + +type WorkerFixtures = { + _workerServers: { server: TestServer, httpsServer: TestServer }; +}; + +export const serverTest = baseTest.extend(serverFixtures); + +export const test = serverTest.extend({ + mcpArgs: [undefined, { option: true }], + + client: async ({ startClient }, use) => { + const { client } = await startClient(); + await use(client); + }, + + startClient: async ({ mcpHeadless, mcpBrowser, mcpArgs, mcpServerType }, use, testInfo) => { + const configDir = path.dirname(test.info().config.configFile!); + const clients: Client[] = []; + + await use(async options => { + const args: string[] = mcpArgs ?? []; + + if (mcpHeadless) + args.push('--headless'); + + if (mcpServerType === 'test-mcp') { + if (!options?.args?.some(arg => arg.startsWith('--config'))) + args.push('--config', test.info().outputPath()); + } else { + if (process.env.CI && process.platform === 'linux') + args.push('--no-sandbox'); + if (mcpBrowser) + args.push(`--browser=${mcpBrowser}`); + if (options?.config) { + const configFile = testInfo.outputPath('config.json'); + await fs.promises.writeFile(configFile, JSON.stringify(options.config, null, 2)); + args.push(`--config=${path.relative(configDir, configFile)}`); + } + } + + if (options?.args) + args.push(...options.args); + + const client = new Client({ name: options?.clientName ?? 'test', version: '1.0.0' }, options?.roots ? { capabilities: { roots: {} } } : undefined); + if (options?.roots) { + client.setRequestHandler(ListRootsRequestSchema, async request => { + if (options.rootsResponseDelay) + await new Promise(resolve => setTimeout(resolve, options.rootsResponseDelay)); + return { + roots: options.roots, + }; + }); + } + const env = { ...process.env, ...options?.env }; + const { transport, stderr } = await createTransport(mcpServerType, args, env); + let stderrBuffer = ''; + stderr?.on('data', data => { + if (process.env.PWDEBUGIMPL) + process.stderr.write(data); + stderrBuffer += data.toString(); + }); + clients.push(client); + await client.connect(transport); + await client.ping(); + return { client, stderr: () => stderrBuffer }; + }); + + await Promise.all(clients.map(client => client.close())); + }, + + wsEndpoint: async ({ }, use) => { + const browserServer = await chromium.launchServer(); + await use(browserServer.wsEndpoint()); + await browserServer.close(); + }, + + cdpServer: async ({ mcpBrowser }, use, testInfo) => { + test.skip(!['chrome', 'msedge', 'chromium'].includes(mcpBrowser!), 'CDP is not supported for non-Chromium browsers'); + + let browserContext: BrowserContext | undefined; + const port = 9100 + testInfo.workerIndex; + await use({ + endpoint: `http://localhost:${port}`, + start: async () => { + if (browserContext) + throw new Error('CDP server already exists'); + browserContext = await chromium.launchPersistentContext(testInfo.outputPath('cdp-user-data-dir'), { + channel: mcpBrowser, + headless: true, + args: [ + `--remote-debugging-port=${port}`, + ], + }); + return browserContext; + } + }); + await browserContext?.close(); + }, + + mcpHeadless: async ({ headless }, use) => { + await use(headless); + }, + + server: async ({ server }, use) => { + server.setContent('/favicon.ico', '', 'image/x-icon'); + server.setContent('/', ``, 'text/html'); + server.setContent('/hello-world', ` + Title + Hello, world! + `, 'text/html'); + await use(server); + }, + + mcpBrowser: ['chrome', { option: true }], + + mcpServerType: ['mcp', { option: true }], +}); + +async function createTransport(mcpServerType: TestOptions['mcpServerType'], args: string[], env: NodeJS.ProcessEnv): Promise<{ + transport: Transport, + stderr: Stream | null, +}> { + const profilesDir = test.info().outputPath('ms-playwright'); + const transport = new StdioClientTransport({ + command: 'node', + args: [...(mcpServerType === 'test-mcp' ? testMcpServerPath : mcpServerPath), ...args], + cwd: test.info().outputPath(), + stderr: 'pipe', + env: { + ...env, + DEBUG_COLORS: '0', + DEBUG_HIDE_DATE: '1', + PWMCP_PROFILES_DIR_FOR_TEST: profilesDir, + }, + }); + return { + transport, + stderr: transport.stderr!, + }; +} + +type Response = Awaited>; + +export const expect = baseExpect.extend({ + toHaveResponse(response: Response, object: any) { + const parsed = parseResponse(response); + const isNot = this.isNot; + try { + if (isNot) + expect(parsed).not.toEqual(expect.objectContaining(object)); + else + expect(parsed).toEqual(expect.objectContaining(object)); + } catch (e) { + return { + pass: isNot, + message: () => e.message, + }; + } + return { + pass: !isNot, + message: () => ``, + }; + }, + + toHaveTextResponse(response: Response, value: any) { + const text = response.content[0].text + .replace(/\[id=[^\]]+\]/g, '[id=]') + .replace(/\([\d\.]+m?s\)/g, '(XXms)') + .replace(/[✓] /g, 'ok'); + + const isNot = this.isNot; + try { + if (isNot) + expect(text).not.toEqual(value); + else + expect(text).toEqual(value); + } catch (e) { + return { + pass: isNot, + message: () => e.message, + }; + } + return { + pass: !isNot, + message: () => ``, + }; + }, +}); + +export function formatOutput(output: string): string[] { + return output.split('\n').map(line => line.replace(/^pw:mcp:test /, '').replace(/user data dir.*/, 'user data dir').trim()).filter(Boolean); +} + +export const mcpServerPath = [path.join(__dirname, '../../packages/playwright/cli.js'), 'run-mcp-server']; +export const testMcpServerPath = [path.join(__dirname, '../../packages/playwright-test/cli.js'), 'run-test-mcp-server']; + +type Files = { [key: string]: string | Buffer }; + +export async function writeFiles(files: Files, options?: { update?: boolean }) { + const baseDir = test.info().outputPath(); + + if (!options?.update && !Object.keys(files).some(name => name.includes('package.json'))) { + files = { + ...files, + 'package.json': `{ "name": "test-project" }`, + }; + } + + if (!options?.update && !Object.keys(files).some(name => name.includes('tsconfig.json') || name.includes('jsconfig.json'))) { + files = { + ...files, + 'tsconfig.json': `{}`, + }; + } + + await Promise.all(Object.keys(files).map(async name => { + const fullName = path.join(baseDir, name); + if (files[name] === undefined) + return; + await fs.promises.mkdir(path.dirname(fullName), { recursive: true }); + await fs.promises.writeFile(fullName, files[name]); + })); + + return baseDir; +} + +export async function prepareDebugTest(startClient: StartClient, testFile?: string, clientArgs?: Parameters[0]) { + await writeFiles({ + 'a.test.ts': testFile || ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await page.setContent(''); + await expect(page.getByRole('button', { name: 'Missing' })).toBeVisible({ timeout: 1000 }); + }); + ` + }); + + const { client } = await startClient(clientArgs); + const listResult = await client.callTool({ + name: 'test_list', + }); + const [, id] = listResult.content[0].text.match(/\[id=([^\]]+)\]/); + return { client, id }; +} diff --git a/tests/mcp/form.spec.ts b/tests/mcp/form.spec.ts new file mode 100644 index 0000000000000..893c4517889e2 --- /dev/null +++ b/tests/mcp/form.spec.ts @@ -0,0 +1,123 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_fill_form (textbox)', async ({ client, server }) => { + server.setContent('/', ` + + + + + + + + + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_fill_form', + arguments: { + fields: [ + { + name: 'Name textbox', + type: 'textbox', + ref: 'e4', + value: 'John Doe' + }, + { + name: 'Email textbox', + type: 'textbox', + ref: 'e6', + value: 'john.doe@example.com' + }, + { + name: 'Age textbox', + type: 'slider', + ref: 'e8', + value: '25' + }, + { + name: 'Country select', + type: 'combobox', + ref: 'e10', + value: 'United States' + }, + { + name: 'Subscribe checkbox', + type: 'checkbox', + ref: 'e12', + value: 'true' + }, + ] + }, + })).toHaveResponse({ + code: `await page.getByRole('textbox', { name: 'Name' }).fill('John Doe'); +await page.getByRole('textbox', { name: 'Email' }).fill('john.doe@example.com'); +await page.getByRole('slider', { name: 'Age' }).fill('25'); +await page.getByLabel('Choose a country United').selectOption('United States'); +await page.getByRole('checkbox', { name: 'Subscribe to newsletter' }).setChecked(true);`, + }); + + const response = await client.callTool({ + name: 'browser_snapshot', + arguments: { + }, + }); + expect.soft(response).toHaveResponse({ + pageState: expect.stringMatching(/textbox "Name".*John Doe/), + }); + expect.soft(response).toHaveResponse({ + pageState: expect.stringMatching(/textbox "Email".*john.doe@example.com/), + }); + expect.soft(response).toHaveResponse({ + pageState: expect.stringMatching(/slider "Age".*"25"/), + }); + expect.soft(response).toHaveResponse({ + pageState: expect.stringContaining('option \"United States\" [selected]'), + }); + expect.soft(response).toHaveResponse({ + pageState: expect.stringContaining('checkbox \"Subscribe to newsletter\" [checked]'), + }); +}); diff --git a/tests/mcp/generator.spec.ts b/tests/mcp/generator.spec.ts new file mode 100644 index 0000000000000..8f7ffec24eb14 --- /dev/null +++ b/tests/mcp/generator.spec.ts @@ -0,0 +1,278 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +import { test, expect, writeFiles } from './fixtures'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('generator tools intent', async ({ startClient }) => { + const { client } = await startClient(); + const { tools } = await client.listTools(); + const toolsWithIntent: string[] = []; + for (const tool of tools) { + if (tool.inputSchema.properties?.intent) + toolsWithIntent.push(tool.name); + } + + expect(toolsWithIntent).toEqual([ + 'browser_close', + 'browser_resize', + 'browser_handle_dialog', + 'browser_evaluate', + 'browser_file_upload', + 'browser_fill_form', + 'browser_install', + 'browser_press_key', + 'browser_type', + 'browser_navigate', + 'browser_navigate_back', + 'browser_mouse_move_xy', + 'browser_mouse_click_xy', + 'browser_mouse_drag_xy', + 'browser_click', + 'browser_drag', + 'browser_hover', + 'browser_select_option', + 'browser_tabs', + 'browser_wait_for', + 'browser_verify_element_visible', + 'browser_verify_text_visible', + 'browser_verify_list_visible', + 'browser_verify_value', + ]); +}); + +test('generator_setup_page', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + const response = await client.callTool({ + name: 'generator_setup_page', + arguments: { + plan: 'Test plan', + seedFile: 'a.test.ts', + }, + }); + + expect(response).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction + +### Page state +- Page URL: about:blank +- Page Title: +- Page Snapshot: +\`\`\`yaml +- button "Submit" [ref=e2] +\`\`\` +`)); + + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + intent: 'Click submit button', + }, + }); + + expect(await client.callTool({ + name: 'generator_read_log', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`# Plan + +Test plan + +# Seed file: a.test.ts + +\`\`\`ts + + + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + + +\`\`\` + +# Steps + +### Click submit button +\`\`\`ts +await page.getByRole('button', { name: 'Submit' }).click(); +\`\`\` + + +# Best practices +`)); +}); + +test('click after generator_log_action', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + await client.callTool({ + name: 'generator_setup_page', + arguments: { + plan: 'Test plan', + seedFile: 'a.test.ts', + }, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + intent: 'Click submit button', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click();`, + pageState: expect.stringContaining(`- button "Submit"`), + }); + + expect(await client.callTool({ + name: 'generator_read_log', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`# Plan + +Test plan + +# Seed file: a.test.ts + +\`\`\`ts + + + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + + +\`\`\` + +# Steps + +### Click submit button +\`\`\`ts +await page.getByRole('button', { name: 'Submit' }).click(); +\`\`\` + + +# Best practices +`)); +}); + +test('generator_setup_page is required', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + + expect(await client.callTool({ + name: 'generator_read_log', + arguments: {}, + })).toEqual({ + content: [{ type: 'text', text: `Error: Please setup page using "generator_setup_page" first.` }], + isError: true, + }); + + expect(await client.callTool({ + name: 'generator_write_test', + arguments: { + fileName: 'a.test.ts', + code: '// Test content', + }, + })).toEqual({ + content: [{ type: 'text', text: `Error: Please setup page using "generator_setup_page" first.` }], + isError: true, + }); + + expect(await client.callTool({ + name: 'generator_read_log', + arguments: {}, + })).toEqual({ + content: [{ type: 'text', text: `Error: Please setup page using "generator_setup_page" first.` }], + isError: true, + }); +}); + +test('generator_write_test', async ({ startClient }, testInfo) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + + await client.callTool({ + name: 'generator_setup_page', + arguments: { + plan: 'Test plan', + seedFile: 'a.test.ts', + }, + }); + + expect(await client.callTool({ + name: 'generator_write_test', + arguments: { + fileName: 'a.test.ts', + code: `// Test content`, + }, + })).toHaveResponse({ + result: expect.stringContaining(`Test written to a.test.ts`), + }); + + const code = fs.readFileSync(testInfo.outputPath('a.test.ts'), 'utf8'); + expect(code).toBe(`// Test content`); +}); diff --git a/tests/mcp/headed.spec.ts b/tests/mcp/headed.spec.ts new file mode 100644 index 0000000000000..56fd4923c62a9 --- /dev/null +++ b/tests/mcp/headed.spec.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +for (const mcpHeadless of [false, true]) { + test.describe(`mcpHeadless: ${mcpHeadless}`, () => { + test.use({ mcpHeadless }); + test.skip(process.platform === 'linux', 'Auto-detection wont let this test run on linux'); + + test('browser', async ({ client, server, mcpBrowser }) => { + test.skip(!['chrome', 'msedge', 'chromium'].includes(mcpBrowser ?? ''), 'Only chrome is supported for this test'); + server.setRoute('/', (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(` + + + `); + }); + + const response = await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + expect(response).toHaveResponse({ + pageState: (mcpHeadless ? expect : expect.not).stringContaining(`HeadlessChrome`), + }); + }); + }); +} diff --git a/tests/mcp/http.spec.ts b/tests/mcp/http.spec.ts new file mode 100644 index 0000000000000..b5d2fcb34dda3 --- /dev/null +++ b/tests/mcp/http.spec.ts @@ -0,0 +1,398 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import net from 'net'; + +import { ChildProcess, spawn } from 'child_process'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { test as baseTest, expect, mcpServerPath } from './fixtures'; + +import type { Config } from '../../packages/playwright/src/mcp/config'; +import { ListRootsRequestSchema } from 'packages/playwright/lib/mcp/sdk/bundle'; + +const test = baseTest.extend<{ serverEndpoint: (options?: { args?: string[], noPort?: boolean }) => Promise<{ url: URL, stderr: () => string }> }>({ + serverEndpoint: async ({ mcpHeadless }, use, testInfo) => { + let cp: ChildProcess | undefined; + const userDataDir = testInfo.outputPath('user-data-dir'); + await use(async (options?: { args?: string[], noPort?: boolean }) => { + if (cp) + throw new Error('Process already running'); + + cp = spawn('node', [ + ...mcpServerPath, + ...(options?.noPort ? [] : ['--port=0']), + '--user-data-dir=' + userDataDir, + ...(mcpHeadless ? ['--headless'] : []), + ...(options?.args || []), + ], { + stdio: 'pipe', + env: { + ...process.env, + DEBUG: 'pw:mcp:test', + DEBUG_COLORS: '0', + DEBUG_HIDE_DATE: '1', + }, + }); + let stderr = ''; + const url = await new Promise(resolve => cp!.stderr?.on('data', data => { + stderr += data.toString(); + const match = stderr.match(/Listening on (http:\/\/.*)/); + if (match) + resolve(match[1]); + })); + + return { url: new URL(url), stderr: () => stderr }; + }); + cp?.kill('SIGTERM'); + }, +}); + +test('http transport', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint(); + const transport = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client = new Client({ name: 'test', version: '1.0.0' }); + await client.connect(transport); + await client.ping(); +}); + +test('http transport (config)', async ({ serverEndpoint }) => { + const config: Config = { + server: { + port: 0, + } + }; + const configFile = test.info().outputPath('config.json'); + await fs.promises.writeFile(configFile, JSON.stringify(config, null, 2)); + + const { url } = await serverEndpoint({ noPort: true, args: ['--config=' + configFile] }); + const transport = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client = new Client({ name: 'test', version: '1.0.0' }); + await client.connect(transport); + await client.ping(); +}); + +test('http transport browser lifecycle (isolated)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--isolated'] }); + + const transport1 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + /** + * src/client/streamableHttp.ts + * Clients that no longer need a particular session + * (e.g., because the user is leaving the client application) SHOULD send an + * HTTP DELETE to the MCP endpoint with the Mcp-Session-Id header to explicitly + * terminate the session. + */ + await transport1.terminateSession(); + await client1.close(); + + const transport2 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await transport2.terminateSession(); + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create http session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete http session/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create browser context \(isolated\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser context \(isolated\)/)).length).toBe(2); + + expect(lines.filter(line => line.match(/obtain browser \(isolated\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser \(isolated\)/)).length).toBe(2); + }).toPass(); +}); + +test('http transport browser lifecycle (isolated, multiclient)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--isolated'] }); + + const transport1 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + const transport2 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await transport1.terminateSession(); + await client1.close(); + + const transport3 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client3 = new Client({ name: 'test', version: '1.0.0' }); + await client3.connect(transport3); + await client3.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + await transport2.terminateSession(); + await client2.close(); + await transport3.terminateSession(); + await client3.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create http session/)).length).toBe(3); + expect(lines.filter(line => line.match(/delete http session/)).length).toBe(3); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(3); + expect(lines.filter(line => line.match(/close context/)).length).toBe(3); + + expect(lines.filter(line => line.match(/create browser context \(isolated\)/)).length).toBe(3); + expect(lines.filter(line => line.match(/close browser context \(isolated\)/)).length).toBe(3); + + expect(lines.filter(line => line.match(/obtain browser \(isolated\)/)).length).toBe(1); + expect(lines.filter(line => line.match(/close browser \(isolated\)/)).length).toBe(1); + }).toPass(); +}); + +test('http transport browser lifecycle (persistent)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint(); + + const transport1 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await transport1.terminateSession(); + await client1.close(); + + const transport2 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await transport2.terminateSession(); + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create http session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete http session/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create browser context \(persistent\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser context \(persistent\)/)).length).toBe(2); + + expect(lines.filter(line => line.match(/lock user data dir/)).length).toBe(2); + expect(lines.filter(line => line.match(/release user data dir/)).length).toBe(2); + }).toPass(); +}); + +test('http transport browser lifecycle (persistent, multiclient)', async ({ serverEndpoint, server }) => { + const { url } = await serverEndpoint(); + + const transport1 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + const transport2 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + const response = await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + expect(response.isError).toBe(true); + expect(response.content?.[0].text).toContain('use --isolated to run multiple instances of the same browser'); + + await client1.close(); + await client2.close(); +}); + +test('http transport shared context', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--shared-browser-context'] }); + + // Create first client and navigate + const transport1 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client1 = new Client({ name: 'test1', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + // Create second client - should reuse the same browser context + const transport2 = new StreamableHTTPClientTransport(new URL('/mcp', url)); + const client2 = new Client({ name: 'test2', version: '1.0.0' }); + await client2.connect(transport2); + + // Get tabs from second client - should see the tab created by first client + const tabsResult = await client2.callTool({ + name: 'browser_tabs', + arguments: { action: 'list' }, + }); + + // Should have at least one tab (the one created by client1) + expect(tabsResult.content[0]?.text).toContain('tabs'); + + await transport1.terminateSession(); + await client1.close(); + + // Second client should still work since context is shared + await client2.callTool({ + name: 'browser_snapshot', + arguments: {}, + }); + + await transport2.terminateSession(); + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create http session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete http session/)).length).toBe(2); + + // Should have only one context creation since it's shared + expect(lines.filter(line => line.match(/create shared browser context/)).length).toBe(1); + + // Should see client connect/disconnect messages + expect(lines.filter(line => line.match(/shared context client connected/)).length).toBe(2); + expect(lines.filter(line => line.match(/shared context client disconnected/)).length).toBe(2); + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + // Context should only close when the server shuts down. + expect(lines.filter(line => line.match(/close browser context complete \(persistent\)/)).length).toBe(0); + }).toPass(); + + // Simulate Ctrl+C in a way that works on Windows too. + await fetch(new URL('/killkillkill', url).href).catch(() => {}); + + await expect(async () => { + const lines = stderr().split('\n'); + // Context should only close when the server shuts down. + expect(lines.filter(line => line.match(/close browser context complete \(persistent\)/)).length).toBe(1); + }).toPass(); + +}); + +test('http transport (default)', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint(); + const transport = new StreamableHTTPClientTransport(url); + const client = new Client({ name: 'test', version: '1.0.0' }); + await client.connect(transport); + await client.ping(); + expect(transport.sessionId, 'has session support').toBeDefined(); +}); + +test('client should receive list roots request', async ({ serverEndpoint, server }) => { + const { url } = await serverEndpoint(); + const transport = new StreamableHTTPClientTransport(url); + const client = new Client({ name: 'test', version: '1.0.0' }, { capabilities: { roots: {} } }); + let rootsListedCallback; + const rootsListedPromise = new Promise((resolve, reject) => { + rootsListedCallback = resolve; + setTimeout(() => reject(new Error('timeout waiting for ListRootsRequestSchema')), 5_000); + }); + client.setRequestHandler(ListRootsRequestSchema, async request => { + rootsListedCallback('success'); + return { + roots: [ + { + name: 'test', + uri: 'file://tmp/', + } + ], + }; + }); + await client.connect(transport); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + expect(await rootsListedPromise).toBe('success'); +}); + +test('should not allow rebinding to localhost', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint(); + const response = await fetch(url.href.replace('localhost', '127.0.0.1')); + expect(response.status).toBe(403); + expect(await response.text()).toContain('Access is only allowed at localhost'); +}); + +test('should respect allowed hosts (negative)', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint({ args: ['--allowed-hosts=example.com'] }); + const response = await fetch(url.href); + expect(response.status).toBe(403); + expect(await response.text()).toContain('Access is only allowed at example.com'); +}); + +test('should respect allowed hosts (positive)', async ({ serverEndpoint }) => { + const port = await findFreePort(); + await serverEndpoint({ + args: [ + '--host=127.0.0.1', + '--port=' + port, + '--allowed-hosts=localhost:' + port, + ] + }); + const response = await fetch('http://localhost:' + port); + // 400 is expected for the mcp fetch. + expect(response.status).toBe(400); +}); + +test('should be able to allow any host', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint({ args: ['--allowed-hosts=*'] }); + const response = await fetch(url.href); + // 400 is expected for the mcp fetch. + expect(response.status).toBe(400); + expect(await response.text()).toBe('Invalid request'); +}); + +async function findFreePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.listen(0, () => { + const { port } = server.address() as net.AddressInfo; + server.close(() => resolve(port)); + }); + server.on('error', reject); + }); +} diff --git a/tests/mcp/iframes.spec.ts b/tests/mcp/iframes.spec.ts new file mode 100644 index 0000000000000..3c53de170b718 --- /dev/null +++ b/tests/mcp/iframes.spec.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('stitched aria frames', async ({ client }) => { + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: `data:text/html,

Hello

`, + }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: + - heading "Hello" [level=1] [ref=e2] + - iframe [ref=e3]: + - generic [active] [ref=f1e1]: + - button "World" [ref=f1e2] + - main [ref=f1e3]: + - iframe [ref=f1e4]: + - paragraph [ref=f2e2]: Nested +\`\`\``), + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'World', + ref: 'f1e2', + }, + })).toHaveResponse({ + code: `await page.locator('iframe').first().contentFrame().getByRole('button', { name: 'World' }).click();`, + }); +}); diff --git a/tests/mcp/init-agents.spec.ts b/tests/mcp/init-agents.spec.ts new file mode 100644 index 0000000000000..cedb2bd91dfc3 --- /dev/null +++ b/tests/mcp/init-agents.spec.ts @@ -0,0 +1,124 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, writeFiles } from './fixtures'; +import { spawnAsync } from '../../packages/playwright-core/lib/server/utils/spawnAsync'; + +import path from 'path'; +import fs from 'fs'; + +async function runInitAgents(options?: { args?: string[], cwd?: string }): Promise<{stdout: string, stderr: string, code: number | null, error?: Error}> { + const result = await spawnAsync('npx', [ + 'playwright', 'init-agents', + ...(options?.args || []), + ], { + cwd: options?.cwd, + shell: true, + }); + return result; +} + +test('create seed file default', async ({ }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = {}; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'claude'], + }); + expect(fs.existsSync(path.join(baseDir, 'seed.spec.ts'))).toBe(true); +}); + +test('create seed file with --project', async ({ }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { projects: [{ name: 'foo', testDir: 'foo/e2e' }, { name: 'bar', testDir: 'bar/e2e' }, ] }; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'vscode', '--project', 'bar'], + }); + expect(fs.existsSync(path.join(baseDir, 'bar', 'e2e', 'seed.spec.ts'))).toBe(true); +}); + +test('create seed file with --config', async ({ }) => { + const baseDir = await writeFiles({ + 'custom/playwright.config.ts': ` + module.exports = { projects: [{ name: 'foo', testDir: 'foo/e2e' }, { name: 'bar', testDir: 'bar/e2e' }, ] }; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'vscode', '--config', 'custom/playwright.config.ts', '--project', 'bar'], + }); + expect(fs.existsSync(path.join(baseDir, 'custom', 'bar', 'e2e', 'seed.spec.ts'))).toBe(true); +}); + +test('init claude agents', async ({ }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = {}; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'claude'], + }); + expect(fs.existsSync(path.join(baseDir, '.claude', 'agents', 'playwright-test-planner.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.claude', 'agents', 'playwright-test-generator.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.claude', 'agents', 'playwright-test-healer.md'))).toBe(true); +}); + +test('init vscode agents', async ({ }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = {}; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'vscode'], + }); + expect(fs.existsSync(path.join(baseDir, '.github', 'chatmodes', '🎭 generator.chatmode.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.github', 'chatmodes', ' 🎭 planner.chatmode.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.github', 'chatmodes', '🎭 healer.chatmode.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.vscode', 'mcp.json'))).toBe(true); +}); + +test('init opencode agents', async ({ }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = {}; + `, + }); + + await runInitAgents({ + cwd: baseDir, + args: ['--loop', 'opencode'], + }); + expect(fs.existsSync(path.join(baseDir, '.opencode', 'prompts', 'playwright-test-planner.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.opencode', 'prompts', 'playwright-test-generator.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, '.opencode', 'prompts', 'playwright-test-healer.md'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'opencode.json'))).toBe(true); +}); diff --git a/tests/mcp/init-script.spec.ts b/tests/mcp/init-script.spec.ts new file mode 100644 index 0000000000000..b0c001e5d58b1 --- /dev/null +++ b/tests/mcp/init-script.spec.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; +import fs from 'fs'; + + +for (const context of ['isolated', 'persistent']) { + test(`--init-script option loads and executes script (${context})`, async ({ startClient, server }, testInfo) => { + // Create a temporary init script + const initScriptPath = testInfo.outputPath('init-script1.js'); + const initScriptContent1 = `window.testInitScriptExecuted = true;`; + await fs.promises.writeFile(initScriptPath, initScriptContent1); + + const initScriptPath2 = testInfo.outputPath('init-script2.js'); + const initScriptContent2 = `console.log('Init script executed successfully');`; + await fs.promises.writeFile(initScriptPath2, initScriptContent2); + + // Start the client with the init script option + const { client: client } = await startClient({ + args: [`--init-script=${initScriptPath}`, `--init-script=${initScriptPath2}`, ...(context === 'isolated' ? ['--isolated'] : [])] + }); + + // Navigate to a page and verify the init script was executed + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + await client.callTool({ + name: 'browser_evaluate', + arguments: { function: '() => console.log("Custom log")' } + }); + + // Check that the init script variables are available + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { function: '() => window.testInitScriptExecuted' } + })).toHaveResponse({ + result: 'true', + }); + + expect(await client.callTool({ + name: 'browser_console_messages', + })).toHaveResponse({ + result: expect.stringMatching(/Init script executed successfully.*Custom log/ms), + }); + }); +} + +test('--init-script option with non-existent file throws error', async ({ startClient }, testInfo) => { + const nonExistentPath = testInfo.outputPath('non-existent-script.js'); + + // Attempting to start with a non-existent init script should fail + await expect(startClient({ + args: [`--init-script=${nonExistentPath}`] + })).rejects.toThrow(); +}); diff --git a/packages/playwright-test-mcp/src/context.ts b/tests/mcp/install.spec.ts similarity index 64% rename from packages/playwright-test-mcp/src/context.ts rename to tests/mcp/install.spec.ts index aa20aac081cca..70fde07597ee7 100644 --- a/packages/playwright-test-mcp/src/context.ts +++ b/tests/mcp/install.spec.ts @@ -14,17 +14,13 @@ * limitations under the License. */ -import { TestRunner } from 'playwright/lib/runner/testRunner'; +import { test, expect } from './fixtures'; -import type { ConfigLocation } from 'playwright/lib/common/config'; - -export class Context { - readonly testRunner: TestRunner; - - constructor(configLocation: ConfigLocation) { - this.testRunner = new TestRunner(configLocation, {}); - } - - async close() { - } -} +test('browser_install', async ({ client, mcpBrowser }) => { + test.skip(mcpBrowser !== 'chromium', 'Test only chromium'); + expect(await client.callTool({ + name: 'browser_install', + })).toHaveResponse({ + tabs: expect.stringContaining(`No open tabs`), + }); +}); diff --git a/tests/mcp/launch.spec.ts b/tests/mcp/launch.spec.ts new file mode 100644 index 0000000000000..2d81dcc7d54a5 --- /dev/null +++ b/tests/mcp/launch.spec.ts @@ -0,0 +1,173 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +import { test, expect, formatOutput } from './fixtures'; + +test('test reopen browser', async ({ startClient, server }) => { + const { client, stderr } = await startClient({ + env: { + DEBUG: 'pw:mcp:test', + } + }); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + expect(await client.callTool({ + name: 'browser_close', + })).toHaveResponse({ + code: `await page.close()`, + tabs: `No open tabs. Use the "browser_navigate" tool to navigate to a page first.`, + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`), + }); + + await client.close(); + + if (process.platform === 'win32') + return; + + await expect.poll(() => formatOutput(stderr()), { timeout: 0 }).toEqual([ + 'create context', + 'create browser context (persistent)', + 'lock user data dir', + 'close context', + 'close browser context (persistent)', + 'release user data dir', + 'close browser context complete (persistent)', + 'create browser context (persistent)', + 'lock user data dir', + 'close context', + 'close browser context (persistent)', + 'release user data dir', + 'close browser context complete (persistent)', + ]); +}); + +test('executable path', async ({ startClient, server }) => { + const { client } = await startClient({ args: [`--executable-path=bogus`] }); + const response = await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + expect(response).toHaveResponse({ + result: expect.stringContaining(`executable doesn't exist`), + isError: true, + }); +}); + +test('persistent context', async ({ startClient, server }) => { + server.setContent('/', ` + + + + `, 'text/html'); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Storage: NO`), + }); + + await new Promise(resolve => setTimeout(resolve, 3000)); + + await client.callTool({ + name: 'browser_close', + }); + + const { client: client2 } = await startClient(); + expect(await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Storage: YES`), + }); +}); + +test('isolated context', async ({ startClient, server }) => { + server.setContent('/', ` + + + + `, 'text/html'); + + const { client: client1 } = await startClient({ args: [`--isolated`] }); + expect(await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Storage: NO`), + }); + + await client1.callTool({ + name: 'browser_close', + }); + + const { client: client2 } = await startClient({ args: [`--isolated`] }); + expect(await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Storage: NO`), + }); +}); + +test('isolated context with storage state', async ({ startClient, server }, testInfo) => { + const storageStatePath = testInfo.outputPath('storage-state.json'); + await fs.promises.writeFile(storageStatePath, JSON.stringify({ + origins: [ + { + origin: server.PREFIX, + localStorage: [{ name: 'test', value: 'session-value' }], + }, + ], + })); + + server.setContent('/', ` + + + + `, 'text/html'); + + const { client } = await startClient({ args: [ + `--isolated`, + `--storage-state=${storageStatePath}`, + ] }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + pageState: expect.stringContaining(`Storage: session-value`), + }); +}); diff --git a/tests/mcp/library.spec.ts b/tests/mcp/library.spec.ts new file mode 100644 index 0000000000000..24eaf2d05e31a --- /dev/null +++ b/tests/mcp/library.spec.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import child_process from 'child_process'; +import fs from 'fs/promises'; +import { test, expect } from './fixtures'; + +test('library can be used from CommonJS', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/456' } }, async ({}, testInfo) => { + const file = testInfo.outputPath('main.cjs'); + await fs.writeFile(file, ` + import('playwright/lib/mcp/index') + .then(playwrightMCP => playwrightMCP.createConnection()) + .then(() => console.log('OK')); + `); + expect(child_process.execSync(`node ${file}`, { encoding: 'utf-8' })).toContain('OK'); +}); diff --git a/tests/mcp/mdb.spec.ts b/tests/mcp/mdb.spec.ts new file mode 100644 index 0000000000000..317266cb5b525 --- /dev/null +++ b/tests/mcp/mdb.spec.ts @@ -0,0 +1,294 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { z } from 'zod'; +import zodToJsonSchema from 'zod-to-json-schema'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; + +import { runMainBackend, runOnPauseBackendLoop } from '../../packages/playwright/lib/mcp/sdk/mdb'; +import * as mcpBundle from '../../packages/playwright/lib/mcp/sdk/bundle'; + +import { test, expect } from './fixtures'; + +test('call top level tool', async () => { + const { mdbUrl } = await startMDBAndCLI(); + const mdbClient = await createMDBClient(mdbUrl); + + const { tools } = await mdbClient.client.listTools(); + expect(tools).toEqual([{ + name: 'cli_echo', + description: 'Echo a message', + inputSchema: expect.any(Object), + }, { + name: 'cli_pause_in_gdb', + description: 'Pause in gdb', + inputSchema: expect.any(Object), + }, { + name: 'cli_pause_in_gdb_twice', + description: 'Pause in gdb twice', + inputSchema: expect.any(Object), + }, { + name: 'gdb_bt', + description: 'Print backtrace', + inputSchema: expect.any(Object), + }, { + name: 'gdb_echo', + description: 'Echo a message', + inputSchema: expect.any(Object), + }]); + + const echoResult = await mdbClient.client.callTool({ + name: 'cli_echo', + arguments: { + message: 'Hello, world!', + }, + }); + expect(echoResult.content).toEqual([{ type: 'text', text: 'Echo: Hello, world!, roots: ' }]); + + await mdbClient.close(); +}); + +test('pause on error', async () => { + const { mdbUrl } = await startMDBAndCLI(); + const mdbClient = await createMDBClient(mdbUrl); + + // Make a call that results in a recoverable error. + const interruptResult = await mdbClient.client.callTool({ + name: 'cli_pause_in_gdb', + arguments: {}, + }); + expect(interruptResult.content).toEqual([{ type: 'text', text: 'Paused on exception' }]); + + // Call the new inner tool. + const btResult = await mdbClient.client.callTool({ + name: 'gdb_bt', + arguments: {}, + }); + expect(btResult.content).toEqual([{ type: 'text', text: 'Backtrace' }]); + + await mdbClient.close(); +}); + +test('outer and inner roots available', async () => { + const { mdbUrl } = await startMDBAndCLI(); + const mdbClient = await createMDBClient(mdbUrl, [{ name: 'test', uri: 'file://tmp/' }]); + + expect(await mdbClient.client.callTool({ + name: 'cli_echo', + arguments: { + message: 'Hello, cli!', + }, + })).toEqual({ + content: [{ + type: 'text', + text: 'Echo: Hello, cli!, roots: test=file://tmp/', + }] + }); + + await mdbClient.client.callTool({ + name: 'cli_pause_in_gdb', + arguments: {}, + }); + + expect(await mdbClient.client.callTool({ + name: 'gdb_echo', + arguments: { + message: 'Hello, bt!', + }, + })).toEqual({ + content: [{ + type: 'text', + text: 'Echo: Hello, bt!, roots: test=file://tmp/', + }] + }); + + await mdbClient.close(); +}); + +test('reset on pause tools', async () => { + const { mdbUrl, log } = await startMDBAndCLI(); + const mdbClient = await createMDBClient(mdbUrl); + + // Make a call that results in a recoverable error. + const interruptResult = await mdbClient.client.callTool({ + name: 'cli_pause_in_gdb', + arguments: {}, + }); + expect(interruptResult.content).toEqual([{ type: 'text', text: 'Paused on exception' }]); + + // Call the new inner tool. + const btResult = await mdbClient.client.callTool({ + name: 'gdb_bt', + arguments: {}, + }); + expect(btResult.content).toEqual([{ type: 'text', text: 'Backtrace' }]); + + await mdbClient.client.callTool({ + name: 'cli_echo', + arguments: {}, + }); + + await expect.poll(() => log).toEqual([ + 'CLI: initialize', + 'CLI: callTool cli_pause_in_gdb', + 'GDB: listTools', + 'GDB: initialize', + 'GDB: callTool gdb_bt', + 'CLI: afterCallTool gdb_bt', + 'GDB: serverClosed', + 'CLI: callTool cli_echo', + ]); + + await mdbClient.close(); +}); + +async function startMDBAndCLI(): Promise<{ mdbUrl: string, log: string[] }> { + const mdbUrlBox = { mdbUrl: undefined as string | undefined }; + const log: string[] = []; + const cliBackendFactory = { + name: 'CLI', + nameInConfig: 'cli', + version: '0.0.0', + create: () => new CLIBackend(log) + }; + + const mdbUrl = (await runMainBackend(cliBackendFactory, { port: 0 }))!; + mdbUrlBox.mdbUrl = mdbUrl; + return { mdbUrl, log }; +} + +async function createMDBClient(mdbUrl: string, roots: any[] | undefined = undefined): Promise<{ client: Client, close: () => Promise }> { + const client = new Client({ name: 'Test client', version: '0.0.0' }, roots ? { capabilities: { roots: {} } } : undefined); + if (roots) + client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots })); + const transport = new StreamableHTTPClientTransport(new URL(mdbUrl)); + await client.connect(transport); + return { + client, + close: async () => { + await transport.terminateSession(); + await client.close(); + } + }; +} + +class CLIBackend { + private _roots: any[] | undefined; + private _log: string[] = []; + + constructor(log: string[]) { + this._log = log; + } + + async initialize(server, clientInfo) { + this._log.push('CLI: initialize'); + this._roots = clientInfo.roots; + } + + async listTools() { + this._log.push('CLI: listTools'); + return [{ + name: 'cli_echo', + description: 'Echo a message', + inputSchema: zodToJsonSchema(z.object({ message: z.string() })) as any, + }, { + name: 'cli_pause_in_gdb', + description: 'Pause in gdb', + inputSchema: zodToJsonSchema(z.object({})) as any, + }, { + name: 'cli_pause_in_gdb_twice', + description: 'Pause in gdb twice', + inputSchema: zodToJsonSchema(z.object({})) as any, + }, { + name: 'gdb_bt', + description: 'Print backtrace', + inputSchema: zodToJsonSchema(z.object({})) as any, + }, { + name: 'gdb_echo', + description: 'Echo a message', + inputSchema: zodToJsonSchema(z.object({ message: z.string() })) as any, + }]; + } + + async afterCallTool(name: string, args: any) { + this._log.push(`CLI: afterCallTool ${name}`); + } + + async callTool(name: string, args: any) { + this._log.push(`CLI: callTool ${name}`); + if (name === 'cli_echo') + return { content: [{ type: 'text', text: `Echo: ${args?.message as string}, roots: ${stringifyRoots(this._roots)}` }] }; + if (name === 'cli_pause_in_gdb') { + await runOnPauseBackendLoop(new GDBBackend(this._log), 'Paused on exception'); + return { content: [{ type: 'text', text: 'Done' }] }; + } + if (name === 'cli_pause_in_gdb_twice') { + await runOnPauseBackendLoop(new GDBBackend(this._log), 'Paused on exception 1'); + await runOnPauseBackendLoop(new GDBBackend(this._log), 'Paused on exception 2'); + return { content: [{ type: 'text', text: 'Done' }] }; + } + throw new Error(`Unknown tool: ${name}`); + } + + serverClosed() { + this._log.push('CLI: serverClosed'); + } +} + +class GDBBackend { + private _roots: any[] | undefined; + private _log: string[] = []; + + constructor(log: string[]) { + this._log = log; + } + + async initialize(server, clientVersion) { + this._log.push('GDB: initialize'); + this._roots = clientVersion.roots; + } + + async listTools() { + this._log.push('GDB: listTools'); + return [{ + name: 'gdb_bt', + description: 'Print backtrace', + inputSchema: zodToJsonSchema(z.object({})) as any, + }, { + name: 'gdb_echo', + description: 'Echo a message', + inputSchema: zodToJsonSchema(z.object({ message: z.string() })) as any, + }]; + } + + async callTool(name: string, args: any) { + this._log.push(`GDB: callTool ${name}`); + if (name === 'gdb_echo') + return { content: [{ type: 'text', text: `Echo: ${args?.message as string}, roots: ${stringifyRoots(this._roots)}` }] }; + if (name === 'gdb_bt') + return { content: [{ type: 'text', text: 'Backtrace' }] }; + throw new Error(`Unknown tool: ${name}`); + } + + serverClosed() { + this._log.push('GDB: serverClosed'); + } +} + +function stringifyRoots(roots: any[]) { + return roots.map(root => `${root.name}=${root.uri}`).join(','); +} diff --git a/tests/mcp/network.spec.ts b/tests/mcp/network.spec.ts new file mode 100644 index 0000000000000..a47bcce4ae96d --- /dev/null +++ b/tests/mcp/network.spec.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_network_requests', async ({ client, server }) => { + server.setContent('/', ` + + `, 'text/html'); + + server.setContent('/json', JSON.stringify({ name: 'John Doe' }), 'application/json'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Click me button', + ref: 'e2', + }, + }); + + await expect.poll(() => client.callTool({ + name: 'browser_network_requests', + })).toHaveResponse({ + result: expect.stringContaining(`[GET] ${`${server.PREFIX}/`} => [200] OK +[GET] ${`${server.PREFIX}/json`} => [200] OK`), + }); +}); diff --git a/tests/mcp/pdf.spec.ts b/tests/mcp/pdf.spec.ts new file mode 100644 index 0000000000000..937eb16c879d2 --- /dev/null +++ b/tests/mcp/pdf.spec.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +import { test, expect } from './fixtures'; + +test('save as pdf unavailable', async ({ startClient, server }) => { + const { client } = await startClient(); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + expect(await client.callTool({ + name: 'browser_pdf_save', + })).toHaveResponse({ + result: 'Error: Tool "browser_pdf_save" not found', + isError: true, + }); +}); + +test('save as pdf', async ({ startClient, mcpBrowser, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output'), capabilities: ['pdf'] }, + }); + + test.skip(!!mcpBrowser && !['chromium', 'chrome', 'msedge'].includes(mcpBrowser), 'Save as PDF is only supported in Chromium.'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`), + }); + + expect(await client.callTool({ + name: 'browser_pdf_save', + })).toHaveResponse({ + code: expect.stringContaining(`await page.pdf(`), + result: expect.stringMatching(/Saved page as.*page-[^:]+.pdf/), + }); +}); + +test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + test.skip(!!mcpBrowser && !['chromium', 'chrome', 'msedge'].includes(mcpBrowser), 'Save as PDF is only supported in Chromium.'); + const { client } = await startClient({ + config: { outputDir, capabilities: ['pdf'] }, + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`), + }); + + expect(await client.callTool({ + name: 'browser_pdf_save', + arguments: { + filename: 'output.pdf', + }, + })).toHaveResponse({ + result: expect.stringContaining(`output.pdf`), + code: expect.stringContaining(`await page.pdf(`), + }); + + const files = [...fs.readdirSync(outputDir)]; + + expect(fs.existsSync(outputDir)).toBeTruthy(); + const pdfFiles = files.filter(f => f.endsWith('.pdf')); + expect(pdfFiles).toHaveLength(1); + expect(pdfFiles[0]).toMatch(/^output.pdf$/); +}); diff --git a/tests/mcp/planner.spec.ts b/tests/mcp/planner.spec.ts new file mode 100644 index 0000000000000..64684a56c2742 --- /dev/null +++ b/tests/mcp/planner.spec.ts @@ -0,0 +1,251 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, writeFiles } from './fixtures'; + +import fs from 'fs'; +import path from 'path'; +import url from 'url'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('planner_setup_page', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test.beforeEach(async ({ page }) => { + await page.setContent(''); + }); + test('template', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + const response = await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'a.test.ts', + }, + }); + + expect(response).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction + +### Page state +- Page URL: about:blank +- Page Title: +- Page Snapshot: +\`\`\`yaml +- button "Submit" [ref=e2] +\`\`\` +`)); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click();`, + pageState: expect.stringContaining(`- button "Submit"`), + }); +}); + +test('planner_setup_page seed resolution', async ({ startClient }) => { + await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + testDir: './tests', + }; + `, + 'tests/seed.test.ts': ` + import { test, expect } from '@playwright/test'; + test('template', async ({ page }) => { + await page.setContent(''); + }); + `, + }); + + const { client } = await startClient(); + + // Relative to test dir. + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'seed.test.ts', + }, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test.`)); + + // Relative to config dir. + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'tests/seed.test.ts', + }, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test.`)); +}); + +test('planner_setup_page seed resolution - rootPath', async ({ startClient }) => { + await writeFiles({ + 'packages/my-app/configs/playwright.config.ts': ` + module.exports = { + testDir: '../tests', + }; + `, + 'packages/my-app/tests/seed.test.ts': ` + import { test, expect } from '@playwright/test'; + test('template', async ({ page }) => { + await page.setContent(''); + }); + `, + }); + + const { client } = await startClient({ + args: ['--config=packages/my-app/configs/playwright.config.ts'], + roots: [{ name: 'root', uri: url.pathToFileURL(test.info().outputPath('')).toString() }], + }); + + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'packages/my-app/tests/seed.test.ts', + }, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test.`)); +}); + +test('planner_setup_page with dependencies', async ({ startClient }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'setup', testMatch: /.*setup\\.ts/ }, + { name: 'chromium', dependencies: ['setup'] }, + { name: 'ignored', dependencies: ['chromium'] }, + ], + }; + `, + 'auth.setup.ts': ` + import { test as setup, expect } from '@playwright/test'; + setup('auth', async ({ page }, testInfo) => { + require('fs').writeFileSync(testInfo.outputPath('auth.txt'), 'done'); + }); + `, + 'seed.test.ts': ` + import { test, expect } from '@playwright/test'; + test('template', async ({ page }, testInfo) => { + require('fs').writeFileSync(testInfo.outputPath('template.txt'), 'done'); + }); + `, + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'seed.test.ts', + project: 'chromium', + }, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + // Should pause at the target test, not in a dependency or any other stray project. + expect(fs.existsSync(path.join(baseDir, 'test-results', 'auth.setup.ts-auth-setup', 'auth.txt'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'test-results', 'seed-template-chromium', 'template.txt'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'test-results', 'seed-template-ignored', 'template.txt'))).toBe(false); +}); + +test('planner_setup_page (loading error)', async ({ startClient }) => { + await writeFiles({ + 'seed.test.ts': ` + throw new Error('loading error'); + `, + }); + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'seed.test.ts', + }, + })).toHaveTextResponse(expect.stringContaining('Error: loading error')); +}); + +test('planner_setup_page (wrong test location)', async ({ startClient }) => { + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: { + seedFile: 'a.test.ts', + }, + })).toEqual({ + content: [{ type: 'text', text: `Error: seed test not found.` }], + isError: true, + }); +}); + +test('planner_setup_page (no test location)', async ({ startClient }) => { + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); +}); + +test('planner_setup_page chooses top-level project', async ({ startClient }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'one', testDir: './one' }, + { name: 'two', testDir: './two', dependencies: ['one'] }, + ], + }; + `, + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + expect(fs.existsSync(path.join(baseDir, 'one', 'seed.spec.ts'))).toBe(false); + expect(fs.existsSync(path.join(baseDir, 'two', 'seed.spec.ts'))).toBe(true); +}); + +test('planner_setup_page without location respects testsDir', async ({ startClient }) => { + await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + testDir: './tests', + projects: [{ name: 'foo' }] + }; + `, + + 'tests/a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('existing', async ({ page }) => { + }); + `, + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + expect(fs.existsSync(test.info().outputPath('tests', 'seed.spec.ts'))).toBe(true); +}); diff --git a/packages/playwright-test-mcp/src/tool.ts b/tests/mcp/playwright.config.extension.ts similarity index 59% rename from packages/playwright-test-mcp/src/tool.ts rename to tests/mcp/playwright.config.extension.ts index 28b05159d4c1b..526fda2001f51 100644 --- a/packages/playwright-test-mcp/src/tool.ts +++ b/tests/mcp/playwright.config.extension.ts @@ -14,15 +14,19 @@ * limitations under the License. */ -import type { z } from 'zod'; -import type { Context } from './context.js'; -import type * as mcp from 'playwright/src/mcp/exports.js'; +import { defineConfig } from '@playwright/test'; -export type Tool = { - schema: mcp.ToolSchema; - handle: (context: Context, params: z.output) => Promise; -}; +import type { TestOptions } from './fixtures'; -export function defineTool(tool: Tool): Tool { - return tool; -} +export default defineConfig({ + testDir: './', + grep: /extension/, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'list', + projects: [ + { name: 'chromium', use: { mcpBrowser: 'chromium' } }, + ], +}); diff --git a/tests/mcp/playwright.config.ts b/tests/mcp/playwright.config.ts new file mode 100644 index 0000000000000..11ab165bf8864 --- /dev/null +++ b/tests/mcp/playwright.config.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as path from 'path'; + +import { defineConfig } from '@playwright/test'; + +import type { TestOptions } from './fixtures'; +import type { ReporterDescription } from '@playwright/test'; + +const rootTestDir = path.join(__dirname, '..'); +const testDir = path.join(rootTestDir, 'mcp'); +const outputDir = path.join(__dirname, '..', '..', 'test-results'); + +const reporters = () => { + const result: ReporterDescription[] = process.env.CI ? [ + ['dot'], + ['json', { outputFile: path.join(outputDir, 'report.json') }], + ['blob', { outputDir: path.join(__dirname, '..', '..', 'blob-report'), fileName: `${process.env.PWTEST_BOT_NAME}.zip` }], + ] : [ + ['list'] + ]; + return result; +}; + +const metadata = { + platform: process.platform, + headless: 'headless', + mode: 'default', + video: false, +}; + +export default defineConfig({ + testDir: rootTestDir, + grepInvert: /extension/, + fullyParallel: true, + forbidOnly: !!process.env.CI, + workers: process.env.CI ? 2 : undefined, + reporter: reporters(), + projects: [ + { name: 'chrome', metadata: { ...metadata, browserName: 'chromium', channel: 'chrome' }, testDir }, + { name: 'chromium', use: { mcpBrowser: 'chromium' }, metadata: { ...metadata, browserName: 'chromium' }, testDir }, + { name: 'firefox', use: { mcpBrowser: 'firefox' }, metadata: { ...metadata, browserName: 'firefox' }, testDir }, + { name: 'webkit', use: { mcpBrowser: 'webkit' }, metadata: { ...metadata, browserName: 'webkit' }, testDir }, + ... process.platform === 'win32' ? [{ name: 'msedge', use: { mcpBrowser: 'msedge' }, metadata: { ...metadata, browserName: 'chromium', channel: 'msedge' }, testDir }] : [], + ], +}); diff --git a/tests/mcp/request-blocking.spec.ts b/tests/mcp/request-blocking.spec.ts new file mode 100644 index 0000000000000..a2ca31879ba70 --- /dev/null +++ b/tests/mcp/request-blocking.spec.ts @@ -0,0 +1,127 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { test, expect } from './fixtures'; + +const BLOCK_MESSAGE = /Blocked by Web Inspector|NS_ERROR_FAILURE|net::ERR_BLOCKED_BY_CLIENT/g; + +const fetchPage = async (client: Client, url: string) => { + const result = await client.callTool({ + name: 'browser_navigate', + arguments: { + url, + }, + }); + + return JSON.stringify(result, null, 2); +}; + +test('default to allow all', async ({ server, client }) => { + server.setContent('/ppp', 'content:PPP', 'text/html'); + const result = await fetchPage(client, server.PREFIX + '/ppp'); + expect(result).toContain('content:PPP'); +}); + +test('blocked works (hostname)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--blocked-origins', 'microsoft.com;example.com;playwright.dev'] + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('blocked works (origin)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--blocked-origins', 'https://microsoft.com;https://example.com;https://playwright.dev'] + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('allowed works (hostname)', async ({ server, startClient }) => { + server.setContent('/ppp', 'content:PPP', 'text/html'); + const { client } = await startClient({ + args: ['--allowed-origins', `microsoft.com;${new URL(server.PREFIX).host};playwright.dev`] + }); + const result = await fetchPage(client, server.PREFIX + '/ppp'); + expect(result).toContain('content:PPP'); +}); + +test('allowed works (origin)', async ({ server, startClient }) => { + server.setContent('/ppp', 'content:PPP', 'text/html'); + const { client } = await startClient({ + args: ['--allowed-origins', `https://microsoft.com;${new URL(server.PREFIX).origin};https://playwright.dev`] + }); + const result = await fetchPage(client, server.PREFIX + '/ppp'); + expect(result).toContain('content:PPP'); +}); + +test('blocked takes precedence (hostname)', async ({ startClient }) => { + const { client } = await startClient({ + args: [ + '--blocked-origins', 'example.com', + '--allowed-origins', 'example.com', + ], + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('blocked takes precedence (origin)', async ({ startClient }) => { + const { client } = await startClient({ + args: [ + '--blocked-origins', 'https://example.com', + '--allowed-origins', 'https://example.com', + ], + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('allowed without blocked blocks all non-explicitly specified origins (hostname)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--allowed-origins', 'playwright.dev'], + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('allowed without blocked blocks all non-explicitly specified origins (origin)', async ({ startClient }) => { + const { client } = await startClient({ + args: ['--allowed-origins', 'https://playwright.dev'], + }); + const result = await fetchPage(client, 'https://example.com/'); + expect(result).toMatch(BLOCK_MESSAGE); +}); + +test('blocked without allowed allows non-explicitly specified origins (hostname)', async ({ server, startClient }) => { + server.setContent('/ppp', 'content:PPP', 'text/html'); + const { client } = await startClient({ + args: ['--blocked-origins', 'example.com'], + }); + const result = await fetchPage(client, server.PREFIX + '/ppp'); + expect(result).toContain('content:PPP'); +}); + +test('blocked without allowed allows non-explicitly specified origins (origin)', async ({ server, startClient }) => { + server.setContent('/ppp', 'content:PPP', 'text/html'); + const { client } = await startClient({ + args: ['--blocked-origins', 'https://example.com'], + }); + const result = await fetchPage(client, server.PREFIX + '/ppp'); + expect(result).toContain('content:PPP'); +}); diff --git a/tests/mcp/roots.spec.ts b/tests/mcp/roots.spec.ts new file mode 100644 index 0000000000000..3a24b33e77863 --- /dev/null +++ b/tests/mcp/roots.spec.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import { pathToFileURL } from 'url'; + +import { test, expect } from './fixtures'; + +const p = process.platform === 'win32' ? 'c:\\non\\existent\\folder' : '/non/existent/folder'; + +test('should use separate user data by root path', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + clientName: 'Visual Studio Code', + roots: [ + { + name: 'test', + uri: 'file://' + p.replace(/\\/g, '/'), + } + ], + }); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + const hash = createHash(p); + const [file] = await fs.promises.readdir(testInfo.outputPath('ms-playwright')); + expect(file).toContain(hash); +}); + +test('check that trace is saved in workspace', async ({ startClient, server }, testInfo) => { + const rootPath = testInfo.outputPath('workspace'); + const { client } = await startClient({ + args: ['--save-trace'], + clientName: 'My client', + roots: [ + { + name: 'workspace', + uri: pathToFileURL(rootPath).toString(), + }, + ], + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + const [file] = await fs.promises.readdir(path.join(rootPath, '.playwright-mcp')); + expect(file).toContain('traces'); +}); + +test('should list all tools when listRoots is slow', async ({ startClient }) => { + const { client } = await startClient({ + clientName: 'Another custom client', + roots: [], + rootsResponseDelay: 1000, + }); + const tools = await client.listTools(); + expect(tools.tools.length).toBeGreaterThan(10); +}); + +test('should tolerate malformed roots', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + clientName: 'Visual Studio Code', + roots: [ + { + name: 'test', + uri: 'bogus://' + p.replace(/\\/g, '/'), + } + ], + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + const [file] = await fs.promises.readdir(testInfo.outputPath('ms-playwright')); + expect(file).toMatch(/mcp-.*/); +}); + +function createHash(data: string): string { + return crypto.createHash('sha256').update(data).digest('hex').slice(0, 7); +} diff --git a/tests/mcp/screenshot.spec.ts b/tests/mcp/screenshot.spec.ts new file mode 100644 index 0000000000000..50b2eaeaff5f3 --- /dev/null +++ b/tests/mcp/screenshot.spec.ts @@ -0,0 +1,327 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +import { test, expect } from './fixtures'; + +test('browser_take_screenshot (viewport)', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + })).toHaveResponse({ + code: expect.stringContaining(`await page.screenshot`), + attachments: [{ + data: expect.any(String), + mimeType: 'image/png', + type: 'image', + }], + }); +}); + +test('browser_take_screenshot (element)', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`[ref=e1]`), + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + arguments: { + element: 'hello button', + ref: 'e1', + }, + })).toEqual({ + content: [ + { + text: expect.stringContaining(`page.getByText('Hello, world!').screenshot`), + type: 'text', + }, + { + data: expect.any(String), + mimeType: 'image/png', + type: 'image', + }, + ], + }); +}); + +test('--output-dir should work', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + const { client } = await startClient({ + config: { outputDir }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + await client.callTool({ + name: 'browser_take_screenshot', + }); + + expect(fs.existsSync(outputDir)).toBeTruthy(); + const files = [...fs.readdirSync(outputDir)].filter(f => f.endsWith('.png')); + expect(files).toHaveLength(1); + expect(files[0]).toMatch(/^page-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.png$/); +}); + +for (const type of ['png', 'jpeg']) { + test(`browser_take_screenshot (type: ${type})`, async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + const { client } = await startClient({ + config: { outputDir }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + arguments: { type }, + })).toEqual({ + content: [ + { + text: expect.stringMatching( + new RegExp(`page-\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\-\\d{3}Z\\.${type}`) + ), + type: 'text', + }, + { + data: expect.any(String), + mimeType: `image/${type}`, + type: 'image', + }, + ], + }); + + const files = [...fs.readdirSync(outputDir)].filter(f => f.endsWith(`.${type}`)); + + expect(fs.existsSync(outputDir)).toBeTruthy(); + expect(files).toHaveLength(1); + expect(files[0]).toMatch( + new RegExp(`^page-\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}-\\d{3}Z\\.${type}$`) + ); + }); + +} + +test('browser_take_screenshot (default type should be png)', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + const { client } = await startClient({ + config: { outputDir }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + })).toHaveResponse({ + code: `await page.goto('${server.PREFIX}');`, + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + })).toEqual({ + content: [ + { + text: expect.stringMatching( + new RegExp(`page-\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\-\\d{3}Z\\.png`) + ), + type: 'text', + }, + { + data: expect.any(String), + mimeType: 'image/png', + type: 'image', + }, + ], + }); + + const files = [...fs.readdirSync(outputDir)].filter(f => f.endsWith('.png')); + + expect(fs.existsSync(outputDir)).toBeTruthy(); + expect(files).toHaveLength(1); + expect(files[0]).toMatch(/^page-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z\.png$/); +}); + +test('browser_take_screenshot (filename: "output.png")', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + const { client } = await startClient({ + config: { outputDir }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + arguments: { + filename: 'output.png', + }, + })).toEqual({ + content: [ + { + text: expect.stringContaining(`output.png`), + type: 'text', + }, + { + data: expect.any(String), + mimeType: 'image/png', + type: 'image', + }, + ], + }); + + const files = [...fs.readdirSync(outputDir)].filter(f => f.endsWith('.png')); + + expect(fs.existsSync(outputDir)).toBeTruthy(); + expect(files).toHaveLength(1); + expect(files[0]).toMatch(/^output\.png$/); +}); + +test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + const { client } = await startClient({ + config: { + outputDir, + imageResponses: 'omit', + }, + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + await client.callTool({ + name: 'browser_take_screenshot', + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + })).toEqual({ + content: [ + { + text: expect.stringContaining(`await page.screenshot`), + type: 'text', + }, + ], + }); +}); + +test('browser_take_screenshot (fullPage: true)', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_take_screenshot', + arguments: { fullPage: true }, + })).toEqual({ + content: [ + { + text: expect.stringContaining('fullPage: true'), + type: 'text', + } + ], + }); +}); + +test('browser_take_screenshot (fullPage with element should error)', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`[ref=e1]`), + }); + + const result = await client.callTool({ + name: 'browser_take_screenshot', + arguments: { + fullPage: true, + element: 'hello button', + ref: 'e1', + }, + }); + + expect(result.isError).toBe(true); + expect(result.content?.[0]?.text).toContain('fullPage cannot be used with element screenshots'); +}); + +test('browser_take_screenshot (viewport without snapshot)', async ({ startClient, server }, testInfo) => { + const { client } = await startClient({ + config: { outputDir: testInfo.outputPath('output') }, + }); + + // Ensure we have a tab but don't navigate anywhere (no snapshot captured) + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'list', + }, + })).toHaveResponse({ + tabs: `- 0: (current) [] (about:blank)`, + }); + + // This should work without requiring a snapshot since it's a viewport screenshot + expect(await client.callTool({ + name: 'browser_take_screenshot', + })).toEqual({ + content: [ + { + text: expect.stringContaining(`page.screenshot`), + type: 'text', + }, + { + data: expect.any(String), + mimeType: 'image/png', + type: 'image', + }, + ], + }); +}); diff --git a/tests/mcp/secrets.spec.ts b/tests/mcp/secrets.spec.ts new file mode 100644 index 0000000000000..0f8cb47095908 --- /dev/null +++ b/tests/mcp/secrets.spec.ts @@ -0,0 +1,128 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'node:fs'; + +import { test, expect } from './fixtures'; + +test('browser_type', async ({ startClient, server }) => { + const secretsFile = test.info().outputPath('secrets.env'); + await fs.promises.writeFile(secretsFile, 'X-PASSWORD=password123'); + + const { client } = await startClient({ + args: ['--secrets', secretsFile], + }); + + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + { + const response = await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'X-PASSWORD', + submit: true, + }, + }); + expect(response).toHaveResponse({ + code: `await page.getByRole('textbox').fill(process.env['X-PASSWORD']); +await page.getByRole('textbox').press('Enter');`, + pageState: expect.stringContaining(`- textbox`), + }); + } + + expect(await client.callTool({ + name: 'browser_console_messages', + })).toHaveResponse({ + result: expect.stringContaining(`[LOG] Key pressed: Enter , Text: X-PASSWORD`), + }); +}); + + +test('browser_fill_form', async ({ startClient, server }) => { + const secretsFile = test.info().outputPath('secrets.env'); + await fs.promises.writeFile(secretsFile, 'X-PASSWORD=password123'); + + const { client } = await startClient({ + args: ['--secrets', secretsFile], + }); + + server.setContent('/', ` + + + +
+ + +
+ + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_fill_form', + arguments: { + fields: [ + { + name: 'Email textbox', + type: 'textbox', + ref: 'e4', + value: 'John Doe' + }, + { + name: 'Password textbox', + type: 'textbox', + ref: 'e6', + value: 'X-PASSWORD' + }, + ] + }, + })).toHaveResponse({ + code: `await page.getByRole('textbox', { name: 'Email' }).fill('John Doe'); +await page.getByRole('textbox', { name: 'Password' }).fill(process.env['X-PASSWORD']);`, + }); + + expect(await client.callTool({ + name: 'browser_snapshot', + arguments: {}, + })).toHaveResponse({ + pageState: expect.stringContaining(`- textbox \"Password\" [active] [ref=e6]: X-PASSWORD`), + }); +}); diff --git a/tests/mcp/seed-default-project.spec.ts b/tests/mcp/seed-default-project.spec.ts new file mode 100644 index 0000000000000..c0acdef706d2e --- /dev/null +++ b/tests/mcp/seed-default-project.spec.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; +import { test, expect, writeFiles } from './fixtures'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('seed test runs in first top-level project by default', async ({ startClient }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'first-project', testDir: './first' }, + { name: 'second-project', testDir: './second' }, + { name: 'third-project', testDir: './third' }, + ], + }; + `, + }); + + const { client } = await startClient(); + + // Call planner_setup_page without specifying a project - should use first top-level project + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + // Verify seed.spec.ts was created in the first project's testDir + expect(fs.existsSync(path.join(baseDir, 'first', 'seed.spec.ts'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'second', 'seed.spec.ts'))).toBe(false); + expect(fs.existsSync(path.join(baseDir, 'third', 'seed.spec.ts'))).toBe(false); +}); + +test('seed test runs in first top-level project with dependencies', async ({ startClient }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'setup', testDir: './setup', testMatch: /.*setup\\.ts/ }, + { name: 'first-top-level', testDir: './first', dependencies: ['setup'] }, + { name: 'second-top-level', testDir: './second', dependencies: ['setup'] }, + { name: 'third-top-level', testDir: './third' }, + ], + }; + `, + }); + + const { client } = await startClient(); + + // Call planner_setup_page without specifying a project + // Should use the first top-level project (first-top-level, not setup which is a dependency) + expect(await client.callTool({ + name: 'planner_setup_page', + arguments: {}, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + // Verify seed.spec.ts was created in the first top-level project's testDir + expect(fs.existsSync(path.join(baseDir, 'setup', 'seed.spec.ts'))).toBe(false); + expect(fs.existsSync(path.join(baseDir, 'first', 'seed.spec.ts'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'second', 'seed.spec.ts'))).toBe(false); + expect(fs.existsSync(path.join(baseDir, 'third', 'seed.spec.ts'))).toBe(false); +}); + +test('generator_setup_page uses first top-level project by default', async ({ startClient }) => { + const baseDir = await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'alpha', testDir: './alpha' }, + { name: 'beta', testDir: './beta', dependencies: ['alpha'] }, + { name: 'gamma', testDir: './gamma' }, + ], + }; + `, + }); + + const { client } = await startClient(); + + // Call generator_setup_page without specifying a project - should use first top-level project + expect(await client.callTool({ + name: 'generator_setup_page', + arguments: { + plan: 'Test plan for verification', + }, + })).toHaveTextResponse(expect.stringContaining(`### Paused at end of test. ready for interaction`)); + + // Verify seed.spec.ts was created in the first project's testDir + expect(fs.existsSync(path.join(baseDir, 'alpha', 'seed.spec.ts'))).toBe(false); + expect(fs.existsSync(path.join(baseDir, 'beta', 'seed.spec.ts'))).toBe(true); + expect(fs.existsSync(path.join(baseDir, 'gamma', 'seed.spec.ts'))).toBe(false); +}); diff --git a/tests/mcp/session-log.spec.ts b/tests/mcp/session-log.spec.ts new file mode 100644 index 0000000000000..6f9a98ee66cb6 --- /dev/null +++ b/tests/mcp/session-log.spec.ts @@ -0,0 +1,275 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; + +import { test, expect } from './fixtures'; + +test('session log should record tool calls', async ({ startClient, server }, testInfo) => { + const { client, stderr } = await startClient({ + args: [ + '--save-session', + '--output-dir', testInfo.outputPath('output'), + ], + }); + + server.setContent('/', `Title`, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Submit button', + ref: 'e2', + }, + })).toHaveResponse({ + code: `await page.getByRole('button', { name: 'Submit' }).click();`, + pageState: expect.stringContaining(`- button "Submit"`), + }); + + const output = stderr().split('\n').filter(line => line.startsWith('Session: '))[0]; + const sessionFolder = output.substring('Session: '.length); + await expect.poll(() => readSessionLog(sessionFolder)).toBe(` +### Tool call: browser_navigate +- Args +\`\`\`json +{ + "url": "http://localhost:${server.PORT}" +} +\`\`\` +- Code +\`\`\`js +await page.goto('http://localhost:${server.PORT}'); +\`\`\` +- Snapshot: 001.snapshot.yml + + +### Tool call: browser_click +- Args +\`\`\`json +{ + "element": "Submit button", + "ref": "e2" +} +\`\`\` +- Code +\`\`\`js +await page.getByRole('button', { name: 'Submit' }).click(); +\`\`\` +- Snapshot: 002.snapshot.yml + +`); +}); + +test('session log should record user action', async ({ cdpServer, startClient }, testInfo) => { + const browserContext = await cdpServer.start(); + const { client, stderr } = await startClient({ + args: [ + '--save-session', + '--output-dir', testInfo.outputPath('output'), + `--cdp-endpoint=${cdpServer.endpoint}`, + ], + }); + + // Force browser context creation. + await client.callTool({ + name: 'browser_snapshot', + }); + + const [page] = browserContext.pages(); + await page.setContent(` + + + `); + + await page.getByRole('button', { name: 'Button 1' }).click(); + + const output = stderr().split('\n').filter(line => line.startsWith('Session: '))[0]; + const sessionFolder = output.substring('Session: '.length); + + await expect.poll(() => readSessionLog(sessionFolder)).toBe(` +### Tool call: browser_snapshot +- Args +\`\`\`json +{} +\`\`\` +- Snapshot: 001.snapshot.yml + + +### User action: click +- Args +\`\`\`json +{ + "name": "click", + "ref": "e2", + "button": "left", + "modifiers": 0, + "clickCount": 1 +} +\`\`\` +- Code +\`\`\`js +await page.getByRole('button', { name: 'Button 1' }).click(); +\`\`\` +- Snapshot: 002.snapshot.yml + +`); +}); + +test('session log should update user action', async ({ cdpServer, startClient }, testInfo) => { + const browserContext = await cdpServer.start(); + const { client, stderr } = await startClient({ + args: [ + '--save-session', + '--output-dir', testInfo.outputPath('output'), + `--cdp-endpoint=${cdpServer.endpoint}`, + ], + }); + + // Force browser context creation. + await client.callTool({ + name: 'browser_snapshot', + }); + + const [page] = browserContext.pages(); + await page.setContent(` + + + `); + + await page.getByRole('button', { name: 'Button 1' }).dblclick(); + + const output = stderr().split('\n').filter(line => line.startsWith('Session: '))[0]; + const sessionFolder = output.substring('Session: '.length); + + await expect.poll(() => readSessionLog(sessionFolder)).toBe(` +### Tool call: browser_snapshot +- Args +\`\`\`json +{} +\`\`\` +- Snapshot: 001.snapshot.yml + + +### User action: click +- Args +\`\`\`json +{ + "name": "click", + "ref": "e2", + "button": "left", + "modifiers": 0, + "clickCount": 2 +} +\`\`\` +- Code +\`\`\`js +await page.getByRole('button', { name: 'Button 1' }).dblclick(); +\`\`\` +- Snapshot: 002.snapshot.yml + +`); +}); + +test('session log should record tool calls and user actions', async ({ cdpServer, startClient }, testInfo) => { + const browserContext = await cdpServer.start(); + const { client, stderr } = await startClient({ + args: [ + '--save-session', + '--output-dir', testInfo.outputPath('output'), + `--cdp-endpoint=${cdpServer.endpoint}`, + ], + }); + + const [page] = browserContext.pages(); + await page.setContent(` + + + `); + + await client.callTool({ + name: 'browser_snapshot', + }); + + // Manual action. + await page.getByRole('button', { name: 'Button 1' }).click(); + + // This is to simulate a delay after the user action before the tool action. + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Tool action. + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Button 2', + ref: 'e3', + }, + }); + + const output = stderr().split('\n').filter(line => line.startsWith('Session: '))[0]; + const sessionFolder = output.substring('Session: '.length); + await expect.poll(() => readSessionLog(sessionFolder)).toBe(` +### Tool call: browser_snapshot +- Args +\`\`\`json +{} +\`\`\` +- Snapshot: 001.snapshot.yml + + +### User action: click +- Args +\`\`\`json +{ + "name": "click", + "ref": "e2", + "button": "left", + "modifiers": 0, + "clickCount": 1 +} +\`\`\` +- Code +\`\`\`js +await page.getByRole('button', { name: 'Button 1' }).click(); +\`\`\` +- Snapshot: 002.snapshot.yml + + +### Tool call: browser_click +- Args +\`\`\`json +{ + "element": "Button 2", + "ref": "e3" +} +\`\`\` +- Code +\`\`\`js +await page.getByRole('button', { name: 'Button 2' }).click(); +\`\`\` +- Snapshot: 003.snapshot.yml + +`); +}); + +async function readSessionLog(sessionFolder: string): Promise { + return await fs.promises.readFile(path.join(sessionFolder, 'session.md'), 'utf8').catch(() => ''); +} diff --git a/tests/mcp/sse.spec.ts b/tests/mcp/sse.spec.ts new file mode 100644 index 0000000000000..c1c8c9cdf54a5 --- /dev/null +++ b/tests/mcp/sse.spec.ts @@ -0,0 +1,294 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +import { ChildProcess, spawn } from 'child_process'; +import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { test as baseTest, expect, mcpServerPath } from './fixtures'; + +import type { Config } from '../../packages/playwright/src/mcp/config'; + +const test = baseTest.extend<{ serverEndpoint: (options?: { args?: string[], noPort?: boolean }) => Promise<{ url: URL, stderr: () => string }> }>({ + serverEndpoint: async ({ mcpHeadless }, use, testInfo) => { + let cp: ChildProcess | undefined; + const userDataDir = testInfo.outputPath('user-data-dir'); + await use(async (options?: { args?: string[], noPort?: boolean }) => { + if (cp) + throw new Error('Process already running'); + + cp = spawn('node', [ + ...mcpServerPath, + ...(options?.noPort ? [] : ['--port=0']), + '--user-data-dir=' + userDataDir, + ...(mcpHeadless ? ['--headless'] : []), + ...(options?.args || []), + ], { + stdio: 'pipe', + env: { + ...process.env, + DEBUG: 'pw:mcp:test', + DEBUG_COLORS: '0', + DEBUG_HIDE_DATE: '1', + }, + }); + let stderr = ''; + const url = await new Promise(resolve => cp!.stderr?.on('data', data => { + stderr += data.toString(); + const match = stderr.match(/Listening on (http:\/\/.*)/); + if (match) + resolve(match[1]); + })); + + return { url: new URL(url), stderr: () => stderr }; + }); + cp?.kill('SIGTERM'); + }, +}); + +test('sse transport', async ({ serverEndpoint }) => { + const { url } = await serverEndpoint(); + const transport = new SSEClientTransport(new URL('/sse', url)); + const client = new Client({ name: 'test', version: '1.0.0' }); + await client.connect(transport); + await client.ping(); +}); + +test('sse transport (config)', async ({ serverEndpoint }) => { + const config: Config = { + server: { + port: 0, + } + }; + const configFile = test.info().outputPath('config.json'); + await fs.promises.writeFile(configFile, JSON.stringify(config, null, 2)); + + const { url } = await serverEndpoint({ noPort: true, args: ['--config=' + configFile] }); + const transport = new SSEClientTransport(new URL('/sse', url)); + const client = new Client({ name: 'test', version: '1.0.0' }); + await client.connect(transport); + await client.ping(); +}); + +test('sse transport browser lifecycle (isolated)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--isolated'] }); + + const transport1 = new SSEClientTransport(new URL('/sse', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await client1.close(); + + const transport2 = new SSEClientTransport(new URL('/sse', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create SSE session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete SSE session/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create browser context \(isolated\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser context \(isolated\)/)).length).toBe(2); + + expect(lines.filter(line => line.match(/obtain browser \(isolated\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser \(isolated\)/)).length).toBe(2); + }).toPass(); +}); + +test('sse transport browser lifecycle (isolated, multiclient)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--isolated'] }); + + const transport1 = new SSEClientTransport(new URL('/sse', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + const transport2 = new SSEClientTransport(new URL('/sse', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await client1.close(); + + const transport3 = new SSEClientTransport(new URL('/sse', url)); + const client3 = new Client({ name: 'test', version: '1.0.0' }); + await client3.connect(transport3); + await client3.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + await client2.close(); + await client3.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create SSE session/)).length).toBe(3); + expect(lines.filter(line => line.match(/delete SSE session/)).length).toBe(3); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(3); + expect(lines.filter(line => line.match(/close context/)).length).toBe(3); + + expect(lines.filter(line => line.match(/create browser context \(isolated\)/)).length).toBe(3); + expect(lines.filter(line => line.match(/close browser context \(isolated\)/)).length).toBe(3); + + expect(lines.filter(line => line.match(/obtain browser \(isolated\)/)).length).toBe(1); + expect(lines.filter(line => line.match(/close browser \(isolated\)/)).length).toBe(1); + }).toPass(); +}); + +test('sse transport browser lifecycle (persistent)', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint(); + + const transport1 = new SSEClientTransport(new URL('/sse', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await client1.close(); + + const transport2 = new SSEClientTransport(new URL('/sse', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create SSE session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete SSE session/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + expect(lines.filter(line => line.match(/create browser context \(persistent\)/)).length).toBe(2); + expect(lines.filter(line => line.match(/close browser context \(persistent\)/)).length).toBe(2); + + expect(lines.filter(line => line.match(/lock user data dir/)).length).toBe(2); + expect(lines.filter(line => line.match(/release user data dir/)).length).toBe(2); + }).toPass(); +}); + +test('sse transport browser lifecycle (persistent, multiclient)', async ({ serverEndpoint, server }) => { + const { url } = await serverEndpoint(); + + const transport1 = new SSEClientTransport(new URL('/sse', url)); + const client1 = new Client({ name: 'test', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + const transport2 = new SSEClientTransport(new URL('/sse', url)); + const client2 = new Client({ name: 'test', version: '1.0.0' }); + await client2.connect(transport2); + const response = await client2.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + expect(response.isError).toBe(true); + expect(response.content?.[0].text).toContain('use --isolated to run multiple instances of the same browser'); + + await client1.close(); + await client2.close(); +}); + +test('sse transport shared context', async ({ serverEndpoint, server }) => { + const { url, stderr } = await serverEndpoint({ args: ['--shared-browser-context'] }); + + // Create first client and navigate + const transport1 = new SSEClientTransport(new URL('/sse', url)); + const client1 = new Client({ name: 'test1', version: '1.0.0' }); + await client1.connect(transport1); + await client1.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + // Create second client - should reuse the same browser context + const transport2 = new SSEClientTransport(new URL('/sse', url)); + const client2 = new Client({ name: 'test2', version: '1.0.0' }); + await client2.connect(transport2); + + // Get tabs from second client - should see the tab created by first client + const tabsResult = await client2.callTool({ + name: 'browser_tabs', + arguments: { action: 'list' }, + }); + + // Should have at least one tab (the one created by client1) + expect(tabsResult.content[0]?.text).toContain('tabs'); + + await client1.close(); + + // Second client should still work since context is shared + await client2.callTool({ + name: 'browser_snapshot', + arguments: {}, + }); + + await client2.close(); + + await expect(async () => { + const lines = stderr().split('\n'); + expect(lines.filter(line => line.match(/create SSE session/)).length).toBe(2); + expect(lines.filter(line => line.match(/delete SSE session/)).length).toBe(2); + + // Should have only one context creation since it's shared + expect(lines.filter(line => line.match(/create shared browser context/)).length).toBe(1); + + // Should see client connect/disconnect messages + expect(lines.filter(line => line.match(/shared context client connected/)).length).toBe(2); + expect(lines.filter(line => line.match(/shared context client disconnected/)).length).toBe(2); + expect(lines.filter(line => line.match(/create context/)).length).toBe(2); + expect(lines.filter(line => line.match(/close context/)).length).toBe(2); + + // Context should only close when the server shuts down. + expect(lines.filter(line => line.match(/close browser context complete \(persistent\)/)).length).toBe(0); + }).toPass(); + + await fetch(new URL('/killkillkill', url).href).catch(() => {}); + + await expect(async () => { + const lines = stderr().split('\n'); + // Context should only close when the server shuts down. + expect(lines.filter(line => line.match(/close browser context complete \(persistent\)/)).length).toBe(1); + }).toPass(); +}); diff --git a/tests/mcp/tabs.spec.ts b/tests/mcp/tabs.spec.ts new file mode 100644 index 0000000000000..a0b40d824b783 --- /dev/null +++ b/tests/mcp/tabs.spec.ts @@ -0,0 +1,155 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +import type { Client } from '@modelcontextprotocol/sdk/client/index.js'; + +async function createTab(client: Client, title: string, body: string) { + await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'new', + }, + }); + return await client.callTool({ + name: 'browser_navigate', + arguments: { + url: `data:text/html,${title}${body}`, + }, + }); +} + +test('list initial tabs', async ({ client }) => { + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'list', + }, + })).toHaveResponse({ + tabs: `- 0: (current) [] (about:blank)`, + }); +}); + +test('list first tab', async ({ client }) => { + await createTab(client, 'Tab one', 'Body one'); + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'list', + }, + })).toHaveResponse({ + tabs: `- 0: [] (about:blank) +- 1: (current) [Tab one] (data:text/html,Tab oneBody one)`, + }); +}); + +test('create new tab', async ({ client }) => { + expect(await createTab(client, 'Tab one', 'Body one')).toHaveResponse({ + tabs: `- 0: [] (about:blank) +- 1: (current) [Tab one] (data:text/html,Tab oneBody one)`, + pageState: expect.stringContaining(`- Page URL: data:text/html,Tab oneBody one +- Page Title: Tab one +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Body one +\`\`\``), + }); + + expect(await createTab(client, 'Tab two', 'Body two')).toHaveResponse({ + tabs: `- 0: [] (about:blank) +- 1: [Tab one] (data:text/html,Tab oneBody one) +- 2: (current) [Tab two] (data:text/html,Tab twoBody two)`, + pageState: expect.stringContaining(`- Page URL: data:text/html,Tab twoBody two +- Page Title: Tab two +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Body two +\`\`\``), + }); +}); + +test('select tab', async ({ client }) => { + await createTab(client, 'Tab one', 'Body one'); + await createTab(client, 'Tab two', 'Body two'); + + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'select', + index: 1, + }, + })).toHaveResponse({ + tabs: `- 0: [] (about:blank) +- 1: (current) [Tab one] (data:text/html,Tab oneBody one) +- 2: [Tab two] (data:text/html,Tab twoBody two)`, + pageState: expect.stringContaining(`- Page URL: data:text/html,Tab oneBody one +- Page Title: Tab one +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Body one +\`\`\``), + }); + + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'select', + index: 0, + }, + })).toHaveResponse({ + tabs: `- 0: (current) [] (about:blank) +- 1: [Tab one] (data:text/html,Tab oneBody one) +- 2: [Tab two] (data:text/html,Tab twoBody two)`, + pageState: expect.stringContaining(`- Page URL: about:blank`), + }); +}); + +test('close tab', async ({ client }) => { + await createTab(client, 'Tab one', 'Body one'); + await createTab(client, 'Tab two', 'Body two'); + + expect(await client.callTool({ + name: 'browser_tabs', + arguments: { + action: 'close', + index: 2, + }, + })).toHaveResponse({ + tabs: `- 0: [] (about:blank) +- 1: (current) [Tab one] (data:text/html,Tab oneBody one)`, + pageState: expect.stringContaining(`- Page URL: data:text/html,Tab oneBody one +- Page Title: Tab one +- Page Snapshot: +\`\`\`yaml +- generic [active] [ref=e1]: Body one +\`\`\``), + }); +}); + +test('reuse first tab when navigating', async ({ startClient, cdpServer, server }) => { + const browserContext = await cdpServer.start(); + const pages = browserContext.pages(); + + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + }); + + expect(pages.length).toBe(1); + expect(await pages[0].title()).toBe('Title'); +}); diff --git a/tests/mcp/test-debug.spec.ts b/tests/mcp/test-debug.spec.ts new file mode 100644 index 0000000000000..0c3b031ef239a --- /dev/null +++ b/tests/mcp/test-debug.spec.ts @@ -0,0 +1,434 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, writeFiles, prepareDebugTest } from './fixtures'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('test_debug (passed)', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => { + expect(1 + 1).toBe(2); + }); + ` + }); + + const { client } = await startClient(); + const listResult = await client.callTool({ + name: 'test_list', + }); + const [, id] = listResult.content[0].text.match(/\[id=([^\]]+)\]/); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'pass' }, + }, + })).toHaveTextResponse(` +Running 1 test using 1 worker + ok 1 [id=] a.test.ts:3:11 › pass (XXms) + 1 passed (XXms)`); +}); + +test('test_debug (pause/resume)', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(` +Running 1 test using 1 worker +### Paused on error: +Error: expect(locator).toBeVisible() failed + +Locator: getByRole('button', { name: 'Missing' }) +Expected: visible +Timeout: 1000ms +Error: element(s) not found + +Call log: + - Expect "toBeVisible" with timeout 1000ms + - waiting for getByRole('button', { name: 'Missing' }) + + +### Page state +- Page URL: about:blank +- Page Title: +- Page Snapshot: +\`\`\`yaml +- button "Submit" [ref=e2] +\`\`\` + +### Task +Try recovering from the error prior to continuing`); + + expect(await client.callTool({ + name: 'test_run', + arguments: { + locations: ['a.test.ts'], + }, + })).toHaveTextResponse(expect.stringContaining(`1) [id=] a.test.ts:3:11 › fail`)); +}); + +test('test_debug (browser_snapshot/network/console)', async ({ startClient, server }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await page.goto(${JSON.stringify(server.HELLO_WORLD)}); + await page.evaluate(() => { + console.log('hello from console'); + setTimeout(() => { throw new Error('error from page'); }, 0); + }); + await expect(page.getByRole('button', { name: 'Missing' })).toBeVisible({ timeout: 1000 }); + }); + `); + await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + await expect.poll(() => client.callTool({ + name: 'browser_network_requests', + })).toHaveResponse({ + result: expect.stringContaining(`[GET] ${server.HELLO_WORLD} => [200] OK`), + }); + expect(await client.callTool({ + name: 'browser_console_messages', + })).toHaveResponse({ + result: expect.stringMatching(/\[LOG\] hello from console.*\nError: error from page/), + }); + expect(await client.callTool({ + name: 'browser_snapshot', + })).toHaveResponse({ + pageState: expect.stringContaining(`generic [active] [ref=e1]: Hello, world!`), + }); +}); + +test('test_debug (multiple pages and custom error)', async ({ startClient, server }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + const page2 = await page.context().newPage(); + await page.goto(${JSON.stringify(server.EMPTY_PAGE)}); + await page2.goto(${JSON.stringify(server.PREFIX + '/wrappedlink.html')}); + throw new Error('non-api error'); + }); + `); + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(` +Running 1 test using 1 worker +### Paused on error: +Error: non-api error + +### Page 1 of 2 +- Page URL: ${server.EMPTY_PAGE} +- Page Title: +- Page Snapshot: +\`\`\`yaml + +\`\`\` + +### Page 2 of 2 +- Page URL: ${server.PREFIX + '/wrappedlink.html'} +- Page Title: +- Page Snapshot: +\`\`\`yaml +- link "123321" [ref=e3] [cursor=pointer]: + - /url: "#clicked" +\`\`\` + +### Task +Try recovering from the error prior to continuing`); +}); + +test('test_debug (pause/snapshot/resume)', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(` +Running 1 test using 1 worker +### Paused on error: +Error: expect(locator).toBeVisible() failed + +Locator: getByRole('button', { name: 'Missing' }) +Expected: visible +Timeout: 1000ms +Error: element(s) not found + +Call log: + - Expect "toBeVisible" with timeout 1000ms + - waiting for getByRole('button', { name: 'Missing' }) + + +### Page state +- Page URL: about:blank +- Page Title: +- Page Snapshot: +\`\`\`yaml +- button "Submit" [ref=e2] +\`\`\` + +### Task +Try recovering from the error prior to continuing`); + + expect(await client.callTool({ + name: 'browser_snapshot', + })).toHaveResponse({ + pageState: expect.stringContaining(`- button \"Submit\" [ref=e2]`), + }); + + expect(await client.callTool({ + name: 'test_run', + arguments: { + locations: ['a.test.ts'], + }, + })).toHaveTextResponse(expect.stringContaining(`1) [id=] a.test.ts:3:11 › fail`)); +}); + +test('test_debug / evaluate', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => 21+21', + }, + })).toHaveResponse({ + result: `42`, + }); +}); + +test('test_debug / evaluate x 2', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => 21+21', + }, + })).toHaveResponse({ + result: `42`, + }); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toEqual({ + content: [ + { type: 'text', text: expect.stringContaining(`Paused on error`) }, + ] + }); + + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: '() => 21+23', + }, + })).toHaveResponse({ + result: `44`, + }); +}); + +test('test_debug / evaluate (with element)', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + expect(await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: 'element => element.textContent', + element: 'button', + ref: 'e2', + }, + })).toHaveResponse({ + result: `"Submit"`, + }); +}); + +test('test_debug / generate_locator', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient); + await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + expect(await client.callTool({ + name: 'browser_generate_locator', + arguments: { + element: 'button', + ref: 'e2', + }, + })).toHaveResponse({ + result: `getByRole('button', { name: 'Submit' })`, + }); +}); + +test('test_debug w/ console.log in test', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + console.log('console.log'); + console.error('console.error'); + await expect(page.getByRole('button', { name: 'Missing' })).toBeVisible({ timeout: 1000 }); + }); + `); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(expect.stringContaining(` +Running 1 test using 1 worker +[out] console.log +[err] console.error +### Paused on error: +Error: expect(locator).toBeVisible() failed`)); +}); + +test('test_debug w/ console_messages', async ({ startClient }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await page.evaluate(() => { + console.error('console.error'); + console.log('console.log'); // Log should be ignored in the initial message. + setTimeout(() => { + const error = new Error('Error from page'); + error.stack = ''; + throw error; + }, 0); + return new Promise(f => setTimeout(f, 10)); + }); + throw new Error('failure'); + }); + `); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(expect.stringContaining(` +Running 1 test using 1 worker +### Paused on error: +Error: failure + +### Page state +- Page URL: about:blank +- Page Title: +- Console Messages: + - [ERROR] console.error @ :1 + - Error from page +- Page Snapshot: +`)); + + expect(await client.callTool({ + name: 'browser_console_messages', + })).toHaveResponse({ + result: expect.stringMatching(/\[ERROR\] console.error.*\n\[LOG] console.log/), + }); +}); + +test('test_debug w/ network_requests', async ({ startClient, server }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + await page.goto(${JSON.stringify(server.HELLO_WORLD)}); + await page.evaluate(async () => { + await fetch('missing'); + }); + await expect(page.getByRole('button', { name: 'Missing' })).toBeVisible({ timeout: 1000 }); + }); + `); + + expect(await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + })).toHaveTextResponse(expect.stringContaining(` +Running 1 test using 1 worker +### Paused on error: +Error: expect(locator).toBeVisible() failed`)); + + expect(await client.callTool({ + name: 'browser_network_requests', + })).toHaveResponse({ + result: `\[GET\] ${server.HELLO_WORLD} => [200] OK\n\[GET\] ${server.PREFIX}/missing => [404] Not Found`, + }); +}); + +test('test_debug w/ route', async ({ startClient, server }) => { + const { client, id } = await prepareDebugTest(startClient, ` + import { test, expect } from '@playwright/test'; + test('fail', async ({ page }) => { + let counter = 0; + await page.route('**', route => route.fulfill({ body: 'Title' + (++counter) + '
mocked
', contentType: 'text/html' })); + await page.goto(${JSON.stringify(server.EMPTY_PAGE)}); + await expect(page.getByRole('button', { name: 'Missing' })).toBeVisible({ timeout: 1000 }); + }); + `); + + const response = await client.callTool({ + name: 'test_debug', + arguments: { + test: { id, title: 'fail' }, + }, + }); + expect(response).toHaveTextResponse(expect.stringContaining(` +Running 1 test using 1 worker +### Paused on error: +Error: expect(locator).toBeVisible() failed`)); + expect(response).toHaveTextResponse(expect.stringContaining(`- Page URL: ${server.EMPTY_PAGE}\n- Page Title: Title1`)); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + pageState: expect.stringContaining(`- Page URL: ${server.HELLO_WORLD}\n- Page Title: Title2`), + }); +}); diff --git a/tests/mcp/test-list.spec.ts b/tests/mcp/test-list.spec.ts new file mode 100644 index 0000000000000..af94d470b8117 --- /dev/null +++ b/tests/mcp/test-list.spec.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, writeFiles } from './fixtures'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('test_list', async ({ startClient }) => { + await writeFiles({ + 'playwright.config.ts': ` + module.exports = { projects: [{ name: 'foo' }, {}] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('example1', async ({}) => { + expect(1 + 1).toBe(2); + }); + test('example2', async ({}) => { + expect(1 + 1).toBe(2); + }); + ` + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'test_list', + arguments: {}, + })).toHaveTextResponse(`Listing tests: + [id=] [project=foo] › a.test.ts:3:11 › example1 + [id=] [project=foo] › a.test.ts:6:11 › example2 + [id=] a.test.ts:3:11 › example1 + [id=] a.test.ts:6:11 › example2 +Total: 4 tests in 1 file`); +}); diff --git a/tests/mcp/test-run.spec.ts b/tests/mcp/test-run.spec.ts new file mode 100644 index 0000000000000..291548066f06c --- /dev/null +++ b/tests/mcp/test-run.spec.ts @@ -0,0 +1,166 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect, writeFiles } from './fixtures'; + +test.use({ mcpServerType: 'test-mcp' }); + +test('test_run', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', () => {}); + test('fails', () => { expect(1).toBe(2); }); + test.describe('suite', () => { + test('inner passes', () => {}); + test('inner fails', () => { expect(1).toBe(2); }); + }); + `, + 'b.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', () => {}); + test('fails', () => { expect(1).toBe(2); }); + `, + 'c.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', () => {}); + test.skip('skipped', () => {}); + `, + }); + + const { client } = await startClient(); + const response = await client.callTool({ + name: 'test_run', + }); + + const text = response.content[0].text; + + expect(text).toContain(`3 failed`); + expect(text).toContain(`1 skipped`); + expect(text).toContain(`4 passed`); + + expect(text).toContain(`a.test.ts:3:11 › passes`); + expect(text).toContain(`c.test.ts:3:11 › passes`); + expect(text).toContain(`c.test.ts:4:12 › skipped`); + expect(text).toContain(`b.test.ts:3:11 › passes`); + expect(text).toContain(`a.test.ts:4:11 › fails`); + expect(text).toContain(`b.test.ts:4:11 › fails`); + expect(text).toContain(`a.test.ts:6:13 › suite › inner passes`); + expect(text).toContain(`a.test.ts:7:13 › suite › inner fails`); + + expect(text).not.toContain(`../../test-results`); +}); + +test('test_run for a failed tests is not an error', async ({ startClient }) => { + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('fails', () => { expect(1).toBe(2); }); + `, + }); + + const { client } = await startClient(); + const response = await client.callTool({ + name: 'test_run', + }); + + const text = response.content[0].text; + // The tool run has succeeded, even though the test has failed. + expect(response.isError).toBeFalsy(); + + expect(text).toContain(`1 failed`); +}); + +test('test_run filters', async ({ startClient }) => { + await writeFiles({ + 'playwright.config.ts': ` + module.exports = { projects: [{ name: 'foo' }, { name: 'bar' }] }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('example1', async ({}) => { + expect(1 + 1).toBe(2); + }); + test('example2', async ({}) => { + expect(1 + 1).toBe(2); + }); + `, + 'b.test.ts': ` + import { test, expect } from '@playwright/test'; + test('example1', async ({}) => { + expect(1 + 1).toBe(2); + }); + test('example2', async ({}) => { + expect(1 + 1).toBe(2); + }); + ` + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'test_run', + arguments: { + locations: ['b.test.ts'], + projects: ['foo'], + }, + })).toHaveTextResponse(` +Running 2 tests using 1 worker + ok 1 [id=] [project=foo] › b.test.ts:3:11 › example1 (XXms) + ok 2 [id=] [project=foo] › b.test.ts:6:11 › example2 (XXms) + 2 passed (XXms)`); +}); + +test('test_run should include dependencies', async ({ startClient }) => { + await writeFiles({ + 'playwright.config.ts': ` + module.exports = { + projects: [ + { name: 'setup', testMatch: /.*setup\\.ts/ }, + { name: 'chromium', dependencies: ['setup'] }, + ], + }; + `, + 'auth.setup.ts': ` + import { test as setup, expect } from '@playwright/test'; + setup('auth', async ({}) => { + expect(1 + 1).toBe(2); + }); + `, + 'example.test.ts': ` + import { test, expect } from '@playwright/test'; + test('example1', async ({}) => { + expect(1 + 1).toBe(2); + }); + test('example2', async ({}) => { + expect(1 + 1).toBe(2); + }); + ` + }); + + const { client } = await startClient(); + expect(await client.callTool({ + name: 'test_run', + arguments: { + locations: ['example.test.ts'], + projects: ['chromium'], + }, + })).toHaveTextResponse(` +Running 3 tests using 1 worker + ok 1 [id=] [project=setup] › auth.setup.ts:3:12 › auth (XXms) + ok 2 [id=] [project=chromium] › example.test.ts:3:11 › example1 (XXms) + ok 3 [id=] [project=chromium] › example.test.ts:6:11 › example2 (XXms) + 3 passed (XXms)`); +}); diff --git a/tests/mcp/timeouts.spec.ts b/tests/mcp/timeouts.spec.ts new file mode 100644 index 0000000000000..57c459812194b --- /dev/null +++ b/tests/mcp/timeouts.spec.ts @@ -0,0 +1,97 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('action timeout (default)', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + expect(await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'Hi!', + submit: true, + }, + })).toHaveResponse({ + result: expect.stringContaining(`Timeout 5000ms exceeded.`), + }); +}); + +test('action timeout (custom)', async ({ startClient, server }) => { + const { client } = await startClient({ args: [`--timeout-action=1234`] }); + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + expect(await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'Hi!', + submit: true, + }, + })).toHaveResponse({ + result: expect.stringContaining(`Timeout 1234ms exceeded.`), + }); +}); + +test('navigation timeout', async ({ startClient, server }) => { + const { client } = await startClient({ args: [`--timeout-navigation=1234`] }); + server.setRoute('/slow', async () => { + await new Promise(f => setTimeout(f, 1500)); + return new Response('OK'); + }); + server.setContent('/', ` + + + + + `, 'text/html'); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX + '/slow', + }, + })).toHaveResponse({ + result: expect.stringContaining(`Timeout 1234ms exceeded.`), + }); +}); diff --git a/tests/mcp/tracing.spec.ts b/tests/mcp/tracing.spec.ts new file mode 100644 index 0000000000000..c790aa926c9bd --- /dev/null +++ b/tests/mcp/tracing.spec.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import path from 'path'; +import { test, expect } from './fixtures'; + +test('check that trace is saved with --save-trace', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + + const { client } = await startClient({ + args: ['--save-trace', `--output-dir=${outputDir}`], + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + const [file] = await fs.promises.readdir(outputDir); + expect(file).toContain('traces'); +}); + +test('check that trace is saved with browser_start_tracing', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + + const { client } = await startClient({ args: [`--output-dir=${outputDir}`, '--caps=tracing'] }); + + expect(await client.callTool({ + name: 'browser_start_tracing', + })).toHaveResponse({ + result: expect.stringContaining(`Tracing started, saving to ${outputDir}`), + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_stop_tracing', + })).toHaveResponse({ + result: expect.stringMatching(/trace-\d+.trace/) + }); + + const files = await fs.promises.readdir(path.join(outputDir, 'traces')); + expect(files).toEqual([ + 'resources', + expect.stringMatching(/trace-\d+\.network/), + expect.stringMatching(/trace-\d+\.trace/), + ]); +}); + +test('check that trace is saved with browser_start_tracing (no output dir)', async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath(); + + const { client } = await startClient({ + args: ['--caps=tracing'], + env: { ...process.env, PW_TMPDIR_FOR_TEST: outputDir }, + }); + + expect(await client.callTool({ + name: 'browser_start_tracing', + })).toHaveResponse({ + result: expect.stringContaining(`Tracing started, saving to ${outputDir}`), + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + expect(await client.callTool({ + name: 'browser_stop_tracing', + })).toHaveResponse({ + result: expect.stringMatching(/trace-\d+.trace/) + }); + + const folders = await fs.promises.readdir(path.join(outputDir, 'playwright-mcp-output')); + expect(folders.length).toBe(1); + expect(folders[0]).toMatch(/\d+/); + const files = await fs.promises.readdir(path.join(outputDir, 'playwright-mcp-output', folders[0], 'traces')); + expect(files).toEqual([ + 'resources', + expect.stringMatching(/trace-\d+\.network/), + expect.stringMatching(/trace-\d+\.trace/), + ]); +}); diff --git a/tests/mcp/type.spec.ts b/tests/mcp/type.spec.ts new file mode 100644 index 0000000000000..7ac23141a3bb2 --- /dev/null +++ b/tests/mcp/type.spec.ts @@ -0,0 +1,138 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_type', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + + { + const response = await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'Hi!', + submit: true, + }, + }); + expect(response).toHaveResponse({ + code: `await page.getByRole('textbox').fill('Hi!'); +await page.getByRole('textbox').press('Enter');`, + pageState: expect.stringContaining(`- textbox`), + }); + } + + expect(await client.callTool({ + name: 'browser_console_messages', + })).toHaveResponse({ + result: expect.stringContaining(`[LOG] Key pressed: Enter , Text: Hi!`), + }); +}); + +test('browser_type (slowly)', async ({ client, server }) => { + server.setContent('/', ` + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + { + const response = await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'Hi!', + slowly: true, + }, + }); + + expect(response).toHaveResponse({ + code: `await page.getByRole('textbox').pressSequentially('Hi!');`, + pageState: expect.stringContaining(`- textbox`), + }); + } + const response = await client.callTool({ + name: 'browser_console_messages', + }); + expect(response).toHaveResponse({ + result: expect.stringContaining(`[LOG] Key pressed: H Text: `), + }); + expect(response).toHaveResponse({ + result: expect.stringContaining(`[LOG] Key pressed: i Text: H`), + }); + expect(response).toHaveResponse({ + result: expect.stringContaining(`[LOG] Key pressed: ! Text: Hi`), + }); +}); + +test('browser_type (no submit)', async ({ client, server }) => { + server.setContent('/', ` + + `, 'text/html'); + + { + const response = await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + }); + expect(response).toHaveResponse({ + pageState: expect.stringContaining(`- textbox`), + }); + } + { + const response = await client.callTool({ + name: 'browser_type', + arguments: { + element: 'textbox', + ref: 'e2', + text: 'Hi!', + }, + }); + expect(response).toHaveResponse({ + code: expect.stringContaining(`fill('Hi!')`), + // Should yield no snapshot. + pageState: expect.not.stringContaining(`- textbox`), + }); + } + { + const response = await client.callTool({ + name: 'browser_console_messages', + }); + expect(response).toHaveResponse({ + result: expect.stringContaining(`[LOG] New value: Hi!`), + }); + } +}); diff --git a/tests/mcp/verify.spec.ts b/tests/mcp/verify.spec.ts new file mode 100644 index 0000000000000..392b8b5b819ea --- /dev/null +++ b/tests/mcp/verify.spec.ts @@ -0,0 +1,522 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test.use({ mcpArgs: ['--caps=testing'] }); + +test('browser_verify_element_visible', async ({ client, server }) => { + server.setContent('/', ` + Test Page + +

Welcome

+
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_element_visible', + arguments: { + role: 'button', + accessibleName: 'Submit', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible();`, + }); + + expect(await client.callTool({ + name: 'browser_verify_element_visible', + arguments: { + role: 'heading', + accessibleName: 'Welcome', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();`, + }); + + expect(await client.callTool({ + name: 'browser_verify_element_visible', + arguments: { + role: 'alert', + accessibleName: 'Success message', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByRole('alert', { name: 'Success message' })).toBeVisible();`, + }); +}); + +test('browser_verify_element_visible (not found)', async ({ client, server }) => { + server.setContent('/', ` + Test Page + + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_element_visible', + arguments: { + role: 'button', + accessibleName: 'Cancel', + }, + })).toHaveResponse({ + isError: true, + result: 'Element with role "button" and accessible name "Cancel" not found', + }); +}); + +test('browser_verify_text_visible', async ({ client, server }) => { + server.setContent('/', ` + Test Page +

Hello world

+
Welcome to our site
+ Status: Active + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: 'Hello world', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByText('Hello world')).toBeVisible();`, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: 'Welcome to our site', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByText('Welcome to our site')).toBeVisible();`, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: 'Status: Active', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByText('Status: Active')).toBeVisible();`, + }); +}); + +test('browser_verify_text_visible (not found)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +

Hello world

+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: 'Goodbye world', + }, + })).toHaveResponse({ + isError: true, + result: 'Text not found', + }); +}); + +test('browser_verify_text_visible (with quotes)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +

She said "Hello world"

+
It's a beautiful day
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: 'She said "Hello world"', + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByText('She said "Hello world"')).toBeVisible();`, + }); + + expect(await client.callTool({ + name: 'browser_verify_text_visible', + arguments: { + text: "It's a beautiful day", + }, + })).toHaveResponse({ + result: 'Done', + code: `await expect(page.getByText('It\\'s a beautiful day')).toBeVisible();`, + }); +}); + +test('browser_verify_list_visible', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
    +
  • Apple
  • +
  • Banana
  • +
  • Cherry
  • +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_list_visible', + arguments: { + element: 'Fruit list', + ref: 'e2', + items: ['Apple', 'Banana', 'Cherry'], + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.locator('body')).toMatchAriaSnapshot(\` +- list: + - listitem: "Apple" + - listitem: "Banana" + - listitem: "Cherry" +\`);`), + }); +}); + +test('browser_verify_list_visible (partial items)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
    +
  • Apple
  • +
  • Banana
  • +
  • Cherry
  • +
  • Date
  • +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_list_visible', + arguments: { + element: 'Fruit list', + ref: 'e2', + items: ['Apple', 'Cherry'], + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.locator('body')).toMatchAriaSnapshot(\` +- list: + - listitem: "Apple" + - listitem: "Cherry" +\`);`), + }); +}); + +test('browser_verify_list_visible (item not found)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
    +
  • Apple
  • +
  • Banana
  • +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_list_visible', + arguments: { + element: 'Fruit list', + ref: 'e2', + items: ['Apple', 'Cherry'], + }, + })).toHaveResponse({ + isError: true, + result: 'Item "Cherry" not found', + }); +}); + +test('browser_verify_value (textbox)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'textbox', + element: 'Name textbox', + ref: 'e3', + value: 'John Doe', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('textbox', { name: 'Name' })).toHaveValue('John Doe');`), + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'textbox', + element: 'Email textbox', + ref: 'e4', + value: 'john@example.com', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('textbox', { name: 'Email' })).toHaveValue('john@example.com');`), + }); +}); + +test('browser_verify_value (textbox wrong value)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'textbox', + element: 'Name textbox', + ref: 'e3', + value: 'Jane Smith', + }, + })).toHaveResponse({ + isError: true, + result: 'Expected value "Jane Smith", but got "John Doe"', + }); +}); + +test('browser_verify_value (checkbox checked)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'checkbox', + element: 'Subscribe checkbox', + ref: 'e3', + value: 'true', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('checkbox')).toBeChecked();`), + }); +}); + +test('browser_verify_value (checkbox unchecked)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'checkbox', + element: 'Subscribe checkbox', + ref: 'e3', + value: 'false', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('checkbox')).not.toBeChecked();`), + }); +}); + +test('browser_verify_value (checkbox wrong value)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'checkbox', + element: 'Subscribe checkbox', + ref: 'e3', + value: 'false', + }, + })).toHaveResponse({ + isError: true, + result: 'Expected value "false", but got "true"', + }); +}); + +test('browser_verify_value (radio checked)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + + + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'radio', + element: 'Color radio', + ref: 'e3', + value: 'true', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('radio', { name: 'Red' })).toBeChecked();`), + }); +}); + +test('browser_verify_value (slider)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ + +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'slider', + element: 'Volume slider', + ref: 'e3', + value: '75', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('slider')).toHaveValue('75');`), + }); +}); + +test('browser_verify_value (combobox)', async ({ client, server }) => { + server.setContent('/', ` + Test Page +
+ +
+ `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_verify_value', + arguments: { + type: 'combobox', + element: 'Country select', + ref: 'e3', + value: 'United States', + }, + })).toHaveResponse({ + result: 'Done', + code: expect.stringContaining(`await expect(page.getByRole('combobox')).toHaveValue('United States');`), + }); +}); diff --git a/tests/mcp/video.spec.ts b/tests/mcp/video.spec.ts new file mode 100644 index 0000000000000..0da9d2653e41b --- /dev/null +++ b/tests/mcp/video.spec.ts @@ -0,0 +1,112 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; +import { test, expect } from './fixtures'; + +for (const mode of ['isolated', 'persistent']) { + test(`should work with --save-video (${mode})`, async ({ startClient, server }, testInfo) => { + const outputDir = testInfo.outputPath('output'); + + const { client } = await startClient({ + args: [ + '--save-video=800x600', + ...(mode === 'isolated' ? ['--isolated'] : []), + '--output-dir', outputDir, + ], + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: `async () => { + document.body.style.backgroundColor = "red"; + await new Promise(resolve => setTimeout(resolve, 100)); + document.body.style.backgroundColor = "green"; + await new Promise(resolve => setTimeout(resolve, 100)); + document.body.style.backgroundColor = "blue"; + await new Promise(resolve => setTimeout(resolve, 100)); + }`, + }, + }); + + expect(await client.callTool({ + name: 'browser_close', + })).toHaveResponse({ + code: expect.stringContaining(`page.close()`), + }); + + const [file] = await fs.promises.readdir(outputDir); + expect(file).toMatch(/page-.*\.webm/); + }); + + test(`should work with recordVideo (${mode})`, async ({ startClient, server }, testInfo) => { + const videosDir = testInfo.outputPath('videos'); + + const { client } = await startClient({ + config: { + browser: { + contextOptions: { + recordVideo: { + dir: videosDir, + size: { width: 800, height: 600 }, + }, + } + } + }, + args: [ + ...(mode === 'isolated' ? ['--isolated'] : []), + ], + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.HELLO_WORLD }, + })).toHaveResponse({ + code: expect.stringContaining(`page.goto('http://localhost`), + }); + + await client.callTool({ + name: 'browser_evaluate', + arguments: { + function: `async () => { + document.body.style.backgroundColor = "red"; + await new Promise(resolve => setTimeout(resolve, 100)); + document.body.style.backgroundColor = "green"; + await new Promise(resolve => setTimeout(resolve, 100)); + document.body.style.backgroundColor = "blue"; + await new Promise(resolve => setTimeout(resolve, 100)); + }`, + }, + }); + + expect(await client.callTool({ + name: 'browser_close', + })).toHaveResponse({ + code: expect.stringContaining(`page.close()`), + }); + + const [file] = await fs.promises.readdir(videosDir); + expect(file).toMatch(/.*.\webm/); + }); +} diff --git a/tests/mcp/wait.spec.ts b/tests/mcp/wait.spec.ts new file mode 100644 index 0000000000000..0388faf259b57 --- /dev/null +++ b/tests/mcp/wait.spec.ts @@ -0,0 +1,107 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('browser_wait_for(text)', async ({ client, server }) => { + server.setContent('/', ` + + + +
Text to disappear
+ + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Click me', + ref: 'e2', + }, + }); + + expect(await client.callTool({ + name: 'browser_wait_for', + arguments: { text: 'Text to appear' }, + code: `await page.getByText("Text to appear").first().waitFor({ state: 'visible' });`, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [ref=e3]: Text to appear`), + }); +}); + +test('browser_wait_for(textGone)', async ({ client, server }) => { + server.setContent('/', ` + + + +
Text to disappear
+ + `, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + await client.callTool({ + name: 'browser_click', + arguments: { + element: 'Click me', + ref: 'e2', + }, + }); + + expect(await client.callTool({ + name: 'browser_wait_for', + arguments: { textGone: 'Text to disappear' }, + code: `await page.getByText("Text to disappear").first().waitFor({ state: 'hidden' });`, + })).toHaveResponse({ + pageState: expect.stringContaining(`- generic [ref=e3]: Text to appear`), + }); +}); + +test('browser_wait_for(time)', async ({ client, server }) => { + server.setContent('/', `
Hello World
`, 'text/html'); + + await client.callTool({ + name: 'browser_navigate', + arguments: { url: server.PREFIX }, + }); + + expect(await client.callTool({ + name: 'browser_wait_for', + arguments: { time: 1 }, + })).toHaveResponse({ + code: `await new Promise(f => setTimeout(f, 1 * 1000));`, + }); +}); diff --git a/tests/mcp/webdriver.spec.ts b/tests/mcp/webdriver.spec.ts new file mode 100644 index 0000000000000..9467056f9efff --- /dev/null +++ b/tests/mcp/webdriver.spec.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from './fixtures'; + +test('do not falsely advertise user agent as a test driver', async ({ client, server, mcpBrowser }) => { + test.skip(mcpBrowser === 'firefox'); + test.skip(mcpBrowser === 'webkit'); + server.setRoute('/', (req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(` + + + `); + }); + + expect(await client.callTool({ + name: 'browser_navigate', + arguments: { + url: server.PREFIX, + }, + })).toHaveResponse({ + pageState: expect.stringContaining(`webdriver: false`), + }); +}); diff --git a/tests/page/expect-boolean.spec.ts b/tests/page/expect-boolean.spec.ts index 8f8a58c9d9a81..4f256cb65ab3c 100644 --- a/tests/page/expect-boolean.spec.ts +++ b/tests/page/expect-boolean.spec.ts @@ -123,12 +123,15 @@ Timeout: 1000ms`); const error = await expect(locator2).not.toBeChecked({ timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).not.toBeChecked() failed -Locator: locator('input2') +Locator: locator('input2') Expected: not checked -Received: -Timeout: 1000ms`); - expect(stripAnsi(error.message)).toContain(`- Expect "not toBeChecked" with timeout 1000ms`); - expect(stripAnsi(error.message)).toContain(`- waiting for locator(\'input2\')`); +Timeout: 1000ms +Error: element(s) not found + +Call log: + - Expect "not toBeChecked" with timeout 1000ms + - waiting for locator('input2') +`); }); test('with role', async ({ page }) => { @@ -466,11 +469,14 @@ test.describe('toBeHidden', () => { const error = await expect(locator).not.toBeHidden({ timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).not.toBeHidden() failed -Locator: locator('button') +Locator: locator('button') Expected: not hidden -Received: -Timeout: 1000ms`); - expect(stripAnsi(error.message)).toContain(`- Expect "not toBeHidden" with timeout 1000ms`); +Timeout: 1000ms +Error: element(s) not found + +Call log: + - Expect "not toBeHidden" with timeout 1000ms +`); }); test('with impossible timeout .not', async ({ page }) => { @@ -568,9 +574,10 @@ test.describe(() => { test('text content type', async ({ page, server }) => { const res = await page.request.get(`${server.PREFIX}/text-content-type`); const error = await expect(res).toBeOK().catch(e => e); + expect(stripAnsi(error.message)).toContain(`expect(response).toBeOK() failed`); expect(error.message).toContain(`→ GET ${server.PREFIX}/text-content-type`); expect(error.message).toContain(`← 404 Not Found`); - expect(error.message).toContain(`Text error`); + expect(stripAnsi(error.message)).toContain(`Response text:\nText error`); }); test('no content type', async ({ page, server }) => { diff --git a/tests/page/expect-matcher-result.spec.ts b/tests/page/expect-matcher-result.spec.ts index 277a0efbcbf8a..d5afc751d1300 100644 --- a/tests/page/expect-matcher-result.spec.ts +++ b/tests/page/expect-matcher-result.spec.ts @@ -77,7 +77,6 @@ test('toBeTruthy-based assertions should have matcher result', async ({ page }) const e = await expect(page.locator('#node2')).toBeVisible({ timeout: 1 }).catch(e => e); e.matcherResult.message = stripAnsi(e.matcherResult.message); expect.soft(e.matcherResult).toEqual({ - actual: '', expected: 'visible', message: expect.stringContaining(`expect(locator).toBeVisible() failed`), name: 'toBeVisible', @@ -88,12 +87,13 @@ test('toBeTruthy-based assertions should have matcher result', async ({ page }) expect.soft(stripAnsi(e.toString())).toContain(`Error: expect(locator).toBeVisible() failed -Locator: locator('#node2') +Locator: locator('#node2') Expected: visible -Received: -Timeout: 1ms +Timeout: 1ms +Error: element(s) not found -Call log`); +Call log: +`); } @@ -117,7 +117,8 @@ Expected: not visible Received: visible Timeout: 1ms -Call log`); +Call log: +`); } }); diff --git a/tests/page/expect-misc.spec.ts b/tests/page/expect-misc.spec.ts index 7aa5a2f2a831e..e550d3ca72ee9 100644 --- a/tests/page/expect-misc.spec.ts +++ b/tests/page/expect-misc.spec.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { stripVTControlCharacters } from 'node:util'; import { stripAnsi } from '../config/utils'; import { test, expect } from './pageTest'; @@ -243,10 +242,13 @@ test.describe('toHaveClass', () => { const error = await expect(locator).toHaveClass('foo bar baz', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveClass(expected) failed -Locator: locator('div') -Expected string: "foo bar baz" -Received string: "bar baz" -Timeout: 1000ms`); +Locator: locator('div') +Expected: "foo bar baz" +Received: "bar baz" +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "toHaveClass" with timeout 1000ms`); }); @@ -289,10 +291,13 @@ test.describe('toContainClass', () => { const error = await expect(locator).toContainClass('does-not-exist', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toContainClass(expected) failed -Locator: locator('div') -Expected string: "does-not-exist" -Received string: "bar baz" -Timeout: 1000ms`); +Locator: locator('div') +Expected: "does-not-exist" +Received: "bar baz" +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "toContainClass" with timeout 1000ms`); }); @@ -326,9 +331,12 @@ test.describe('toHaveTitle', () => { const error = await expect(page).toHaveTitle('Hello', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(page).toHaveTitle(expected) failed -Expected string: "Hello" -Received string: "Bye" -Timeout: 1000ms`); +Expected: "Hello" +Received: "Bye" +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "toHaveTitle" with timeout 1000ms`); }); }); @@ -344,32 +352,46 @@ test.describe('toHaveURL', () => { const error = await expect(page).toHaveURL('wrong', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(page).toHaveURL(expected) failed -Expected string: "wrong" -Received string: "data:text/html,
A
" -Timeout: 1000ms`); - expect(stripVTControlCharacters(error.message)).toContain('Expected string: "wrong"\nReceived string: "data:text/html,
A
"'); +Expected: "wrong" +Received: "data:text/html,
A
" +Timeout: 1000ms + +Call log: +`); }); test('fail with invalid argument', async ({ page }) => { await page.goto('data:text/html,
A
'); // @ts-expect-error const error = await expect(page).toHaveURL({}).catch(e => e); - expect(stripVTControlCharacters(error.message)).toContain(`expect(page).toHaveURL([object Object])`); - expect(stripVTControlCharacters(error.message)).toContain('Expected has type: object\nExpected has value: {}'); + expect(stripAnsi(error.message)).toContain(`expect(page).toHaveURL(expected) failed + +Error: expected value must be a string or regular expression +Expected has type: object +Expected has value: {} +`); }); test('fail with positive predicate', async ({ page }) => { await page.goto('data:text/html,
A
'); const error = await expect(page).toHaveURL(_url => false).catch(e => e); - expect(stripVTControlCharacters(error.message)).toContain('expect(page).toHaveURL(expected)'); - expect(stripVTControlCharacters(error.message)).toContain('Expected predicate to succeed\nReceived string: "data:text/html,
A
"'); + expect(stripAnsi(error.message)).toContain(`expect(page).toHaveURL(expected) failed + +Expected: predicate to succeed +Received: "data:text/html,
A
" +Timeout: 10000ms +`); }); test('fail with negative predicate', async ({ page }) => { await page.goto('data:text/html,
A
'); const error = await expect(page).not.toHaveURL(_url => true).catch(e => e); - expect(stripVTControlCharacters(error.message)).toContain('expect(page).not.toHaveURL(expected)'); - expect(stripVTControlCharacters(error.message)).toContain('Expected predicate to fail\nReceived string: "data:text/html,
A
"'); + expect(stripAnsi(error.message)).toContain(`expect(page).not.toHaveURL(expected) failed + +Expected: predicate to fail +Received: "data:text/html,
A
" +Timeout: 10000ms +`); }); test('resolve predicate on initial call', async ({ page }) => { @@ -404,10 +426,13 @@ test.describe('toHaveAttribute', () => { const error = await expect(locator).toHaveAttribute('disabled', '', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveAttribute(expected) failed -Locator: locator('#node') -Expected string: "" -Received string: serializes to the same string -Timeout: 1000ms`); +Locator: locator('#node') +Expected: "" +Received: serializes to the same string +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "toHaveAttribute" with timeout 1000ms`); } { @@ -417,7 +442,10 @@ Timeout: 1000ms`); Locator: locator('#node') Expected pattern: /.*/ Received string: "" -Timeout: 1000ms`); +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "toHaveAttribute" with timeout 1000ms`); } await expect(locator).not.toHaveAttribute('disabled', ''); @@ -433,10 +461,13 @@ Timeout: 1000ms`); const error = await expect(locator).not.toHaveAttribute('checked', '', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).not.toHaveAttribute(expected) failed -Locator: locator('#node') -Expected string: not "" -Received string: "" -Timeout: 1000ms`); +Locator: locator('#node') +Expected: not "" +Received: "" +Timeout: 1000ms + +Call log: +`); expect(stripAnsi(error.message)).toContain(`- Expect "not toHaveAttribute" with timeout 1000ms`); } { @@ -582,3 +613,31 @@ test('toHaveText that does not match should not produce logs twice', async ({ pa expect(error.message).not.toContain('locator resolved to'); expect(error.message.replace(waitingForMessage, '')).not.toContain(waitingForMessage); }); + +test('strict mode violation error format', async ({ page }) => { + await page.setContent('
a
b
'); + const error = await expect(page.locator('div')).toHaveText('foo').catch(e => e); + expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed + +Locator: locator('div') +Expected: "foo" +Error: strict mode violation: locator('div') resolved to 2 elements: + 1)
a
aka getByText('a') + 2)
b
aka getByText('b') + +Call log: +`); +}); + +test('invalid selector error format', async ({ page }) => { + await page.setContent('
a
b
'); + const error = await expect(page.locator('##')).toBeVisible().catch(e => e); + expect(stripAnsi(error.message)).toContain(`expect(locator).toBeVisible() failed + +Locator: ## +Expected: visible +Error: Unexpected token "#" while parsing css selector "##". Did you mean to CSS.escape it? + +Call log: +`); +}); diff --git a/tests/page/expect-timeout.spec.ts b/tests/page/expect-timeout.spec.ts index 7143d309f8af6..211dab2d0c5f7 100644 --- a/tests/page/expect-timeout.spec.ts +++ b/tests/page/expect-timeout.spec.ts @@ -17,15 +17,18 @@ import { stripAnsi } from '../config/utils'; import { test, expect } from './pageTest'; -test('should print timed out error message', async ({ page }) => { +test('should print element not found', async ({ page }) => { await page.setContent('
Text content
'); const error = await expect(page.locator('no-such-thing')).toHaveText('hey', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed Locator: locator('no-such-thing') -Expected string: "hey" -Received: -Timeout: 1000ms`); +Expected: "hey" +Timeout: 1000ms +Error: element(s) not found + +Call log: +`); }); test('should print timed out error message when value does not match', async ({ page }) => { @@ -33,10 +36,13 @@ test('should print timed out error message when value does not match', async ({ const error = await expect(page.locator('div')).toHaveText('hey', { timeout: 1000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed -Locator: locator('div') -Expected string: "hey" -Received string: "Text content" -Timeout: 1000ms`); +Locator: locator('div') +Expected: "hey" +Received: "Text content" +Timeout: 1000ms + +Call log: +`); }); test('should print timed out error message with impossible timeout', async ({ page }) => { @@ -45,9 +51,11 @@ test('should print timed out error message with impossible timeout', async ({ pa expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed Locator: locator('no-such-thing') -Expected string: "hey" -Received: -Timeout: 1ms`); +Expected: "hey" +Timeout: 1ms +Error: element(s) not found + +Call log:`); }); test('should print timed out error message when value does not match with impossible timeout', async ({ page }) => { @@ -55,10 +63,13 @@ test('should print timed out error message when value does not match with imposs const error = await expect(page.locator('div')).toHaveText('hey', { timeout: 1 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed -Locator: locator('div') -Expected string: "hey" -Received string: "Text content" -Timeout: 1ms`); +Locator: locator('div') +Expected: "hey" +Received: "Text content" +Timeout: 1ms + +Call log: +`); }); test('should have timeout error name', async ({ page }) => { @@ -89,10 +100,13 @@ test('should timeout during first locator handler check', async ({ page, server const error = await expect(page.locator('span')).toHaveText('bye', { timeout: 3000 }).catch(e => e); expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed -Locator: locator('span') -Expected string: "bye" -Received string: "" -Timeout: 3000ms`); +Locator: locator('span') +Expected: "bye" +Received: "" +Timeout: 3000ms + +Call log: +`); expect(error.message).toContain(`locator handler has finished, waiting for locator('div') to be hidden`); expect(error.message).toContain(`locator resolved to visible
hello
`); }); diff --git a/tests/page/expect-to-have-text.spec.ts b/tests/page/expect-to-have-text.spec.ts index b1b19cc02a294..6b80d522faaab 100644 --- a/tests/page/expect-to-have-text.spec.ts +++ b/tests/page/expect-to-have-text.spec.ts @@ -35,8 +35,15 @@ test.describe('toHaveText with regex', () => { await page.setContent('
Text content
'); const locator = page.locator('#node'); const error = await expect(locator).toHaveText(/Text 2/, { timeout: 1000 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('Expected pattern: /Text 2/'); - expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed + +Locator: locator('#node') +Expected pattern: /Text 2/ +Received string: "Text content" +Timeout: 1000ms + +Call log: +`); }); }); @@ -59,6 +66,29 @@ test.describe('toContainText with regex', () => { }); }); +test.describe('toContainText with string', () => { + test('pass', async ({ page }) => { + await page.setContent('
Text content
'); + const locator = page.locator('#node'); + await expect(locator).toContainText('content'); + }); + + test('fail', async ({ page }) => { + await page.setContent('
Text content
'); + const locator = page.locator('#node'); + const error = await expect(locator).toContainText('foo', { timeout: 1000 }).catch(e => e); + expect(stripAnsi(error.message)).toContain(`expect(locator).toContainText(expected) failed + +Locator: locator('#node') +Expected substring: "foo" +Received string: "Text content" +Timeout: 1000ms + +Call log: +`); + }); +}); + test.describe('toHaveText with text', () => { test('pass', async ({ page }) => { await page.setContent('
Text \ncontent 
'); @@ -91,8 +121,8 @@ test.describe('toHaveText with text', () => { await page.setContent('
Text content
'); const locator = page.locator('#node'); const error = await expect(locator).toHaveText('Text', { timeout: 1000 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('Expected string: "Text"'); - expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + expect(stripAnsi(error.message)).toContain('Expected: "Text"'); + expect(stripAnsi(error.message)).toContain('Received: "Text content"'); }); test('pass eventually', async ({ page }) => { @@ -134,8 +164,8 @@ test.describe('toHaveText with text', () => { test('fail with impossible timeout', async ({ page }) => { await page.setContent('
Text content
'); const error = await expect(page.locator('#node')).toHaveText('Text', { timeout: 1 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('Expected string: "Text"'); - expect(stripAnsi(error.message)).toContain('Received string: "Text content"'); + expect(stripAnsi(error.message)).toContain('Expected: "Text"'); + expect(stripAnsi(error.message)).toContain('Received: "Text content"'); }); }); @@ -152,15 +182,15 @@ test.describe('not.toHaveText', () => { await page.setContent('
Text content
'); const locator = page.locator('#node'); const error = await expect(locator).not.toHaveText('Text content', { timeout: 1000 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('Expected string: not "Text content"'); - expect(stripAnsi(error.message)).toContain('Received string: "Text content'); + expect(stripAnsi(error.message)).toContain('Expected: not "Text content"'); + expect(stripAnsi(error.message)).toContain('Received: "Text content'); }); test('should work when selector does not match', async ({ page }) => { await page.setContent('
hello
'); const error = await expect(page.locator('span')).not.toHaveText('hello', { timeout: 1000 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('Expected string: not "hello"'); - expect(stripAnsi(error.message)).toContain('Received: '); + expect(stripAnsi(error.message)).toContain('Expected: not "hello"'); + expect(stripAnsi(error.message)).toContain('Error: element(s) not found'); expect(stripAnsi(error.message)).toContain('waiting for locator(\'span\')'); }); }); @@ -226,11 +256,23 @@ test.describe('toHaveText with array', () => { await page.setContent('
Text 1
Text 3
'); const locator = page.locator('div'); const error = await expect(locator).toHaveText(['Text 1', /Text \d/, 'Extra'], { timeout: 1000 }).catch(e => e); - expect(stripAnsi(error.message)).toContain('- "Extra"'); - expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected)`); - expect(stripAnsi(error.message)).toContain(`Timeout: 1000ms`); - expect(stripAnsi(error.message)).toContain(`- Expect "toHaveText" with timeout 1000ms`); - expect(stripAnsi(error.message)).toContain('waiting for locator(\'div\')'); + expect(stripAnsi(error.message)).toContain(`expect(locator).toHaveText(expected) failed + +Locator: locator('div') +Timeout: 1000ms +- Expected - 1 ++ Received + 0 + + Array [ + \"Text 1\", + \"Text 3\", +- \"Extra\", + ] + +Call log: + - Expect \"toHaveText\" with timeout 1000ms + - waiting for locator('div') +`); expect(stripAnsi(error.message)).toContain('locator resolved to 2 elements'); }); diff --git a/tests/page/interception.spec.ts b/tests/page/interception.spec.ts index 08c502e9e1a85..b8653f9ef3838 100644 --- a/tests/page/interception.spec.ts +++ b/tests/page/interception.spec.ts @@ -123,6 +123,10 @@ it('should work with glob', async () => { expect(urlMatches(undefined, 'https://playwright.dev/foobar', 'https://playwright.dev/fooBAR')).toBeFalsy(); expect(urlMatches(undefined, 'https://playwright.dev/foobar?a=b', 'https://playwright.dev/foobar?A=B')).toBeFalsy(); + expect(urlMatches(undefined, 'https://localhost:3000/?a=b', '**/?a=b')).toBeTruthy(); + expect(urlMatches(undefined, 'https://localhost:3000/?a=b', '**?a=b')).toBeTruthy(); + expect(urlMatches(undefined, 'https://localhost:3000/?a=b', '**=b')).toBeTruthy(); + // This is not supported, we treat ? as a query separator. expect(globToRegex('http://localhost:8080/?imple/path.js').test('http://localhost:8080/Simple/path.js')).toBeFalsy(); expect(urlMatches(undefined, 'http://playwright.dev/', 'http://playwright.?ev')).toBeFalsy(); diff --git a/tests/page/matchers.misc.spec.ts b/tests/page/matchers.misc.spec.ts index 5f785f49a3a98..eb3b092a1eb8b 100644 --- a/tests/page/matchers.misc.spec.ts +++ b/tests/page/matchers.misc.spec.ts @@ -41,7 +41,7 @@ it('should print no-locator-resolved error when locator matcher did not resolve const error = await matcher().catch(e => e); expect(error).toBeInstanceOf(Error); expect(error.message).toContain(`waiting for locator('.nonexisting')`); - expect(stripAnsi(error.message)).toMatch(/Received: ?"?/); + expect(stripAnsi(error.message)).toContain('Error: element(s) not found'); }); } }); diff --git a/tests/page/page-aria-snapshot-ai.spec.ts b/tests/page/page-aria-snapshot-ai.spec.ts index c7afd03cf34da..982cc120cb6cf 100644 --- a/tests/page/page-aria-snapshot-ai.spec.ts +++ b/tests/page/page-aria-snapshot-ai.spec.ts @@ -187,15 +187,15 @@ it('emit generic roles for nodes w/o roles', async ({ page }) => { - generic [ref=e3]: - generic [ref=e4]: - radio "Apple" [checked] - - generic [ref=e5]: Apple - - generic [ref=e6]: - - generic [ref=e7]: + - text: Apple + - generic [ref=e5]: + - generic [ref=e6]: - radio "Pear" - - generic [ref=e8]: Pear - - generic [ref=e9]: - - generic [ref=e10]: + - text: Pear + - generic [ref=e7]: + - generic [ref=e8]: - radio "Orange" - - generic [ref=e11]: Orange + - text: Orange `); }); @@ -227,6 +227,23 @@ it('should include cursor pointer hint', async ({ page }) => { `); }); +it('should not nest cursor pointer hints', async ({ page }) => { + await page.setContent(` + + Link with a button + + + `); + + const snapshot = await snapshotForAI(page); + expect(snapshot).toContainYaml(` + - link \"Link with a button Button\" [ref=e2] [cursor=pointer]: + - /url: about:blank + - text: Link with a button + - button "Button" [ref=e3] + `); +}); + it('should gracefully fallback when child frame cant be captured', async ({ page, server }) => { await page.setContent(`

Test

@@ -392,3 +409,42 @@ it('should support many properties on iframes', async ({ page }) => { - textbox "Input in iframe" [active] [ref=f1e2] `); }); + +it('should collapse inline generic nodes', async ({ page }) => { + await page.setContent(` +
    +
  • 3 bds
  • +
  • 2 ba
  • +
  • 1,200 sqft
  • +
+
    +
  • 3
  • +
  • 2
  • +
  • 1,200
  • +
`); + + const snapshot1 = await snapshotForAI(page); + expect(snapshot1).toContainYaml(` + - generic [active] [ref=e1]: + - list [ref=e2]: + - listitem [ref=e3]: 3 bds + - listitem [ref=e4]: 2 ba + - listitem [ref=e5]: 1,200 sqft + - list [ref=e6]: + - listitem [ref=e7]: + - generic [ref=e8]: "3" + - listitem [ref=e9]: + - generic [ref=e10]: "2" + - listitem [ref=e11]: + - generic [ref=e12]: 1,200 + `); +}); + +it('should not remove generic nodes with title', async ({ page }) => { + await page.setContent(`
Element content
`); + + const snapshot = await snapshotForAI(page); + expect(snapshot).toContainYaml(` + - generic "Element title" [ref=e2] + `); +}); diff --git a/tests/page/page-aria-snapshot.spec.ts b/tests/page/page-aria-snapshot.spec.ts index e08f4830da0f6..8157d7787b651 100644 --- a/tests/page/page-aria-snapshot.spec.ts +++ b/tests/page/page-aria-snapshot.spec.ts @@ -669,3 +669,28 @@ it('should not show unhidden children of aria-hidden elements', { annotation: { expect(await page.locator('body').ariaSnapshot()).toBe(''); }); + +it('should snapshot placeholder when different from the name', async ({ page }) => { + await page.setContent(` + + `); + expect(await page.locator('body').ariaSnapshot()).toContainYaml(` + - textbox "Placeholder" + `); + + await page.setContent(` + + `); + expect(await page.locator('body').ariaSnapshot()).toContainYaml(` + - textbox "Label": + - /placeholder: Placeholder + `); +}); + +it('match values both against regex and string', async ({ page }) => { + await page.setContent(`Log in`); + await checkAndMatchSnapshot(page.locator('body'), ` + - link "Log in": + - /url: /auth?r=/ + `); +}); diff --git a/tests/page/page-basic.spec.ts b/tests/page/page-basic.spec.ts index d8e8d40dec7ce..715c03b54ca32 100644 --- a/tests/page/page-basic.spec.ts +++ b/tests/page/page-basic.spec.ts @@ -125,7 +125,7 @@ it('should have sane user agent', async ({ page, browserName, isElectron, isAndr // Second part in parenthesis is platform - ignore it. // Third part for Firefox is the last one and encodes engine and browser versions. - if (browserName === 'firefox') { + if (browserName === 'firefox' || browserName as any === '_bidiFirefox') { const [engine, browser] = part3.split(' '); expect(engine.startsWith('Gecko')).toBe(true); expect(browser.startsWith('Firefox')).toBe(true); diff --git a/tests/page/page-check.spec.ts b/tests/page/page-check.spec.ts index 01b00ddc55c90..73fe05acb40a5 100644 --- a/tests/page/page-check.spec.ts +++ b/tests/page/page-check.spec.ts @@ -145,3 +145,9 @@ it('should check the box using setChecked', async ({ page }) => { await page.setChecked('input', false); expect(await page.evaluate(() => window['checkbox'].checked)).toBe(false); }); + +it('should throw when trying to uncheck radio button', async ({ page }) => { + await page.setContent(``); + const error = await page.uncheck('#radio').catch(e => e); + expect(error.message).toContain('Cannot uncheck radio button'); +}); diff --git a/tests/page/page-event-console.spec.ts b/tests/page/page-event-console.spec.ts index d37b28e734979..081f743937e91 100644 --- a/tests/page/page-event-console.spec.ts +++ b/tests/page/page-event-console.spec.ts @@ -134,7 +134,7 @@ it('should trigger correct Log', async ({ page, server, browserName, isWindows } page.waitForEvent('console'), page.evaluate(async url => fetch(url).catch(e => {}), server.EMPTY_PAGE) ]); - expect(message.text()).toContain('Access-Control-Allow-Origin'); + expect(message.text()).toMatch(/Access-Control-Allow-Origin|CORS/); expect(message.type()).toEqual('error'); }); @@ -199,7 +199,9 @@ it('should use object previews for errors', async ({ page, browserName }) => { await page.evaluate(() => console.log(new Error('Exception'))); if (browserName === 'chromium') expect(text).toContain('.evaluate'); - if (browserName === 'webkit') + if (browserName as any === '_bidiChromium') + expect(text).toEqual('error'); + if (browserName === 'webkit' || browserName as any === '_bidiFirefox') expect(text).toEqual('Error: Exception'); if (browserName === 'firefox') expect(text).toEqual('Error'); @@ -222,3 +224,20 @@ it('do not update console count on unhandled rejections', async ({ page }) => { await expect.poll(() => messages).toEqual(['begin', 'end']); }); + +it('consoleMessages should work', async ({ page }) => { + await page.evaluate(() => { + for (let i = 0; i < 301; i++) + console.log('message' + i); + }); + + const messages = await page.consoleMessages(); + const objects = messages.map(m => ({ text: m.text(), type: m.type(), page: m.page() })); + + const expected = []; + for (let i = 201; i < 301; i++) + expected.push(expect.objectContaining({ text: 'message' + i, type: 'log', page })); + + expect(objects.length, 'should be at least 100 messages').toBeGreaterThanOrEqual(100); + expect(objects.slice(objects.length - expected.length), 'should return last messages').toEqual(expected); +}); diff --git a/tests/page/page-event-network.spec.ts b/tests/page/page-event-network.spec.ts index e7004f576a245..c92ef40b5f97f 100644 --- a/tests/page/page-event-network.spec.ts +++ b/tests/page/page-event-network.spec.ts @@ -42,7 +42,7 @@ it('Page.Events.Response @smoke', async ({ page, server }) => { expect(responses[0].request()).toBeTruthy(); }); -it('Page.Events.RequestFailed @smoke', async ({ page, server, browserName, platform }) => { +it('Page.Events.RequestFailed @smoke', async ({ page, server, browserName, platform, channel }) => { server.setRoute('/one-style.css', (req, res) => { res.setHeader('Content-Type', 'text/css'); res.connection.destroy(); @@ -54,10 +54,10 @@ it('Page.Events.RequestFailed @smoke', async ({ page, server, browserName, platf expect(failedRequests[0].url()).toContain('one-style.css'); expect(await failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); - if (browserName === 'chromium') { + if (browserName === 'chromium' || browserName === '_bidiChromium') { expect(failedRequests[0].failure().errorText).toBe('net::ERR_EMPTY_RESPONSE'); } else if (browserName === 'webkit') { - if (platform === 'linux') + if (platform === 'linux' || channel === 'webkit-wsl') expect(failedRequests[0].failure().errorText).toMatch(/(Message Corrupt)|(Connection terminated unexpectedly)/i); else if (platform === 'darwin') expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.'); diff --git a/tests/page/page-event-pageerror.spec.ts b/tests/page/page-event-pageerror.spec.ts index c7fc699683ac0..310f4917f0bb9 100644 --- a/tests/page/page-event-pageerror.spec.ts +++ b/tests/page/page-event-pageerror.spec.ts @@ -25,7 +25,7 @@ it('should fire', async ({ page, server, browserName }) => { ]); expect(error.name).toBe('Error'); expect(error.message).toBe('Fancy error!'); - if (browserName === 'chromium') { + if (browserName === 'chromium' || browserName === '_bidiChromium' || browserName === '_bidiFirefox') { expect(error.stack).toBe(`Error: Fancy error! at c (myscript.js:14:11) at b (myscript.js:10:5) @@ -150,3 +150,21 @@ it('should emit error from unhandled rejects', async ({ page, browserName }) => ]); expect(error.message).toContain('sad :('); }); + +it('pageErrors should work', async ({ page }) => { + await page.evaluate(async () => { + for (let i = 0; i < 301; i++) + window.builtins.setTimeout(() => { throw new Error('error' + i); }, 0); + await new Promise(f => window.builtins.setTimeout(f, 100)); + }); + + const errors = await page.pageErrors(); + const messages = errors.map(e => e.message); + + const expected = []; + for (let i = 201; i < 301; i++) + expected.push('error' + i); + + expect(messages.length, 'should be at least 100 errors').toBeGreaterThanOrEqual(100); + expect(messages.slice(messages.length - expected.length), 'should return last errors').toEqual(expected); +}); diff --git a/tests/page/page-event-request.spec.ts b/tests/page/page-event-request.spec.ts index 028ddcca54fbe..bf7643f0c2377 100644 --- a/tests/page/page-event-request.spec.ts +++ b/tests/page/page-event-request.spec.ts @@ -376,3 +376,31 @@ it('should not expose preflight OPTIONS request with network interception', { `POST ${server.CROSS_PROCESS_PREFIX}/cors`, ]); }); + +it('should return last requests', async ({ page, server }) => { + await page.goto(server.PREFIX + '/title.html'); + for (let i = 0; i < 200; ++i) + server.setRoute('/fetch?' + i, (req, res) => res.end('url:' + server.PREFIX + req.url)); + + // #0 is the navigation request, so start with #1. + for (let i = 0; i < 99; ++i) + await page.evaluate(url => fetch(url), server.PREFIX + '/fetch?' + i); + const first99Requests = await page.requests(); + first99Requests.shift(); + for (let i = 99; i < 199; ++i) + await page.evaluate(url => fetch(url), server.PREFIX + '/fetch?' + i); + const last100Requests = await page.requests(); + const allRequests = [...first99Requests, ...last100Requests]; + + // All 199 requests are fully functional. + const received = await Promise.all(allRequests.map(async request => { + const response = await request.response(); + return { text: await response.text(), url: request.url() }; + })); + const expected = []; + for (let i = 0; i < 199; ++i) { + const url = server.PREFIX + '/fetch?' + i; + expected.push({ url, text: 'url:' + url }); + } + expect(received).toEqual(expected); +}); diff --git a/tests/page/page-goto.spec.ts b/tests/page/page-goto.spec.ts index e3ffb1f93cf54..954228de06f55 100644 --- a/tests/page/page-goto.spec.ts +++ b/tests/page/page-goto.spec.ts @@ -24,9 +24,10 @@ it('should work @smoke', async ({ page, server }) => { expect(page.url()).toBe(server.EMPTY_PAGE); }); -it('should work with file URL', async ({ page, asset, isAndroid, mode }) => { +it('should work with file URL', async ({ page, asset, isAndroid, mode, channel }) => { it.skip(isAndroid, 'No files on Android'); it.skip(mode.startsWith('service')); + it.skip(channel === 'webkit-wsl', 'separate filesystem on wsl'); const fileurl = url.pathToFileURL(asset('empty.html')).href; await page.goto(fileurl); @@ -34,9 +35,10 @@ it('should work with file URL', async ({ page, asset, isAndroid, mode }) => { expect(page.frames().length).toBe(1); }); -it('should work with file URL with subframes', async ({ page, asset, isAndroid, mode }) => { +it('should work with file URL with subframes', async ({ page, asset, isAndroid, mode, channel }) => { it.skip(isAndroid, 'No files on Android'); it.skip(mode.startsWith('service')); + it.skip(channel === 'webkit-wsl', 'separate filesystem on wsl'); const fileurl = url.pathToFileURL(asset('frames/two-frames.html')).href; await page.goto(fileurl); @@ -295,11 +297,13 @@ it('should fail when navigating to bad url', async ({ mode, page, browserName }) await page.goto('asdfasdf').catch(e => error = e); if (browserName === 'chromium' || browserName === 'webkit') expect(error.message).toContain('Cannot navigate to invalid URL'); + else if (browserName === '_bidiFirefox') + expect(error.message).toContain('NS_ERROR_MALFORMED_URI'); else expect(error.message).toContain('Invalid url'); }); -it('should fail when navigating to bad SSL', async ({ page, browserName, httpsServer, platform }) => { +it('should fail when navigating to bad SSL', async ({ page, browserName, httpsServer, platform, channel }) => { // Make sure that network events do not emit 'undefined'. // @see https://crbug.com/750469 page.on('request', request => expect(request).toBeTruthy()); @@ -307,15 +311,15 @@ it('should fail when navigating to bad SSL', async ({ page, browserName, httpsSe page.on('requestfailed', request => expect(request).toBeTruthy()); let error = null; await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); - expect(error.message).toMatch(expectedSSLError(browserName, platform)); + expect(error.message).toMatch(expectedSSLError(browserName, platform, channel)); }); -it('should fail when navigating to bad SSL after redirects', async ({ page, browserName, server, httpsServer, platform }) => { +it('should fail when navigating to bad SSL after redirects', async ({ page, browserName, server, httpsServer, platform, channel }) => { server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html'); let error = null; await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e); - expect(error.message).toMatch(expectedSSLError(browserName, platform)); + expect(error.message).toMatch(expectedSSLError(browserName, platform, channel)); }); it('should not crash when navigating to bad SSL after a cross origin navigation', async ({ page, server, httpsServer }) => { @@ -335,7 +339,8 @@ it('should throw if networkidle2 is passed as an option', async ({ page, server expect(error.message).toContain(`waitUntil: expected one of (load|domcontentloaded|networkidle|commit)`); }); -it('should fail when main resources failed to load', async ({ page, browserName, isWindows, mode }) => { +it('should fail when main resources failed to load', async ({ page, browserName, isWindows, mode, channel }) => { + it.skip(channel === 'webkit-wsl', 'Networking mode mirrored ends up stalling connections rather than terminating them, see https://github.com/microsoft/WSL/issues/10855.'); let error = null; await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e); if (browserName === 'chromium') { @@ -345,7 +350,7 @@ it('should fail when main resources failed to load', async ({ page, browserName, expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); } else if (browserName === 'webkit' && isWindows && mode === 'service2') { expect(error.message).toContain(`proxy handshake error`); - } else if (browserName === 'webkit' && isWindows) { + } else if (browserName === 'webkit' && isWindows && channel !== 'webkit-wsl') { expect(error.message).toContain(`Could not connect to server`); } else if (browserName === 'webkit') { if (mode === 'service2') @@ -728,9 +733,9 @@ it('should work with lazy loading iframes', async ({ page, server, isAndroid }) expect(page.frames().length).toBe(2); }); -it('should report raw buffer for main resource', async ({ page, server, browserName, platform }) => { +it('should report raw buffer for main resource', async ({ page, server, browserName, platform, channel }) => { it.fail(browserName === 'chromium', 'Chromium sends main resource as text'); - it.fail(browserName === 'webkit' && platform === 'win32', 'Same here'); + it.fail(browserName === 'webkit' && platform === 'win32' && channel !== 'webkit-wsl', 'Same here'); server.setRoute('/empty.html', (req, res) => { res.statusCode = 200; diff --git a/tests/page/page-history.spec.ts b/tests/page/page-history.spec.ts index a709ade4cac69..b3e1fe5674b2a 100644 --- a/tests/page/page-history.spec.ts +++ b/tests/page/page-history.spec.ts @@ -52,9 +52,10 @@ it('page.goBack should work with HistoryAPI', async ({ page, server }) => { expect(page.url()).toBe(server.PREFIX + '/first.html'); }); -it('page.goBack should work for file urls', async ({ page, server, asset, browserName, platform, isAndroid, mode }) => { +it('page.goBack should work for file urls', async ({ page, server, asset, channel, isAndroid, mode }) => { it.skip(isAndroid, 'No files on Android'); it.skip(mode.startsWith('service')); + it.skip(channel === 'webkit-wsl'); const url1 = url.pathToFileURL(asset('consolelog.html')).href; const url2 = server.PREFIX + '/consolelog.html'; diff --git a/tests/page/page-keyboard.spec.ts b/tests/page/page-keyboard.spec.ts index 7b0b88b0d9a75..783448b06d323 100644 --- a/tests/page/page-keyboard.spec.ts +++ b/tests/page/page-keyboard.spec.ts @@ -496,8 +496,8 @@ it('should support simple cut-pasting', async ({ page }) => { expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123'); }); -it('should support undo-redo', async ({ page, browserName, isLinux }) => { - it.fixme(browserName === 'webkit' && isLinux, 'https://github.com/microsoft/playwright/issues/12000'); +it('should support undo-redo', async ({ page, browserName, isLinux, channel }) => { + it.fixme(browserName === 'webkit' && isLinux || channel === 'webkit-wsl', 'https://github.com/microsoft/playwright/issues/12000'); await page.setContent(`
`); const div = page.locator('div'); await expect(div).toHaveText(''); diff --git a/tests/page/page-network-request.spec.ts b/tests/page/page-network-request.spec.ts index f84a569c5ca5f..f6ddb0b49033c 100644 --- a/tests/page/page-network-request.spec.ts +++ b/tests/page/page-network-request.spec.ts @@ -88,9 +88,9 @@ it('should return headers', async ({ page, server, browserName }) => { expect(response.request().headers()['user-agent']).toContain('WebKit'); }); -it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should get the same headers as the server', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); - it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); + it.fail(browserName === 'webkit' && platform === 'win32' && channel !== 'webkit-wsl', 'Curl does not show accept-encoding and accept-language'); let serverRequest; server.setRoute('/empty.html', (request, response) => { serverRequest = request; @@ -101,9 +101,9 @@ it('should get the same headers as the server', async ({ page, server, browserNa expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName)); }); -it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion }) => { +it('should not return allHeaders() until they are available', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); - it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); + it.fail(browserName === 'webkit' && platform === 'win32' && channel !== 'webkit-wsl', 'Curl does not show accept-encoding and accept-language'); let requestHeadersPromise; page.on('request', request => requestHeadersPromise = request.allHeaders()); @@ -126,9 +126,9 @@ it('should not return allHeaders() until they are available', async ({ page, ser expect(responseHeaders['foo']).toBe('bar'); }); -it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, }) => { +it('should get the same headers as the server CORS', async ({ page, server, browserName, platform, isElectron, browserMajorVersion, channel }) => { it.skip(isElectron && browserMajorVersion < 99, 'This needs Chromium >= 99'); - it.fail(browserName === 'webkit' && platform === 'win32', 'Curl does not show accept-encoding and accept-language'); + it.fail(browserName === 'webkit' && platform === 'win32' && channel !== 'webkit-wsl', 'Curl does not show accept-encoding and accept-language'); await page.goto(server.PREFIX + '/empty.html'); let serverRequest; @@ -392,7 +392,7 @@ it('should report raw headers', async ({ page, server, browserName, platform, is expectedHeaders = []; for (let i = 0; i < req.rawHeaders.length; i += 2) expectedHeaders.push({ name: req.rawHeaders[i], value: req.rawHeaders[i + 1] }); - if (browserName === 'webkit' && platform === 'win32') { + if (browserName === 'webkit' && platform === 'win32' && channel !== 'webkit-wsl') { expectedHeaders = expectedHeaders.filter(({ name }) => name.toLowerCase() !== 'accept-encoding'); // Convert "value": "en-US, en-US" => "en-US" expectedHeaders = expectedHeaders.map(e => { diff --git a/tests/page/page-network-response.spec.ts b/tests/page/page-network-response.spec.ts index e353aa6956daa..fd2ebbfb0b16c 100644 --- a/tests/page/page-network-response.spec.ts +++ b/tests/page/page-network-response.spec.ts @@ -230,10 +230,11 @@ it('should behave the same way for headers and allHeaders', async ({ page, serve expect(allHeaders['name-b']).toEqual('v4'); }); -it('should provide a Response with a file URL', async ({ page, asset, isAndroid, isElectron, isWindows, browserName, mode }) => { +it('should provide a Response with a file URL', async ({ page, asset, isAndroid, isElectron, isWindows, browserName, mode, channel }) => { it.skip(isAndroid, 'No files on Android'); it.skip(browserName === 'firefox', 'Firefox does return null for file:// URLs'); it.skip(mode.startsWith('service')); + it.skip(channel === 'webkit-wsl'); const fileurl = url.pathToFileURL(asset('frames/two-frames.html')).href; const response = await page.goto(fileurl); diff --git a/tests/page/page-request-continue.spec.ts b/tests/page/page-request-continue.spec.ts index 70b5d716673fd..1b8a0d5f2743b 100644 --- a/tests/page/page-request-continue.spec.ts +++ b/tests/page/page-request-continue.spec.ts @@ -55,12 +55,7 @@ it('should not allow to override unsafe HTTP headers', async ({ page, server, br trailer: 'baz', } }).catch(e => e); - if (isElectron) { - // Electron doesn't send the request if the host header is overridden, - // but doesn't throw an error either. - expect(error).toBeFalsy(); - serverRequestPromise.catch(() => {}); - } else if (browserName === 'chromium') { + if (browserName === 'chromium' || isElectron) { expect(error.message).toContain('Unsafe header'); serverRequestPromise.catch(() => {}); } else { diff --git a/tests/page/page-request-fulfill.spec.ts b/tests/page/page-request-fulfill.spec.ts index 0d939a0a5cbc2..7e5f0f9caf8c4 100644 --- a/tests/page/page-request-fulfill.spec.ts +++ b/tests/page/page-request-fulfill.spec.ts @@ -25,15 +25,15 @@ const it = base.extend<{ // which is actually forwarded to the desktop localhost. // To use request such an url with apiRequestContext on the desktop, we need to change it back to localhost. rewriteAndroidLoopbackURL(url: string): string - }>({ - rewriteAndroidLoopbackURL: ({ isAndroid }, use) => use(givenURL => { - if (!isAndroid) - return givenURL; - const requestURL = new URL(givenURL); - requestURL.hostname = 'localhost'; - return requestURL.toString(); - }) - }); +}>({ + rewriteAndroidLoopbackURL: ({ isAndroid }, use) => use(givenURL => { + if (!isAndroid) + return givenURL; + const requestURL = new URL(givenURL); + requestURL.hostname = 'localhost'; + return requestURL.toString(); + }) +}); it('should work', async ({ page, server }) => { await page.route('**/*', route => { @@ -462,7 +462,7 @@ it('should fulfill with gzip and readback', { it('should not go to the network for fulfilled requests body', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30760' }, -}, async ({ page, server, browserName }) => { +}, async ({ page, server }) => { await page.route('**/one-style.css', async route => { return route.fulfill({ status: 404, @@ -482,6 +482,30 @@ it('should not go to the network for fulfilled requests body', { await page.goto(server.PREFIX + '/one-style.html'); const response = await responsePromise; const body = await response.body(); - expect(body).toBeTruthy(); + expect(body.toString()).toBe('Not Found! (mocked)'); expect(serverHit).toBe(false); }); + +it('should return body for fulfilled responses', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-java/issues/1792' }, +}, async ({ page, server }) => { + for (const status of [100, 200, 404, 500]) { + await it.step(`status ${status}`, async () => { + const bodyOverride = `Custom body ${status}`; + await page.route('**/one-style.css', async route => { + return route.fulfill({ + status, + contentType: 'text/plain', + body: bodyOverride, + }); + }); + + const responsePromise = page.waitForResponse('**/one-style.css'); + await page.goto(server.PREFIX + '/one-style.html'); + const response = await responsePromise; + const body = await response.body(); + expect(body.toString()).toBe(bodyOverride); + }); + await page.unrouteAll(); + } +}); diff --git a/tests/page/page-request-fulfill.spec.ts-snapshots/mock-binary-response-moz-firefox-nightly.png b/tests/page/page-request-fulfill.spec.ts-snapshots/mock-binary-response-moz-firefox-nightly.png new file mode 100644 index 0000000000000..ef2b004d4ea79 Binary files /dev/null and b/tests/page/page-request-fulfill.spec.ts-snapshots/mock-binary-response-moz-firefox-nightly.png differ diff --git a/tests/page/page-request-fulfill.spec.ts-snapshots/mock-svg-moz-firefox-nightly.png b/tests/page/page-request-fulfill.spec.ts-snapshots/mock-svg-moz-firefox-nightly.png new file mode 100644 index 0000000000000..064d497091449 Binary files /dev/null and b/tests/page/page-request-fulfill.spec.ts-snapshots/mock-svg-moz-firefox-nightly.png differ diff --git a/tests/page/page-route.spec.ts b/tests/page/page-route.spec.ts index 1d38c0c2e806d..00b9754bd9de7 100644 --- a/tests/page/page-route.spec.ts +++ b/tests/page/page-route.spec.ts @@ -353,6 +353,8 @@ it('should fail navigation when aborting main resource', async ({ page, server, expect(error.message).toContain(isMac && macVersion < 11 ? 'Request intercepted' : 'Blocked by Web Inspector'); else if (browserName === 'firefox') expect(error.message).toContain('NS_ERROR_FAILURE'); + else if (browserName === '_bidiFirefox') + expect(error.message).toContain('NS_ERROR_ABORT'); else expect(error.message).toContain('net::ERR_FAILED'); }); @@ -513,17 +515,14 @@ it('should work with badly encoded server', async ({ page, server }) => { it('should work with encoded server - 2', async ({ page, server, browserName }) => { // The requestWillBeSent will report URL as-is, whereas interception will // report encoded URL for stylesheet. @see crbug.com/759388 + await page.goto(server.EMPTY_PAGE); const requests = []; await page.route('**/*', route => { void route.continue(); requests.push(route.request()); }); - const response = await page.goto(`data:text/html,`); - expect(response).toBe(null); - if (browserName === 'firefox') - expect(requests.length).toBe(2); // Firefox DevTools report to navigations in this case as well. - else - expect(requests.length).toBe(1); + await page.setContent(``); + expect(requests.length).toBe(1); expect((await requests[0].response()).status()).toBe(404); }); @@ -635,11 +634,11 @@ it('should support cors with GET', async ({ page, server, browserName }) => { const response = await fetch('https://example.com/cars?reject', { mode: 'cors' }); return response.json(); }).catch(e => e); - if (browserName === 'chromium') + if (browserName === 'chromium' || browserName === '_bidiChromium') expect(error.message).toContain('Failed'); if (browserName === 'webkit') expect(error.message).toContain('TypeError'); - if (browserName === 'firefox') + if (browserName === 'firefox' || browserName === '_bidiFirefox') expect(error.message).toContain('NetworkError'); } }); diff --git a/tests/page/page-screenshot.spec.ts b/tests/page/page-screenshot.spec.ts index 6c8dca034bd70..1be6ce3e04dca 100644 --- a/tests/page/page-screenshot.spec.ts +++ b/tests/page/page-screenshot.spec.ts @@ -280,14 +280,14 @@ it.describe('page screenshot', () => { expect(screenshot).toMatchSnapshot('screenshot-clip-odd-size.png'); }); - it('should work for canvas', async ({ page, server, isElectron, isMac, isLinux, macVersion, browserName, isHeadlessShell, headless }) => { + it('should work for canvas', async ({ page, server, isElectron, isMac, isLinux, isWindows, macVersion, browserName, isHeadlessShell, headless, channel }) => { it.fixme(isElectron && isMac, 'Fails on the bots'); - it.fixme(browserName === 'webkit' && isLinux && !headless, 'WebKit has slightly different corners on gtk4.'); + it.fixme(browserName === 'webkit' && !headless && (isLinux || (isWindows && channel === 'webkit-wsl')), 'WebKit has slightly different corners on gtk4.'); await page.setViewportSize({ width: 500, height: 500 }); await page.goto(server.PREFIX + '/screenshots/canvas.html'); const screenshot = await page.screenshot(); if ((!isHeadlessShell && browserName === 'chromium' && isMac && os.arch() === 'arm64' && macVersion >= 14) || - (browserName === 'webkit' && isLinux && os.arch() === 'x64')) + (browserName === 'webkit' && isLinux && os.arch() === 'x64') || channel === 'webkit-wsl') expect(screenshot).toMatchSnapshot('screenshot-canvas-with-accurate-corners.png'); else expect(screenshot).toMatchSnapshot('screenshot-canvas.png'); diff --git a/tests/page/page-wait-for-navigation.spec.ts b/tests/page/page-wait-for-navigation.spec.ts index dd5bfbb259c2f..ecc677b485bad 100644 --- a/tests/page/page-wait-for-navigation.spec.ts +++ b/tests/page/page-wait-for-navigation.spec.ts @@ -82,14 +82,14 @@ it('should work with clicking on anchor links', async ({ page, server }) => { expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); }); -it('should work with clicking on links which do not commit navigation', async ({ page, server, httpsServer, browserName, platform }) => { +it('should work with clicking on links which do not commit navigation', async ({ page, server, httpsServer, browserName, platform, channel }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`foobar`); const [error] = await Promise.all([ page.waitForNavigation().catch(e => e), page.click('a'), ]); - expect(error.message).toMatch(expectedSSLError(browserName, platform)); + expect(error.message).toMatch(expectedSSLError(browserName, platform, channel)); }); it('should work with history.pushState()', async ({ page, server }) => { diff --git a/tests/page/to-match-aria-snapshot.spec.ts b/tests/page/to-match-aria-snapshot.spec.ts index f162ff8d13b64..d1c8a08dc7156 100644 --- a/tests/page/to-match-aria-snapshot.spec.ts +++ b/tests/page/to-match-aria-snapshot.spec.ts @@ -338,8 +338,21 @@ test('selected attribute', async ({ page }) => { const e = await expect(page.locator('body')).toMatchAriaSnapshot(` - row [selected=false] `, { timeout: 1000 }).catch(e => e); - expect(stripAnsi(e.message)).toContain('expect(locator).toMatchAriaSnapshot(expected) failed'); - expect(stripAnsi(e.message)).toContain('Timeout: 1000ms'); + expect(stripAnsi(e.message)).toContain(`expect(locator).toMatchAriaSnapshot(expected) failed + +Locator: locator('body') +Timeout: 1000ms +- Expected - 1 ++ Received + 4 + +- - row [selected=false] ++ - table: ++ - rowgroup: ++ - row "Row" [selected]: ++ - cell "Row" + +Call log: +`); } { @@ -425,8 +438,10 @@ test('expected formatter', async ({ page }) => { // Note that error message should not contain any regular expressions, // unlike the baseline generated by --update-snapshots. - expect(stripAnsi(error.message)).toContain(` -Locator: locator('body') + expect(stripAnsi(error.message)).toContain(`expect(locator).toMatchAriaSnapshot(expected) failed + +Locator: locator('body') +Timeout: 1ms - Expected - 2 + Received + 4 @@ -436,7 +451,9 @@ Locator: locator('body') + - heading "todos" [level=1] + - textbox "What needs to be done?" + - button "Time 15:30" -Timeout: 1ms`); + +Call log: +`); }); test('should unpack escaped names', async ({ page }) => { @@ -730,7 +747,6 @@ test('should detect unexpected children: equal', async ({ page }) => { `, { timeout: 1000 }).catch(e => e); expect(stripAnsi(e.message)).toContain('expect(locator).toMatchAriaSnapshot(expected) failed'); - expect(stripAnsi(e.message)).toContain('Timeout: 1000ms'); expect(stripAnsi(e.message)).toContain('+ - listitem: Two'); }); @@ -770,7 +786,6 @@ test('should detect unexpected children: deep-equal', async ({ page }) => { `, { timeout: 1000 }).catch(e => e); expect(stripAnsi(e.message)).toContain('expect(locator).toMatchAriaSnapshot(expected) failed'); - expect(stripAnsi(e.message)).toContain('Timeout: 1000ms'); expect(stripAnsi(e.message)).toContain('+ - listitem: \"1.2\"'); }); @@ -795,7 +810,6 @@ test('should allow restoring contain mode inside deep-equal', async ({ page }) = `, { timeout: 1000 }).catch(e => e); expect(stripAnsi(e.message)).toContain('expect(locator).toMatchAriaSnapshot(expected) failed'); - expect(stripAnsi(e.message)).toContain('Timeout: 1000ms'); expect(stripAnsi(e.message)).toContain('+ - listitem: \"1.2\"'); await expect(page.locator('body')).toMatchAriaSnapshot(` @@ -835,3 +849,15 @@ test('top-level deep-equal', { annotation: { type: 'issue', description: 'https: + - listitem: "1.2" `.trim()); }); + + +test('treat bad regex as a string', async ({ page }) => { + await page.setContent(`Log in`); + const error = await expect(page.locator('body')).toMatchAriaSnapshot(` + - link "Log in": + - /url: /[a/ + `, { timeout: 1 }).catch(e => e); + expect(stripAnsi(error.message)).toContain('expect(locator).toMatchAriaSnapshot(expected) failed'); + expect(stripAnsi(error.message)).toContain('- - /url: /[a/'); + expect(stripAnsi(error.message)).toContain('+ - /url: /foo'); +}); diff --git a/tests/playwright-test/cache.spec.ts b/tests/playwright-test/cache.spec.ts new file mode 100644 index 0000000000000..132cf8ca6ee68 --- /dev/null +++ b/tests/playwright-test/cache.spec.ts @@ -0,0 +1,103 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +import { test, expect } from './playwright-test-fixtures'; + +test('should clear cache with type:module', async ({ runCLICommand }) => { + const result = await runCLICommand({ + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/test'; + export default defineConfig({}); + `, + 'package.json': ` + { "type": "module" } + `, + 'a.spec.ts': ` + import { test } from '@playwright/test'; + test('example', () => {}); + `, + }, 'clear-cache'); + expect(result.exitCode).toBe(0); +}); + +test('should clear cache for ct', async ({ runCLICommand }) => { + const result = await runCLICommand({ + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/test'; + export default defineConfig({}); + `, + 'a.spec.ts': ` + import { test } from '@playwright/test'; + test('example', () => {}); + `, + }, 'clear-cache', []); + expect(result.exitCode).toBe(0); +}); + +test('should automatically clean cached versions of a changed file', async ({ runInlineTest, writeFiles }) => { + const cacheDir = test.info().outputPath('playwright-test-cache'); + await runInlineTest({ + 'a.spec.ts': ` + import { test } from '@playwright/test'; + test('example', () => {}); + `, + }, undefined, { + PWTEST_CACHE_DIR: cacheDir + }); + + const cacheDirectories = await fs.promises.readdir(cacheDir); + expect(cacheDirectories).toHaveLength(1); + const testCacheDirectory = path.join(cacheDir, cacheDirectories[0]); + + const matchRegex = (extension: string) => new RegExp('^([0-9a-f]{10})_([0-9a-f]{7})_aspec\\.' + extension + '$', 'i'); + + let cachedFiles = await fs.promises.readdir(testCacheDirectory); + cachedFiles.sort(); + expect(cachedFiles).toHaveLength(2); + expect(cachedFiles[0]).toMatch(matchRegex('js')); + expect(cachedFiles[1]).toMatch(matchRegex('map')); + + const initialMatches = cachedFiles[0].match(matchRegex('js')); + expect(initialMatches).toHaveLength(3); + const firstPathHash = initialMatches[1]; + const firstTestHash = initialMatches[2]; + + await runInlineTest({ + 'a.spec.ts': ` + import { test } from '@playwright/test'; + test('modified test', () => {}); + `, + }, undefined, { + PWTEST_CACHE_DIR: cacheDir + }); + + cachedFiles = await fs.promises.readdir(testCacheDirectory); + cachedFiles.sort(); + expect(cachedFiles).toHaveLength(2); + expect(cachedFiles[0]).toMatch(matchRegex('js')); + expect(cachedFiles[1]).toMatch(matchRegex('map')); + + const finalMatches = cachedFiles[0].match(matchRegex('js')); + expect(finalMatches).toHaveLength(3); + const finalPathHash = finalMatches[1]; + const finalTestHash = finalMatches[2]; + + expect(finalPathHash).toBe(firstPathHash); + expect(finalTestHash).not.toBe(firstTestHash); +}); diff --git a/tests/playwright-test/clear-cache.spec.ts b/tests/playwright-test/clear-cache.spec.ts deleted file mode 100644 index 128896c2e3ecc..0000000000000 --- a/tests/playwright-test/clear-cache.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright Microsoft Corporation. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { test, expect } from './playwright-test-fixtures'; - -test('should clear cache with type:module', async ({ runCLICommand }) => { - const result = await runCLICommand({ - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/test'; - export default defineConfig({}); - `, - 'package.json': ` - { "type": "module" } - `, - 'a.spec.ts': ` - import { test } from '@playwright/test'; - test('example', () => {}); - `, - }, 'clear-cache'); - expect(result.exitCode).toBe(0); -}); - -test('should clear cache for ct', async ({ runCLICommand }) => { - const result = await runCLICommand({ - 'playwright.config.ts': ` - import { defineConfig } from '@playwright/test'; - export default defineConfig({}); - `, - 'a.spec.ts': ` - import { test } from '@playwright/test'; - test('example', () => {}); - `, - }, 'clear-cache', []); - expect(result.exitCode).toBe(0); -}); diff --git a/tests/playwright-test/expect-configure.spec.ts b/tests/playwright-test/expect-configure.spec.ts index 6b568eb918e9a..b0bf725cd630e 100644 --- a/tests/playwright-test/expect-configure.spec.ts +++ b/tests/playwright-test/expect-configure.spec.ts @@ -49,7 +49,7 @@ test('should configure message', async ({ runInlineTest }) => { expect(result.passed).toBe(0); expect(result.output).toContain('Error: x-foo must be visible'); expect(result.output).toContain('expect(locator).toBeVisible() failed'); - expect(result.output).toContain('Timeout: 1ms'); + expect(result.output).toContain('Timeout: 1ms'); expect(result.output).toContain('Call log:'); }); @@ -68,7 +68,7 @@ test('should prefer local message', async ({ runInlineTest }) => { expect(result.output).toContain('Error: overridden'); expect(result.output).toContain(`expect(locator).toBeVisible() failed`); - expect(result.output).toContain('Timeout: 1ms'); + expect(result.output).toContain('Timeout: 1ms'); expect(result.output).toContain('Call log:'); }); diff --git a/tests/playwright-test/expect.spec.ts b/tests/playwright-test/expect.spec.ts index a5e5b40c2084c..43c42447b6f00 100644 --- a/tests/playwright-test/expect.spec.ts +++ b/tests/playwright-test/expect.spec.ts @@ -68,7 +68,7 @@ test('should include custom expect message with web-first assertions', async ({ expect(result.output).toContain('Error: x-foo must be visible'); expect(result.output).toContain('expect(locator).toBeVisible() failed'); - expect(result.output).toContain('Timeout: 1ms'); + expect(result.output).toContain('Timeout: 1ms'); expect(result.output).toContain('Call log:'); }); @@ -533,13 +533,13 @@ test('should support toHaveURL with baseURL from webServer', async ({ runInlineT }, { workers: 1 }); const output = result.output; expect(output).toContain('expect(page).toHaveURL'); - expect(output).toContain(`Expected string: \"http://localhost:${port}/kek\"`); + expect(output).toContain(`Expected: \"http://localhost:${port}/kek\"`); expect(result.passed).toBe(1); expect(result.failed).toBe(1); expect(result.exitCode).toBe(1); }); -test('should respect expect.timeout', async ({ runInlineTest }) => { +test('should respect expect.timeout in toHaveURL', async ({ runInlineTest }) => { const result = await runInlineTest({ 'playwright.config.js': `module.exports = { expect: { timeout: 1000 } }`, 'a.test.ts': ` @@ -550,7 +550,7 @@ test('should respect expect.timeout', async ({ runInlineTest }) => { await page.goto('data:text/html,
A
'); const error = await expect(page).toHaveURL('data:text/html,
B
').catch(e => e); expect(stripVTControlCharacters(error.message)).toContain('expect(page).toHaveURL(expected) failed'); - expect(stripVTControlCharacters(error.message)).toContain('Timeout: 1000ms'); + expect(stripVTControlCharacters(error.message)).toContain('Timeout: 1000ms'); expect(error.message).toContain('data:text/html,
'); }); `, @@ -570,7 +570,7 @@ test('should support toHaveURL predicate', async ({ runInlineTest }) => { await page.goto('data:text/html,
A
'); const error = await expect(page).toHaveURL(url => url === 'data:text/html,
B
').catch(e => e); expect(stripVTControlCharacters(error.message)).toContain('expect(page).toHaveURL(expected) failed'); - expect(stripVTControlCharacters(error.message)).toContain('Timeout: 1000ms'); + expect(stripVTControlCharacters(error.message)).toContain('Timeout: 1000ms'); expect(error.message).toContain('data:text/html,
'); }); `, @@ -615,8 +615,8 @@ test('should print expected/received before timeout', async ({ runInlineTest }) expect(result.passed).toBe(0); expect(result.failed).toBe(1); expect(result.output).toContain('Test timeout of 2000ms exceeded.'); - expect(result.output).toContain('Expected string: "Text 2"'); - expect(result.output).toContain('Received string: "Text content"'); + expect(result.output).toContain('Expected: "Text 2"'); + expect(result.output).toContain('Received: "Text content"'); }); test('should print pending operations for toHaveText', async ({ runInlineTest }) => { @@ -634,8 +634,8 @@ test('should print pending operations for toHaveText', async ({ runInlineTest }) expect(result.exitCode).toBe(1); const output = result.output; expect(output).toContain(`expect(locator).toHaveText(expected)`); - expect(output).toContain('Expected string: "Text"'); - expect(output).toContain('Received: '); + expect(output).toContain('Expected: "Text"'); + expect(output).toContain('Error: element(s) not found'); expect(output).toContain('waiting for locator(\'no-such-thing\')'); }); @@ -690,8 +690,8 @@ test('should print expected/received on Ctrl+C', async ({ interactWithTestRunner const result = parseTestRunnerOutput(testProcess.output); expect(result.passed).toBe(0); expect(result.interrupted).toBe(1); - expect(result.output).toContain('Expected string: "Text 2"'); - expect(result.output).toContain('Received string: "Text content"'); + expect(result.output).toContain('Expected: "Text 2"'); + expect(result.output).toContain('Received: "Text content"'); }); test('should not print timed out error message when test times out', async ({ runInlineTest }) => { @@ -1024,7 +1024,7 @@ test('should respect timeout from configured expect when used outside of the tes expect(code).toBe(1); expect(stdout).toBe(''); expect(stripAnsi(stderr)).toContain('expect(locator).toBeAttached() failed'); - expect(stripAnsi(stderr)).toContain('Timeout: 10ms'); + expect(stripAnsi(stderr)).toContain('Timeout: 10ms'); }); test('should expose timeout to custom matchers', async ({ runInlineTest, runTSC }) => { diff --git a/tests/playwright-test/fixtures.spec.ts b/tests/playwright-test/fixtures.spec.ts index f822557d4a137..7bde432d0fcdd 100644 --- a/tests/playwright-test/fixtures.spec.ts +++ b/tests/playwright-test/fixtures.spec.ts @@ -816,3 +816,21 @@ test('automatic worker fixtures should start before automatic test fixtures', as 'WORKER FIXTURE 2', ]); }); + +test('should error if use is not called', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test as base, expect } from '@playwright/test'; + const test = base.extend({ + fixture: async ({}, use) => { + return 123; + }, + }); + + test('test', async ({ fixture }) => { + }); + `, + }); + expect(result.failed).toBe(1); + expect(result.output).toContain(`use() was not called in fixture "fixture"`); +}); diff --git a/tests/playwright-test/global-setup.spec.ts b/tests/playwright-test/global-setup.spec.ts index 419fb8753b5e8..aa508f5ef92c0 100644 --- a/tests/playwright-test/global-setup.spec.ts +++ b/tests/playwright-test/global-setup.spec.ts @@ -454,3 +454,38 @@ test('globalTeardown runs even if callback failed', async ({ runInlineTest }) => ]); expect(result.output).toContain('Error: kaboom'); }); + +test('globalSetup and globalTeardown should have PLAYWRIGHT_TEST=1', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + import * as path from 'path'; + module.exports = { + globalSetup: './globalSetup', + globalTeardown: './globalTeardown', + }; + `, + 'globalSetup.ts': ` + export default () => { + console.log('\\n%%setup env=' + process.env.PLAYWRIGHT_TEST); + }; + `, + 'globalTeardown.ts': ` + export default () => { + console.log('\\n%%teardown env=' + process.env.PLAYWRIGHT_TEST); + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test('should work', async ({}, testInfo) => { + console.log('\\n%%test env=' + process.env.PLAYWRIGHT_TEST); + }); + `, + }); + expect(result.passed).toBe(1); + expect(result.failed).toBe(0); + expect(result.outputLines).toEqual([ + 'setup env=1', + 'test env=1', + 'teardown env=1', + ]); +}); diff --git a/tests/playwright-test/playwright-test-fixtures.ts b/tests/playwright-test/playwright-test-fixtures.ts index c06019a3b2d69..641b0f18c2f13 100644 --- a/tests/playwright-test/playwright-test-fixtures.ts +++ b/tests/playwright-test/playwright-test-fixtures.ts @@ -281,7 +281,7 @@ export const test = base const cacheDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-cache-')); await use(async (files: Files, params: Params = {}, env: NodeJS.ProcessEnv = {}, options: RunOptions = {}) => { const baseDir = await writeFiles(testInfo, files, true); - return await runPlaywrightTest(childProcess, baseDir, params, { ...env, PWTEST_CACHE_DIR: cacheDir }, options, files, mergeReports, useIntermediateMergeReport); + return await runPlaywrightTest(childProcess, baseDir, params, { PWTEST_CACHE_DIR: cacheDir, ...env }, options, files, mergeReports, useIntermediateMergeReport); }); await removeFolders([cacheDir]); }, @@ -313,7 +313,7 @@ export const test = base let testProcess: TestChildProcess | undefined; await use(async (files: Files, params: Params = {}, env: NodeJS.ProcessEnv = {}, options: RunOptions = {}) => { const baseDir = await writeFiles(testInfo, files, true); - testProcess = startPlaywrightTest(childProcess, baseDir, params, { ...env, PWTEST_CACHE_DIR: cacheDir }, options); + testProcess = startPlaywrightTest(childProcess, baseDir, params, { PWTEST_CACHE_DIR: cacheDir, ...env }, options); return testProcess; }); await testProcess?.kill(); diff --git a/tests/playwright-test/playwright.ct-build.spec.ts b/tests/playwright-test/playwright.ct-build.spec.ts index 14b6014199cf7..ecf9d6d1fa12d 100644 --- a/tests/playwright-test/playwright.ct-build.spec.ts +++ b/tests/playwright-test/playwright.ct-build.spec.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures'; import fs from 'fs'; import path from 'path'; +import { expect, playwrightCtConfigText, test } from './playwright-test-fixtures'; test.describe.configure({ mode: 'parallel' }); @@ -41,7 +41,7 @@ test('should work with the empty component list', async ({ runInlineTest }, test expect(metainfo.version).toEqual(require('playwright-core/package.json').version); expect(metainfo.viteVersion).toEqual(require('vite/package.json').version); expect(Object.entries(metainfo.deps)).toHaveLength(0); - expect(Object.entries(metainfo.sources)).toHaveLength(9); + expect(Object.entries(metainfo.sources)).toHaveLength(10); }); test('should extract component list', async ({ runInlineTest }, testInfo) => { @@ -351,6 +351,55 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => { }); }); +test('should not crash when cached component test file is deleted', async ({ runInlineTest }, testInfo) => { + + await test.step('run first test to build the cache', async () => { + const result = await runInlineTest({ + 'playwright.config.ts': playwrightCtConfigText, + 'playwright/index.html': ``, + 'playwright/index.ts': ``, + 'src/button.tsx': ` + export const Button = () => ; + `, + 'src/button.test.tsx': ` + import { test, expect } from '@playwright/experimental-ct-react'; + import { Button } from './button.tsx'; + test('pass', async ({ mount }) => { + const component = await mount(); + await expect(component).toHaveText('Button'); + }); + `, + 'src/button2.tsx': ` + export const Button2 = () => ; + `, + 'src/button2.test.tsx': ` + import { test, expect } from '@playwright/experimental-ct-react'; + import { Button2 } from './button2.tsx'; + test('pass', async ({ mount }) => { + const component = await mount(); + await expect(component).toHaveText('Button 2'); + }); + `, + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(2); + + }); + + await test.step('remove the second test and component and run the tests again', async () => { + + fs.unlinkSync(testInfo.outputPath('src/button2.tsx')); + fs.unlinkSync(testInfo.outputPath('src/button2.test.tsx')); + + const result2 = await runInlineTest({}, { workers: 1 }); + + expect(result2.exitCode).toBe(0); + expect(result2.passed).toBe(1); + }); + +}); + test('should not use global config for preview', async ({ runInlineTest }) => { const result1 = await runInlineTest({ 'playwright.config.ts': playwrightCtConfigText, diff --git a/tests/playwright-test/playwright.trace.spec.ts b/tests/playwright-test/playwright.trace.spec.ts index ee46887e19103..82420aa520a64 100644 --- a/tests/playwright-test/playwright.trace.spec.ts +++ b/tests/playwright-test/playwright.trace.spec.ts @@ -785,7 +785,7 @@ test('should use custom expect message in trace', async ({ runInlineTest }, test ]); }); -test('should not throw when merging traces multiple times', async ({ runInlineTest }, testInfo) => { +test('should not throw when merging traces multiple times', async ({ runInlineTest, server }, testInfo) => { test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/27286' }); const result = await runInlineTest({ @@ -813,7 +813,7 @@ test('should not throw when merging traces multiple times', async ({ runInlineTe }); test.beforeAll(async ({ page }) => { - await page.goto('https://playwright.dev'); + await page.goto(${JSON.stringify(server.PREFIX + '/frames/nested-frames.html')}); }); test.afterAll(async ({ context }) => { @@ -821,7 +821,7 @@ test('should not throw when merging traces multiple times', async ({ runInlineTe }); test('foo', async ({ page }) => { - await expect(page.locator('h1')).toContainText('Playwright'); + await expect(page.frameLocator('iframe').nth(1).locator('div')).toHaveText("Hi, I'm frame"); }); `, }, { workers: 1 }); diff --git a/tests/playwright-test/reporter-attachment.spec.ts b/tests/playwright-test/reporter-attachment.spec.ts index 50d6d1ee0c8c4..65a536e77fadf 100644 --- a/tests/playwright-test/reporter-attachment.spec.ts +++ b/tests/playwright-test/reporter-attachment.spec.ts @@ -322,3 +322,30 @@ test('render text attachment with multiple lines', async ({ runInlineTest }) => expect(text).toContain(' ────────────────────────────────────────────────────────────────────────────────────────────────'); expect(result.exitCode).toBe(1); }); + +test('attaching inside boxed fixture should not log error', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37147' } }, async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test as base } from '@playwright/test'; + + const test = base.extend<{ myFixture: void }>({ + myFixture: [async ({}, use, testInfo) => { + await testInfo.attach('my attachment', { + body: 'foo', + contentType: 'text/plain', + }); + await use(); + }, { box: true }], + }); + + test('my test', ({ myFixture }) => { + expect(1).toBe(0); + }); + `, + }, { reporter: 'line' }, {}); + const text = result.output; + expect(text).toContain(' attachment #1: my attachment (text/plain) ──────────────────────────────────────────────────────'); + expect(text).toContain(' foo'); + expect(text).toContain(' ────────────────────────────────────────────────────────────────────────────────────────────────'); + expect(result.exitCode).toBe(1); +}); diff --git a/tests/playwright-test/reporter-blob.spec.ts b/tests/playwright-test/reporter-blob.spec.ts index 3b17948081000..e16e6d753ea7d 100644 --- a/tests/playwright-test/reporter-blob.spec.ts +++ b/tests/playwright-test/reporter-blob.spec.ts @@ -33,18 +33,18 @@ const NEGATIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'x ' : '✘ '; const test = baseTest.extend<{ showReport: (reportFolder?: string) => Promise - }>({ - showReport: async ({ page }, use) => { - let server: HttpServer | undefined; - await use(async (reportFolder?: string) => { - reportFolder ??= test.info().outputPath('playwright-report'); - server = startHtmlReportServer(reportFolder) as HttpServer; - await server.start(); - await page.goto(server.urlPrefix('precise')); - }); - await server?.stop(); - } - }); +}>({ + showReport: async ({ page }, use) => { + let server: HttpServer | undefined; + await use(async (reportFolder?: string) => { + reportFolder ??= test.info().outputPath('playwright-report'); + server = startHtmlReportServer(reportFolder) as HttpServer; + await server.start(); + await page.goto(server.urlPrefix('precise')); + }); + await server?.stop(); + } +}); test.use({ channel: 'chrome' }); test.slow(!!process.env.CI); diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 24c6bbea1179c..8992eac3c0f96 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -1717,6 +1717,25 @@ for (const useIntermediateMergeReport of [true, false] as const) { await expect(page.locator('.label')).toHaveText('webkit'); }); + test('project label should not show if there are no explicit projects', async ({ runInlineTest, showReport, page }) => { + const result = await runInlineTest({ + 'a.test.js': ` + const { expect, test } = require('@playwright/test'); + test('pass', async ({}) => { + expect(1).toBe(1); + }); + `, + }, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }); + + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + + await showReport(); + + await expect(page.locator('.test-file-test .label')).toHaveCount(0); + await expect(page.locator('.label-row')).not.toBeVisible(); + }); + test('testCaseView - after click test label and go back, testCaseView should be visible', async ({ runInlineTest, showReport, page }) => { const result = await runInlineTest({ 'playwright.config.js': ` @@ -3185,6 +3204,42 @@ for (const useIntermediateMergeReport of [true, false] as const) { }); } +test('should support merge files option', async ({ runInlineTest, showReport, page }) => { + await runInlineTest({ + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test.describe('describe', () => { + test('test 1', async ({}) => {}); + }); + test('test 2', async ({}) => {}); + `, + 'b.test.js': ` + import { test, expect } from '@playwright/test'; + test.describe('describe', () => { + test('test 3', async ({}) => {}); + }); + `, + }, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' }); + + await showReport(); + + await page.getByRole('button', { name: 'Settings' }).click(); + await page.getByRole('checkbox', { name: 'Merge files' }).click(); + + await expect(page.locator('body')).toMatchAriaSnapshot(` + - button "" [expanded] + - region: + - link "test 2" + - link "a.test.js:6" + - button "describe" [expanded] + - region: + - link "test 1" + - link "a.test.js:4" + - link "test 3" + - link "b.test.js:4" + `); +}); + function readAllFromStream(stream: NodeJS.ReadableStream): Promise { return new Promise(resolve => { const chunks: Buffer[] = []; diff --git a/tests/playwright-test/runner.spec.ts b/tests/playwright-test/runner.spec.ts index dece006a06ceb..89bd084db49a7 100644 --- a/tests/playwright-test/runner.spec.ts +++ b/tests/playwright-test/runner.spec.ts @@ -751,9 +751,16 @@ test('slow double SIGINT should be respected in reporter.onExit', async ({ inter }); test('unhandled exception in test.fail should restart worker and continue', async ({ runInlineTest }) => { - const result = await runInlineTest({ + const files = { 'a.spec.ts': ` - import { test, expect } from '@playwright/test'; + import { test as baseTest, expect } from '@playwright/test'; + + const test = baseTest.extend({ + worker: [async ({}, use, info) => { + await use(); + console.log('\\n%%worker teardown=' + info.workerIndex); + }, { scope: 'worker', auto: true }], + }); test('bad', async () => { test.fail(); @@ -767,12 +774,20 @@ test('unhandled exception in test.fail should restart worker and continue', asyn test('good', () => { console.log('\\n%%good running worker=' + test.info().workerIndex); }); - ` - }, { retries: 1, reporter: 'list' }); - expect(result.exitCode).toBe(0); - expect(result.passed).toBe(2); - expect(result.failed).toBe(0); - expect(result.outputLines).toEqual(['bad running worker=0', 'good running worker=1']); + `, + }; + for (const parallel of [true, false]) { + await test.step(`parallel=${parallel}`, async () => { + const options = { retries: 1, reporter: 'list', workers: 1 }; + if (parallel) + options['fully-parallel'] = true; + const result = await runInlineTest(files, options); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(2); + expect(result.failed).toBe(0); + expect(result.outputLines).toEqual(['bad running worker=0', 'worker teardown=0', 'good running worker=1', 'worker teardown=1']); + }); + } }); test('wait for workers to finish before reporter.onEnd', async ({ runInlineTest }) => { diff --git a/tests/playwright-test/stable-test-runner/package-lock.json b/tests/playwright-test/stable-test-runner/package-lock.json index 5783e7dae80ea..c23c8fc838741 100644 --- a/tests/playwright-test/stable-test-runner/package-lock.json +++ b/tests/playwright-test/stable-test-runner/package-lock.json @@ -5,16 +5,16 @@ "packages": { "": { "dependencies": { - "@playwright/test": "^1.55.0-alpha-2025-08-18" + "@playwright/test": "^1.56.0-alpha-2025-09-29" } }, "node_modules/@playwright/test": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-JRaYyoUUCLEAZTPLKTyHbnTkiQwslxbrQwG99+q5nquET1zIv78NOPeNzDUbRGY9B7eLB9L34KBfM/5gJawqoQ==", + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-tQvJn0aqEQMpuiJkdl0Y2QfRaF9UYWxsb0FijzRcpZ4N4e+nBe05O5QCKcwlZuo+kTRE3jkMJAEDSWbVI3yVGQ==", "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0-alpha-2025-08-18" + "playwright": "1.56.0-alpha-2025-09-29" }, "bin": { "playwright": "cli.js" @@ -38,12 +38,12 @@ } }, "node_modules/playwright": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-Q4IjkuNEy1eetYFMBGY5QvguL5DUNRsJsha5dIj+VZ7feCtucHup/bfOfs716zTrnZ5WDYmdd3RteTdwx2Vmuw==", + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-5JRWGuKNMW0Yl1DXsMn8XFlwLu5gGAyyxNNs++LSY04IYxTZ2RAShkaDRacUf3bPOEO7YhkV9QPhdCUDEeetFg==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0-alpha-2025-08-18" + "playwright-core": "1.56.0-alpha-2025-09-29" }, "bin": { "playwright": "cli.js" @@ -56,9 +56,9 @@ } }, "node_modules/playwright-core": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-UefQibPrUeVhXRbb0V7PniATbscGRqamtjs/TmG4ez4rjWDTPq/wd7qMP25ZeWHUOODYsaQGnn6Nc9JChWNTvQ==", + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-miREetirQbDx9wgH+/Z9RzxycL/6BfTjk/H3eSOacFcZtkMVXvF6Mzbp/0s6z/nyVBvgvqqdULaEzK98zsIR/g==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -70,11 +70,11 @@ }, "dependencies": { "@playwright/test": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-JRaYyoUUCLEAZTPLKTyHbnTkiQwslxbrQwG99+q5nquET1zIv78NOPeNzDUbRGY9B7eLB9L34KBfM/5gJawqoQ==", + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-tQvJn0aqEQMpuiJkdl0Y2QfRaF9UYWxsb0FijzRcpZ4N4e+nBe05O5QCKcwlZuo+kTRE3jkMJAEDSWbVI3yVGQ==", "requires": { - "playwright": "1.55.0-alpha-2025-08-18" + "playwright": "1.56.0-alpha-2025-09-29" } }, "fsevents": { @@ -84,18 +84,18 @@ "optional": true }, "playwright": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-Q4IjkuNEy1eetYFMBGY5QvguL5DUNRsJsha5dIj+VZ7feCtucHup/bfOfs716zTrnZ5WDYmdd3RteTdwx2Vmuw==", + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-5JRWGuKNMW0Yl1DXsMn8XFlwLu5gGAyyxNNs++LSY04IYxTZ2RAShkaDRacUf3bPOEO7YhkV9QPhdCUDEeetFg==", "requires": { "fsevents": "2.3.2", - "playwright-core": "1.55.0-alpha-2025-08-18" + "playwright-core": "1.56.0-alpha-2025-09-29" } }, "playwright-core": { - "version": "1.55.0-alpha-2025-08-18", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0-alpha-2025-08-18.tgz", - "integrity": "sha512-UefQibPrUeVhXRbb0V7PniATbscGRqamtjs/TmG4ez4rjWDTPq/wd7qMP25ZeWHUOODYsaQGnn6Nc9JChWNTvQ==" + "version": "1.56.0-alpha-2025-09-29", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.0-alpha-2025-09-29.tgz", + "integrity": "sha512-miREetirQbDx9wgH+/Z9RzxycL/6BfTjk/H3eSOacFcZtkMVXvF6Mzbp/0s6z/nyVBvgvqqdULaEzK98zsIR/g==" } } } diff --git a/tests/playwright-test/stable-test-runner/package.json b/tests/playwright-test/stable-test-runner/package.json index d46a4c4c46dd5..ff193cab863ed 100644 --- a/tests/playwright-test/stable-test-runner/package.json +++ b/tests/playwright-test/stable-test-runner/package.json @@ -1,6 +1,6 @@ { "private": true, "dependencies": { - "@playwright/test": "^1.55.0-alpha-2025-08-18" + "@playwright/test": "^1.56.0-alpha-2025-09-29" } } diff --git a/tests/playwright-test/test-list.spec.ts b/tests/playwright-test/test-list.spec.ts new file mode 100644 index 0000000000000..c307d461533dc --- /dev/null +++ b/tests/playwright-test/test-list.spec.ts @@ -0,0 +1,132 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path from 'path'; +import fs from 'fs'; +import { test, expect } from './playwright-test-fixtures'; + +const varietyWorkspace = { + 'playwright.config.ts': ` + module.exports = { projects: [{ name: 'p1' }, { name: 'p2' }], testDir: 'tests', }; + `, + 'dir/test.list': ` + # this is a multiline comment + + # with an empty line in between + [p1] > dir1/a.test.ts > test1 + [p2] › dir2${path.sep}b.spec.ts › suite › test2 + + # more comments + dir3/c.spec.ts > test2 + `, + 'empty.list': ` + # nothing to see here + `, + 'ignore.list': ` + [p1] > dir1/a.test.ts > test1 + `, + 'tests/dir1/a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('test1', async () => { console.log('\\n%%a-test1-' + test.info().project.name); }); + test('test2', async () => { console.log('\\n%%a-test2-' + test.info().project.name); }); + `, + 'tests/dir2/b.spec.ts': ` + import { test, expect } from '@playwright/test'; + test.describe('suite', () => { + test('test1', async () => { console.log('\\n%%b-test1-' + test.info().project.name); }); + test.describe(() => { + test('test2', async () => { console.log('\\n%%b-test2-' + test.info().project.name); }); + }); + }); + `, + 'tests/dir3/c.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('test1', async () => { console.log('\\n%%c-test1-' + test.info().project.name); }); + test('test2', async () => { console.log('\\n%%c-test2-' + test.info().project.name); }); + `, +}; + +test('--test-list should work', async ({ runInlineTest }) => { + const result = await runInlineTest(varietyWorkspace, { 'workers': 1, 'test-list': 'dir/test.list' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(4); + expect(result.outputLines).toEqual([ + 'a-test1-p1', + 'c-test2-p1', + 'b-test2-p2', + 'c-test2-p2', + ]); +}); + +test('--test-list-invert should work', async ({ runInlineTest }) => { + const result = await runInlineTest(varietyWorkspace, { 'workers': 1, 'test-list-invert': 'dir/test.list' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(8); + expect(result.outputLines).toEqual([ + 'a-test2-p1', + 'b-test1-p1', + 'b-test2-p1', + 'c-test1-p1', + 'a-test1-p2', + 'a-test2-p2', + 'b-test1-p2', + 'c-test1-p2', + ]); +}); + +test('--test-list applies before --test-list-invert', async ({ runInlineTest }) => { + const result = await runInlineTest(varietyWorkspace, { 'workers': 1, 'test-list': 'dir/test.list', 'test-list-invert': 'ignore.list' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(3); + expect(result.outputLines).toEqual([ + 'c-test2-p1', + 'b-test2-p2', + 'c-test2-p2', + ]); +}); + +test('empty --test-list should work', async ({ runInlineTest }) => { + const result = await runInlineTest(varietyWorkspace, { 'workers': 1, 'test-list': 'empty.list' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(0); + expect(result.outputLines).toEqual([ + ]); +}); + +test('--list output should work for --test-list', async ({ runInlineTest }) => { + const listResult = await runInlineTest(varietyWorkspace, { 'list': true }); + expect(listResult.exitCode).toBe(0); + const lines = listResult.output.split('\n').filter(line => !line.includes('Listing tests') && !line.includes('Total:')); + await fs.promises.writeFile(test.info().outputPath('generated.list'), lines.join('\n'), 'utf-8'); + + const result = await runInlineTest(varietyWorkspace, { 'workers': 1, 'test-list': 'generated.list' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(12); + expect(result.outputLines).toEqual([ + 'a-test1-p1', + 'a-test2-p1', + 'b-test1-p1', + 'b-test2-p1', + 'c-test1-p1', + 'c-test2-p1', + 'a-test1-p2', + 'a-test2-p2', + 'b-test1-p2', + 'b-test2-p2', + 'c-test1-p2', + 'c-test2-p2', + ]); +}); diff --git a/tests/playwright-test/test-server.spec.ts b/tests/playwright-test/test-server.spec.ts index 157989c47e688..e333827fe6811 100644 --- a/tests/playwright-test/test-server.spec.ts +++ b/tests/playwright-test/test-server.spec.ts @@ -227,3 +227,32 @@ test('clear cache', async ({ startTestServer, writeFiles }) => { await testServerConnection.clearCache({}); expect((await testServerConnection.runGlobalTeardown({})).status).toBe('passed'); }); + +test('timeout override', async ({ startTestServer, writeFiles }) => { + const testServerConnection = await startTestServer(); + await testServerConnection.initialize({}); + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('foo', () => { + expect(test.info().timeout).toEqual(42); + }); + `, + }); + + expect(await testServerConnection.runTests({ timeout: 42 })).toEqual({ status: 'passed' }); +}); + +test('PLAYWRIGHT_TEST environment variable', async ({ startTestServer, writeFiles }) => { + const testServerConnection = await startTestServer(); + await testServerConnection.initialize({}); + await writeFiles({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('foo', () => { + expect(process.env.PLAYWRIGHT_TEST).toBe('1'); + }); + `, + }); + expect(await testServerConnection.runTests({})).toEqual({ status: 'passed' }); +}); diff --git a/tests/playwright-test/test-step.spec.ts b/tests/playwright-test/test-step.spec.ts index 3e472d4311193..60e818d60474f 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -640,7 +640,7 @@ test('should not propagate errors from within toPass', async ({ runInlineTest }) expect(result.exitCode).toBe(0); expect(result.output).toBe(` hook |Before Hooks -test.step |Expect "toPass" @ a.test.ts:7 +expect |Expect "toPass" @ a.test.ts:7 expect | Expect "toBe" @ a.test.ts:6 expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality expect | Expect "toBe" @ a.test.ts:6 @@ -667,8 +667,8 @@ test('should show final toPass error', async ({ runInlineTest }) => { expect(result.exitCode).toBe(1); expect(stripAnsi(result.output)).toBe(` hook |Before Hooks -test.step |Expect "toPass" @ a.test.ts:6 -test.step |↪ error: Error: expect(received).toBe(expected) // Object.is equality +expect |Expect "toPass" @ a.test.ts:6 +expect |↪ error: Error: expect(received).toBe(expected) // Object.is equality expect | Expect "toBe" @ a.test.ts:5 expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality hook |After Hooks @@ -934,7 +934,7 @@ test('step inside toPass', async ({ runInlineTest }) => { expect(stripAnsi(result.output)).toBe(` hook |Before Hooks test.step |step 1 @ a.test.ts:4 -test.step | Expect "toPass" @ a.test.ts:11 +expect | Expect "toPass" @ a.test.ts:11 test.step | step 2, attempt: 0 @ a.test.ts:7 test.step | ↪ error: Error: expect(received).toBe(expected) // Object.is equality expect | Expect "toBe" @ a.test.ts:9 @@ -981,7 +981,7 @@ fixture | Fixture "context" pw:api | Create context fixture | Fixture "page" pw:api | Create page -test.step |Expect "toPass" @ a.test.ts:11 +expect |Expect "toPass" @ a.test.ts:11 pw:api | Navigate to "about:blank" @ a.test.ts:6 test.step | inner step attempt: 0 @ a.test.ts:7 test.step | ↪ error: Error: expect(received).toBe(expected) // Object.is equality @@ -1033,7 +1033,7 @@ fixture | Fixture "context" pw:api | Create context fixture | Fixture "page" pw:api | Create page -test.step |Expect "poll toHaveLength" @ a.test.ts:14 +expect |Expect "poll toHaveLength" @ a.test.ts:14 pw:api | Navigate to "about:blank" @ a.test.ts:7 test.step | inner step attempt: 0 @ a.test.ts:8 expect | Expect "toBe" @ a.test.ts:10 @@ -1086,7 +1086,7 @@ pw:api | Create context fixture | Fixture "page" pw:api | Create page pw:api |Set content @ a.test.ts:4 -test.step |Expect "poll toBe" @ a.test.ts:13 +expect |Expect "poll toBe" @ a.test.ts:13 expect | Expect "toHaveText" @ a.test.ts:7 test.step | iteration 1 @ a.test.ts:9 expect | Expect "toBeVisible" @ a.test.ts:10 @@ -1746,7 +1746,7 @@ fixture | Fixture "page" pw:api | Create page pw:api |Set content @ a.test.ts:16 expect |Expect "toBeInvisible" @ a.test.ts:17 -test.step | Expect "poll toBe" @ a.test.ts:7 +expect | Expect "poll toBe" @ a.test.ts:7 pw:api | Query count locator('div').filter({ visible: true }) @ a.test.ts:7 expect | Expect "toBe" @ a.test.ts:7 expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality @@ -1770,7 +1770,7 @@ pw:api | Close context `); }); -test('should box fixtures with everything inside them', async ({ runInlineTest }) => { +test('should box fixtures', async ({ runInlineTest }) => { const result = await runInlineTest({ 'reporter.ts': stepIndentReporter, 'playwright.config.ts': `module.exports = { reporter: './reporter' };`, @@ -1784,6 +1784,7 @@ test('should box fixtures with everything inside them', async ({ runInlineTest } await page.goto('data:text/html,
here we go
'); }); await use(1); + await page.setContent('
here we go
'); }, { box: true }], bar: [async ({ page }, use) => { await page.setContent('
here we go
'); @@ -1791,13 +1792,23 @@ test('should box fixtures with everything inside them', async ({ runInlineTest } await page.goto('data:text/html,
here we go
'); }); await use(2); + await page.setContent('
here we go
'); }, { box: false }], + baz: [async ({ page }, use) => { + await page.setContent('
here we go
'); + await test.step('inner step', async () => { + await page.goto('data:text/html,
here we go
'); + }); + await use(3); + await page.setContent('
here we go
'); + }, { box: 'self' }], }); - test('test', async ({ foo, bar, page }) => { + test('test', async ({ foo, bar, baz, page }) => { await expect(page.locator('body')).toBeVisible(); expect(foo).toBe(1); expect(bar).toBe(2); + expect(baz).toBe(3); }); ` }, { reporter: '' }); @@ -1812,14 +1823,20 @@ pw:api | Create context fixture | Fixture "page" pw:api | Create page fixture | Fixture "bar" @ a.test.ts:4 -pw:api | Set content @ a.test.ts:13 -test.step | inner step @ a.test.ts:14 -pw:api | Navigate to "data:" @ a.test.ts:15 -expect |Expect "toBeVisible" @ a.test.ts:22 -expect |Expect "toBe" @ a.test.ts:23 -expect |Expect "toBe" @ a.test.ts:24 +pw:api | Set content @ a.test.ts:14 +test.step | inner step @ a.test.ts:15 +pw:api | Navigate to "data:" @ a.test.ts:16 +pw:api | Set content @ a.test.ts:22 +test.step | inner step @ a.test.ts:23 +pw:api | Navigate to "data:" @ a.test.ts:24 +expect |Expect "toBeVisible" @ a.test.ts:32 +expect |Expect "toBe" @ a.test.ts:33 +expect |Expect "toBe" @ a.test.ts:34 +expect |Expect "toBe" @ a.test.ts:35 hook |After Hooks +pw:api | Set content @ a.test.ts:27 fixture | Fixture "bar" @ a.test.ts:4 +pw:api | Set content @ a.test.ts:19 fixture | Fixture "page" fixture | Fixture "context" pw:api | Close context diff --git a/tests/playwright-test/to-have-screenshot.spec.ts b/tests/playwright-test/to-have-screenshot.spec.ts index 0fea415bf1058..6ec17e52f79dd 100644 --- a/tests/playwright-test/to-have-screenshot.spec.ts +++ b/tests/playwright-test/to-have-screenshot.spec.ts @@ -1350,8 +1350,26 @@ test('should throw pretty error if expected PNG file is not a PNG', async ({ run `, }); expect(result.exitCode).toBe(1); - expect(result.output).toContain('could not decode image as PNG.'); - expect(result.output).toContain('could not decode image as JPEG.'); + expect(result.output).toContain('Could not decode expected image as PNG.'); + expect(result.output).toContain('Could not decode expected image as JPEG.'); +}); + +test('should throw pretty error if expected PNG file is not a PNG while rebasing', async ({ runInlineTest }) => { + const result = await runInlineTest({ + ...playwrightConfig({ + snapshotPathTemplate: '__screenshots__/{testFilePath}/{arg}{ext}', + }), + '__screenshots__/a.spec.js/snapshot.png': 'not a png', + 'a.spec.js': ` + const { test, expect } = require('@playwright/test'); + test('png', async ({ page }) => { + await expect(page).toHaveScreenshot('snapshot.png'); + }); + `, + }, { 'update-snapshots': true }); + expect(result.exitCode).toBe(1); + expect(result.output).toContain('Failed to re-generate expected.'); + expect(result.output).toContain('Could not decode expected image as PNG.'); }); test('should support maskColor option', async ({ runInlineTest }) => { diff --git a/tests/playwright-test/ui-mode-test-attachments.spec.ts b/tests/playwright-test/ui-mode-test-attachments.spec.ts index 6d466acec547b..4984afd9fc07e 100644 --- a/tests/playwright-test/ui-mode-test-attachments.spec.ts +++ b/tests/playwright-test/ui-mode-test-attachments.spec.ts @@ -173,6 +173,35 @@ test('should link from attachment step to attachments view', async ({ runUITest await expect(attachment).toBeInViewport(); }); +test('attachments from inside boxed fixture should be visible', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37147' } }, async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.test.ts': ` + import { test as base } from '@playwright/test'; + + const test = base.extend<{ myFixture: void }>({ + myFixture: [async ({}, use, testInfo) => { + await testInfo.attach('my attachment', { + body: 'foo', + contentType: 'text/plain', + }); + await use(); + }, { box: true }], + }); + + test('my test', ({ myFixture }) => {}); + `, + }, { reporter: 'line' }, {}); + await page.getByText('my test').click(); + await page.getByTitle('Run all').click(); + await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); + + await page.getByRole('treeitem', { name: 'attach "my attachment"' }).getByLabel('Open Attachment').click(); + await expect(page.getByRole('tabpanel', { name: 'Attachments' })).toMatchAriaSnapshot(` + - tabpanel: + - button /my attachment/ + `); +}); + function readAllFromStream(stream: NodeJS.ReadableStream): Promise { return new Promise(resolve => { const chunks: Buffer[] = []; diff --git a/tests/playwright-test/ui-mode-test-network-tab.spec.ts b/tests/playwright-test/ui-mode-test-network-tab.spec.ts index 10810d1616184..14004105edf86 100644 --- a/tests/playwright-test/ui-mode-test-network-tab.spec.ts +++ b/tests/playwright-test/ui-mode-test-network-tab.spec.ts @@ -200,3 +200,29 @@ test('should not duplicate network entries from beforeAll', { await page.getByText('Network', { exact: true }).click(); await expect(page.getByRole('list', { name: 'Network requests' }).getByText('empty.html')).toHaveCount(1); }); + +test('should not preserve selection across test runs', async ({ runUITest, server }) => { + server.setRoute('/api/endpoint', (_, res) => res.setHeader('Content-Type', 'application/json').end()); + + const { page } = await runUITest({ + 'a.spec.ts': ` + import { test } from '@playwright/test'; + + test('some test', async ({ page }) => { + await page.goto('${server.PREFIX}/network-tab/network.html'); + // await page.evaluate(() => (window as any).donePromise); + }); + `, + }); + + await page.getByText('some test').dblclick(); + await page.getByText('Network', { exact: true }).click(); + + await page.getByText('network.html', { exact: true }).click(); + await expect(page.getByText('General')).toBeVisible(); + + await page.getByText('some test').dblclick(); + await expect(page.getByText('network.html', { exact: true })).toBeVisible(); + + await expect(page.getByText('General')).not.toBeVisible(); +}); diff --git a/tests/playwright-test/ui-mode-test-tree.spec.ts b/tests/playwright-test/ui-mode-test-tree.spec.ts index e663b986b6a28..93761b7d9ace4 100644 --- a/tests/playwright-test/ui-mode-test-tree.spec.ts +++ b/tests/playwright-test/ui-mode-test-tree.spec.ts @@ -480,6 +480,66 @@ test('should expand all', { `); }); +test('should allow expanding entire subtrees', async ({ runUITest }) => { + const { page } = await runUITest(basicTestTree); + + await page.getByTestId('test-tree').getByText('suite').click(); + await page.getByTitle('Collapse all').click(); + await expect.poll(dumpTestTree(page)).toContain(` + ► ◯ a.test.ts + ► ◯ b.test.ts + `); + + const firstTestClosedLocator = page.getByTitle('a.test.ts').locator('.codicon-chevron-right').first(); + const firstTestOpenLocator = page.getByTitle('a.test.ts').locator('.codicon-chevron-down').first(); + + await firstTestClosedLocator.click(); + await expect.poll(dumpTestTree(page)).toContain(` + ▼ ◯ a.test.ts + ◯ passes + ◯ fails + ► ◯ suite + ► ◯ b.test.ts + `); + + await firstTestOpenLocator.click(); + await firstTestClosedLocator.click({ modifiers: ['Alt'] }); + + await expect.poll(dumpTestTree(page)).toContain(` + ▼ ◯ a.test.ts + ◯ passes + ◯ fails + ▼ ◯ suite + ◯ inner passes + ◯ inner fails + ► ◯ b.test.ts + `); + + await firstTestOpenLocator.click(); + await firstTestClosedLocator.click(); + + await expect.poll(dumpTestTree(page)).toContain(` + ▼ ◯ a.test.ts + ◯ passes + ◯ fails + ▼ ◯ suite + ◯ inner passes + ◯ inner fails + ► ◯ b.test.ts + `); + + await firstTestOpenLocator.click({ modifiers: ['Alt'] }); + await firstTestClosedLocator.click(); + + await expect.poll(dumpTestTree(page)).toContain(` + ▼ ◯ a.test.ts + ◯ passes + ◯ fails + ► ◯ suite + ► ◯ b.test.ts + `); +}); + test('should resolve title conflicts', async ({ runUITest }) => { const { page } = await runUITest({ 'a.test.ts': ` @@ -517,3 +577,57 @@ test('should resolve title conflicts', async ({ runUITest }) => { - treeitem "[icon-circle-outline] bar 2" `); }); + +test('should merge files', async ({ runUITest }) => { + const { page } = await runUITest({ + 'a.test.ts': ` + import { test } from '@playwright/test'; + + test("first", () => {}); + + test.describe("group", () => { + test("second", () => {}); + }); + + test("third", () => {}); + `, + 'b.test.ts': ` + import { test } from '@playwright/test'; + + test("fourth", () => {}); + + test.describe("group", () => { + test("fifth", () => {}); + }); + + test("sixth", () => {}); + ` + }); + + await page.getByText('Settings', { exact: true }).click(); + await page.getByLabel('Merge files').click(); + + await expect.poll(dumpTestTree(page)).toContain(` + ▼ ◯ + ◯ first + ◯ third + ◯ fourth + ◯ sixth + ▼ ◯ group + ◯ second + ◯ fifth + `); + await expect(page.getByTestId('test-tree')).toMatchAriaSnapshot(` + - tree: + - treeitem "[icon-circle-outline] " [expanded]: + - group: + - treeitem "[icon-circle-outline] first" + - treeitem "[icon-circle-outline] third" + - treeitem "[icon-circle-outline] fourth" + - treeitem "[icon-circle-outline] sixth" + - treeitem "[icon-circle-outline] group" [expanded]: + - group: + - treeitem "[icon-circle-outline] second" + - treeitem "[icon-circle-outline] fifth" + `); +}); diff --git a/tests/playwright-test/ui-mode-trace.spec.ts b/tests/playwright-test/ui-mode-trace.spec.ts index 56c4607bef23b..48fd3873e5b4b 100644 --- a/tests/playwright-test/ui-mode-trace.spec.ts +++ b/tests/playwright-test/ui-mode-trace.spec.ts @@ -506,10 +506,11 @@ test('should hide boxed fixtures and contents, reveal upon show all actions sett - treeitem /After Hooks/ `); - await page.getByText('Settings').click(); - await page.getByText('Show route actions').click(); - await page.getByText('Show configuration actions').click(); - await page.getByText('Show getter actions').click(); + await page.getByRole('button', { name: 'Filter actions' }).click(); + await page.locator('.setting').getByText('Network routes').click(); + await page.locator('.setting').getByText('Getters').click(); + await page.locator('.setting').getByText('Configuration').click(); + await page.getByRole('button', { name: 'Filter actions' }).click(); await page.getByTestId('actions-tree').getByRole('treeitem', { name: 'Before Hooks' }).locator('.codicon-chevron-right').click(); await page.getByTestId('actions-tree').getByRole('treeitem', { name: 'Fixture "fixture"' }).locator('.codicon-chevron-right').click(); diff --git a/tests/playwright-test/update-aria-snapshot.spec.ts b/tests/playwright-test/update-aria-snapshot.spec.ts index 78cdab3bab7e5..203b5c27e43a8 100644 --- a/tests/playwright-test/update-aria-snapshot.spec.ts +++ b/tests/playwright-test/update-aria-snapshot.spec.ts @@ -519,7 +519,7 @@ test('should not update snapshots when locator did not match', async ({ runInlin expect(fs.existsSync(patchPath)).toBe(false); expect(result.output).not.toContain('New baselines created'); expect(result.output).toContain('Expected: "- heading"'); - expect(result.output).toContain('Received: '); + expect(result.output).toContain('Error: element(s) not found'); }); test.describe('update-snapshots none', () => { diff --git a/tests/playwright-test/watch.spec.ts b/tests/playwright-test/watch.spec.ts index 750c3f854df61..45395f861bdb0 100644 --- a/tests/playwright-test/watch.spec.ts +++ b/tests/playwright-test/watch.spec.ts @@ -721,7 +721,7 @@ test('should run CT on changed deps', async ({ runWatchTest, writeFiles }) => { await testProcess.waitForOutput(`src${path.sep}button.spec.tsx:4:11 › pass`); expect(testProcess.output).not.toContain(`src${path.sep}link.spec.tsx`); await testProcess.waitForOutput(`Error: expect(locator).toHaveText(expected) failed`); - await testProcess.waitForOutput('Timeout: 1000ms'); + await testProcess.waitForOutput('Timeout: 1000ms'); await testProcess.waitForOutput('Waiting for file changes.'); }); diff --git a/tests/playwright-test/web-server.spec.ts b/tests/playwright-test/web-server.spec.ts index 92600121052a2..1d79c0ba1a3e3 100644 --- a/tests/playwright-test/web-server.spec.ts +++ b/tests/playwright-test/web-server.spec.ts @@ -123,6 +123,67 @@ test('should create a server with environment variables', async ({ runInlineTest delete process.env['FOOEXTERNAL']; }); +test('should run web server with PLAYWRIGHT_TEST=1 environment variable', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37377' } +}, async ({ runInlineTest }, { workerIndex }) => { + const port = workerIndex * 2 + 10500; + const result = await runInlineTest({ + 'test.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('connect to the server', async ({baseURL, page}) => { + expect(baseURL).toBe('http://localhost:${port}'); + await page.goto(baseURL + '/env-PLAYWRIGHT_TEST'); + expect(await page.textContent('body')).toBe('1'); + }); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: { + command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port}', + port: ${port}, + } + }; + `, + }, {}, { DEBUG: 'pw:webserver' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + expect(result.output).toContain('[WebServer] listening'); + expect(result.output).toContain('[WebServer] error from server'); + expect(result.report.suites[0].specs[0].tests[0].results[0].status).toContain('passed'); +}); + +test('should allow to unset PLAYWRIGHT_TEST environment variable', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37377' } +}, async ({ runInlineTest }, { workerIndex }) => { + const port = workerIndex * 2 + 10500; + const result = await runInlineTest({ + 'test.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('connect to the server', async ({baseURL, page}) => { + expect(baseURL).toBe('http://localhost:${port}'); + await page.goto(baseURL + '/env-PLAYWRIGHT_TEST'); + expect(await page.textContent('body')).toBe(''); + }); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: { + command: 'node ${JSON.stringify(SIMPLE_SERVER_PATH)} ${port}', + port: ${port}, + env: { + 'PLAYWRIGHT_TEST': undefined, + } + } + }; + `, + }, {}, { DEBUG: 'pw:webserver' }); + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + expect(result.output).toContain('[WebServer] listening'); + expect(result.output).toContain('[WebServer] error from server'); + expect(result.report.suites[0].specs[0].tests[0].results[0].status).toContain('passed'); +}); + test('should default cwd to config directory', async ({ runInlineTest }, testInfo) => { const port = testInfo.workerIndex * 2 + 10500; const configDir = testInfo.outputPath('foo'); @@ -864,3 +925,24 @@ test.describe('name option', () => { expect(result.output).toContain(`[${defaultPrefix}]`); }); }); + +test('should throw helpful error when command is empty', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'test.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('pass', async ({}) => {}); + `, + 'playwright.config.ts': ` + module.exports = { + webServer: [ + { + command: '', + url: 'http://localhost:3000', + } + ], + }; + `, + }, undefined); + expect(result.exitCode).toBe(1); + expect(result.output).toContain('config.webServer.command cannot be empty'); +}); diff --git a/tests/playwright-test/worker-index.spec.ts b/tests/playwright-test/worker-index.spec.ts index 7ab48caefa311..e84a205d565c0 100644 --- a/tests/playwright-test/worker-index.spec.ts +++ b/tests/playwright-test/worker-index.spec.ts @@ -324,3 +324,24 @@ test('should respect project.workers>1', async ({ runInlineTest }) => { 'test1-end', ]); }); + +test('should not inherit config.workers into project.workers', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + export default { + workers: 1, + }; + `, + 'a.test.js': ` + import { test, expect } from '@playwright/test'; + test.describe.configure({ mode: 'parallel' }); + test('test1', async ({}, testInfo) => { + }); + test('test2', async ({}, testInfo) => { + }); + `, + }, { workers: 2 }); + expect(result.passed).toBe(2); + expect(result.exitCode).toBe(0); + expect(result.output).toContain('Running 2 tests using 2 workers'); +}); diff --git a/tests/stress/heap.spec.ts b/tests/stress/heap.spec.ts index 66bedfe9ff265..d3d6f41a1d91f 100644 --- a/tests/stress/heap.spec.ts +++ b/tests/stress/heap.spec.ts @@ -66,7 +66,6 @@ test('should not leak dispatchers after closing page', async ({ context, server expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/page').Page)).toBe(COUNT); expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/dispatchers/networkDispatchers').RequestDispatcher)).toBe(COUNT); expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/dispatchers/networkDispatchers').ResponseDispatcher)).toBe(COUNT); - expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/console').ConsoleMessage)).toBe(0); for (const page of pages) await page.close(); @@ -75,7 +74,6 @@ test('should not leak dispatchers after closing page', async ({ context, server expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/page').Page)).toBe(0); expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/dispatchers/networkDispatchers').RequestDispatcher)).toBe(0); expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/dispatchers/networkDispatchers').ResponseDispatcher)).toBe(0); - expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/console').ConsoleMessage)).toBe(0); expect(await queryObjectCount(require('../../packages/playwright-core/lib/client/page').Page)).toBeLessThan(COUNT); expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/page').Page)).toBe(0); @@ -83,6 +81,18 @@ test('should not leak dispatchers after closing page', async ({ context, server expect(await queryObjectCount(require('../../packages/playwright-core/lib/client/network').Response)).toBe(0); }); +test('should not leak requests over 100', async ({ context, server }) => { + const page = await context.newPage(); + await page.goto(server.PREFIX + '/title.html'); + for (let i = 0; i < 100; ++i) + await page.evaluate(url => fetch(url), server.EMPTY_PAGE); + await page.requests(); + for (let i = 0; i < 200; ++i) + await page.evaluate(url => fetch(url), server.EMPTY_PAGE); + await page.requests(); + expect(await queryObjectCount(require('../../packages/playwright-core/lib/server/dispatchers/networkDispatchers').RequestDispatcher)).toBeLessThanOrEqual(100); +}); + test.describe(() => { test.beforeEach(() => { require('../../packages/playwright-core/lib/server/dispatchers/dispatcher').setMaxDispatchersForTest(100); diff --git a/tests/webview2/playwright.config.ts b/tests/webview2/playwright.config.ts index 3feb25792cfa8..e33981d7329cb 100644 --- a/tests/webview2/playwright.config.ts +++ b/tests/webview2/playwright.config.ts @@ -28,6 +28,9 @@ const testDir = path.join(__dirname, '..'); const config: Config = { testDir, outputDir, + expect: { + timeout: 10000, + }, timeout: 30000, globalTimeout: 5400000, workers: process.env.CI ? 1 : undefined, diff --git a/utils/build/build-playwright-driver.sh b/utils/build/build-playwright-driver.sh index 35cff644fd4b6..aadf190edeea3 100755 --- a/utils/build/build-playwright-driver.sh +++ b/utils/build/build-playwright-driver.sh @@ -4,7 +4,7 @@ set -x trap "cd $(pwd -P)" EXIT SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)" -NODE_VERSION="22.18.0" # autogenerated via ./update-playwright-driver-version.mjs +NODE_VERSION="22.20.0" # autogenerated via ./update-playwright-driver-version.mjs cd "$(dirname "$0")" PACKAGE_VERSION=$(node -p "require('../../package.json').version") diff --git a/utils/build/build.js b/utils/build/build.js index 37ea64d3920e2..bdd7b15451125 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -236,6 +236,7 @@ function copyFile(file, from, to) { * outdir?: string, * outfile?: string, * minify?: boolean, + * alias?: Record, * }} BundleOptions */ @@ -260,6 +261,9 @@ bundles.push({ outdir: 'packages/playwright/lib', entryPoints: ['src/mcpBundleImpl.ts'], external: ['express'], + alias: { + 'raw-body': 'raw-body.ts', + }, }); bundles.push({ @@ -508,6 +512,7 @@ for (const bundle of bundles) { ...(bundle.outfile ? { outfile: filePath(bundle.outfile) } : {}), ...(bundle.external ? { external: bundle.external } : {}), ...(bundle.minify !== undefined ? { minify: bundle.minify } : {}), + alias: bundle.alias ? Object.fromEntries(Object.entries(bundle.alias).map(([k, v]) => [k, path.join(filePath(bundle.modulePath), v)])) : undefined, metafile: true, plugins: [pkgSizePlugin], }; @@ -665,6 +670,12 @@ copyFiles.push({ to: 'packages/playwright-core/lib', }); +copyFiles.push({ + files: 'packages/playwright/src/agents/*.md', + from: 'packages/playwright/src', + to: 'packages/playwright/lib', +}); + if (watchMode) { // Run TypeScript for type checking. steps.push(new ProgramStep({ diff --git a/utils/doclint/cli.js b/utils/doclint/cli.js index a694cd07497ef..33f2846ee0722 100755 --- a/utils/doclint/cli.js +++ b/utils/doclint/cli.js @@ -197,6 +197,7 @@ async function run() { 'java', 'css', 'js', + 'markdown', 'ts', 'python', 'py', @@ -217,7 +218,7 @@ async function run() { 'Dockerfile', ]); if (!allowedCodeLangs.has(node.codeLang.split(' ')[0])) - throw new Error(`${path.relative(PROJECT_DIR, filePath)} contains code block with invalid code block language ${node.codeLang}`); + throw new Error(`${path.relative(PROJECT_DIR, filePath)} contains code block with invalid code block language "${node.codeLang}"`); } if (node.type.startsWith('h')) { const hash = mdSectionHash(node.text || ''); diff --git a/utils/flakiness-dashboard/processing/utils.js b/utils/flakiness-dashboard/processing/utils.js index a0f166f7e5473..06cd2b09dec67 100644 --- a/utils/flakiness-dashboard/processing/utils.js +++ b/utils/flakiness-dashboard/processing/utils.js @@ -14,9 +14,9 @@ * limitations under the License. */ // @ts-check -const { DefaultAzureCredential } = require('@azure/identity'); +const { ManagedIdentityCredential } = require('@azure/identity'); const { BlobServiceClient } = require('@azure/storage-blob'); -const defaultAzureCredential = new DefaultAzureCredential(); +const defaultAzureCredential = new ManagedIdentityCredential(); const zlib = require('zlib'); const util = require('util'); diff --git a/utils/generate_clip_paths.js b/utils/generate_clip_paths.js index 210a8405818f8..96747e7f62bf1 100644 --- a/utils/generate_clip_paths.js +++ b/utils/generate_clip_paths.js @@ -57,6 +57,7 @@ const outFile = path.join(ROOT, 'packages', 'injected', 'src', 'recorder', 'clip const iconNames = [ 'gripper', 'circle-large-filled', + 'stop-circle', 'inspect', 'whole-word', 'eye', diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 374f66f179e35..665c7ecd32d12 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -21,7 +21,16 @@ export type BlobReporterOptions = { outputDir?: string, fileName?: string }; export type ListReporterOptions = { printSteps?: boolean }; export type JUnitReporterOptions = { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean }; export type JsonReporterOptions = { outputFile?: string }; -export type HtmlReporterOptions = { outputFolder?: string, open?: 'always' | 'never' | 'on-failure', host?: string, port?: number, attachmentsBaseURL?: string, title?: string, noSnippets?: boolean }; +export type HtmlReporterOptions = { + outputFolder?: string; + open?: 'always' | 'never' | 'on-failure'; + host?: string; + port?: number; + attachmentsBaseURL?: string; + title?: string; + noSnippets?: boolean; + noCopyPrompt?: boolean; +}; export type ReporterDescription = Readonly< ['blob'] | ['blob', BlobReporterOptions] | @@ -191,13 +200,13 @@ export type WorkerFixture = (args: Args, use: (r: R) => Prom type TestFixtureValue = Exclude | TestFixture; type WorkerFixtureValue = Exclude | WorkerFixture; export type Fixtures = { - [K in keyof PW]?: WorkerFixtureValue | [WorkerFixtureValue, { scope: 'worker', timeout?: number | undefined, title?: string, box?: boolean }]; + [K in keyof PW]?: WorkerFixtureValue | [WorkerFixtureValue, { scope: 'worker', timeout?: number | undefined, title?: string, box?: boolean | 'self' }]; } & { - [K in keyof PT]?: TestFixtureValue | [TestFixtureValue, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean }]; + [K in keyof PT]?: TestFixtureValue | [TestFixtureValue, { scope: 'test', timeout?: number | undefined, title?: string, box?: boolean | 'self' }]; } & { - [K in Exclude]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: [WorkerFixtureValue, { scope: 'worker', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean | 'self' }]; } & { - [K in Exclude]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean }]; + [K in Exclude]?: TestFixtureValue | [TestFixtureValue, { scope?: 'test', auto?: boolean, option?: boolean, timeout?: number | undefined, title?: string, box?: boolean | 'self' }]; }; type BrowserName = 'chromium' | 'firefox' | 'webkit'; diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index de8c95376b675..8c7863a3c8edf 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -334,18 +334,16 @@ export type AndroidKey = 'Home' | 'Back' | 'Call' | 'EndCall' | - '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | - 'Star' | 'Pound' | '*' | '#' | + '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | + 'Star' | '*' | 'Pound' | '#' | 'DialUp' | 'DialDown' | 'DialLeft' | 'DialRight' | 'DialCenter' | 'VolumeUp' | 'VolumeDown' | - 'ChannelUp' | 'ChannelDown' | 'Power' | 'Camera' | 'Clear' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | - 'Comma' | ',' | - 'Period' | '.' | + 'Comma' | ',' | 'Period' | '.' | 'AltLeft' | 'AltRight' | 'ShiftLeft' | 'ShiftRight' | 'Tab' | '\t' | @@ -373,8 +371,26 @@ export type AndroidKey = 'Notification' | 'Search' | 'RecentApps' | + 'MediaPlayPause' | + 'MediaStop' | + 'MediaNext' | + 'MediaPrevious' | + 'MediaRewind' | + 'MediaFastForward' | + 'MediaPlay' | + 'MediaPause' | + 'MediaClose' | + 'MediaEject' | + 'MediaRecord' | + 'ChannelUp' | 'ChannelDown' | 'AppSwitch' | 'Assist' | + 'MediaAudioTrack' | + 'MediaTopMenu' | + 'MediaSkipForward' | + 'MediaSkipBackward' | + 'MediaStepForward' | + 'MediaStepBackward' | 'Cut' | 'Copy' | 'Paste'; diff --git a/utils/generate_types/test/test.ts b/utils/generate_types/test/test.ts index 7aec26a418f88..a71dab64b0b6a 100644 --- a/utils/generate_types/test/test.ts +++ b/utils/generate_types/test/test.ts @@ -228,9 +228,9 @@ playwright.chromium.launch().then(async browser => { const launchOptions: playwright.LaunchOptions = { devtools: true, env: { - TIMEOUT: 52, + TIMEOUT: '52', SOMETHING: '/some/path', - JEST_TEST: true + JEST_TEST: 'true' } }; const browser = await playwright.chromium.launch(launchOptions); diff --git a/utils/publish_all_packages.sh b/utils/publish_all_packages.sh index 23e4d3eca19f6..20d86d8856747 100755 --- a/utils/publish_all_packages.sh +++ b/utils/publish_all_packages.sh @@ -15,19 +15,18 @@ trap "cleanup; cd $(pwd -P)" EXIT cd "$(dirname $0)" if [[ $1 == "--help" ]]; then - echo "usage: $(basename $0) [--release|--release-candidate|--alpha|--beta]" + echo "usage: $(basename $0) [--release|--alpha|--beta]" echo echo "Publishes all packages." echo echo "--release publish @latest version of all packages" - echo "--release-candidate publish @rc version of all packages" echo "--alpha publish @next version of all packages" echo "--beta publish @beta version of all packages" exit 1 fi if [[ $# < 1 ]]; then - echo "Please specify either --release, --beta or --alpha or --release-candidate" + echo "Please specify either --release, --beta or --alpha" exit 1 fi @@ -36,11 +35,6 @@ if ! command -v npm >/dev/null; then exit 1 fi -if ! npm whoami >/dev/null 2>&1; then - echo "ERROR: NPM is not logged in." - exit 1 -fi - cd .. NPM_PUBLISH_TAG="next" @@ -54,33 +48,22 @@ if [[ "$1" == "--release" ]]; then fi # Ensure package version does not contain dash. if [[ "${VERSION}" == *-* ]]; then - echo "ERROR: cannot publish pre-release version with --release flag" + echo "ERROR: cannot publish pre-release version ${VERSION} with --release flag" exit 1 fi NPM_PUBLISH_TAG="latest" -elif [[ "$1" == "--release-candidate" ]]; then - if [[ -n $(git status -s) ]]; then - echo "ERROR: git status is dirty; some uncommitted changes or untracked files" - exit 1 - fi - # Ensure package version is properly formatted. - if [[ "${VERSION}" != *-rc* ]]; then - echo "ERROR: release candidate version must have a dash" - exit 1 - fi - NPM_PUBLISH_TAG="rc" elif [[ "$1" == "--alpha" ]]; then - # Ensure package version contains alpha and does not contain rc - if [[ "${VERSION}" != *-alpha* || "${VERSION}" == *-rc* ]]; then - echo "ERROR: cannot publish release version with --alpha flag" + # Ensure package version contains alpha. + if [[ "${VERSION}" != *-alpha* ]]; then + echo "ERROR: cannot publish release version ${VERSION} with --alpha flag" exit 1 fi NPM_PUBLISH_TAG="next" elif [[ "$1" == "--beta" ]]; then - # Ensure package version contains dash. - if [[ "${VERSION}" != *-beta* || "${VERSION}" == *-rc* ]]; then - echo "ERROR: cannot publish release version with --beta flag" + # Ensure package version contains beta. + if [[ "${VERSION}" != *-beta* ]]; then + echo "ERROR: cannot publish release version ${VERSION} with --beta flag" exit 1 fi @@ -94,7 +77,7 @@ echo "==================== Publishing version ${VERSION} ================" node ./utils/workspace.js --ensure-consistent node ./utils/workspace.js --list-public-package-paths | while read package do - npm publish --access=public ${package} --tag="${NPM_PUBLISH_TAG}" --provenance + npm publish --access=public ${package} --tag="${NPM_PUBLISH_TAG}" done echo "Done." diff --git a/utils/workspace.js b/utils/workspace.js index a03b62f52fa2b..5331dc3335ef7 100755 --- a/utils/workspace.js +++ b/utils/workspace.js @@ -219,11 +219,6 @@ const workspace = new Workspace(ROOT_PATH, [ path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue'), files: ['LICENSE'], }), - new PWPackage({ - name: '@playwright/test-runner-mcp', - path: path.join(ROOT_PATH, 'packages', 'playwright-test-mcp'), - files: ['LICENSE'], - }), ]); if (require.main === module) {