Skip to content

Add disable javascript option#723

Open
Pablo1Gustavo wants to merge 2 commits intochrome-php:1.15from
Pablo1Gustavo:feat/disable-javascript-option
Open

Add disable javascript option#723
Pablo1Gustavo wants to merge 2 commits intochrome-php:1.15from
Pablo1Gustavo:feat/disable-javascript-option

Conversation

@Pablo1Gustavo
Copy link
Copy Markdown

@Pablo1Gustavo Pablo1Gustavo commented Apr 25, 2026

Add disableJavascript option

Motivation

The library already exposes Page::setScriptExecution(bool) for toggling JavaScript on a per-page basis, but it must be invoked manually on every page after creation and before navigating, which is easy to forget and verbose when the caller's intent is "this whole browser instance never needs JavaScript".

This PR introduces a first-class browser-level option, disableJavascript, that disables JavaScript execution automatically on every page produced by the browser. It follows the same negative-boolean convention already used by noSandbox and disableNotifications (default false, active when true), so the public API stays consistent and predictable.

Benefits

  • Convenience. No need to remember to call setScriptExecution(false) on every newly created page; the option is set once on the factory and propagates to all pages.
  • Correct timing. The CDP message is sent during page initialization, before any navigation can happen, so the very first request already runs without script execution. Doing this manually is racy.
  • Determinism for PDF and screenshot pipelines. A concrete real-world use case: integrating laravel-pdf (which uses chrome-php under the hood) to render server-side reports. The HTML templates are fully static — no client-side rendering, no analytics, no third-party widgets — but bundled CSS frameworks still ship small JS payloads that fire on load and trigger reflows, network fetches, and timers. None of that affects the final PDF, but it can introduce flakiness — for example, a hanging promise delaying waitForNavigation, or a timer keeping the page in a non-idle state. With disableJavascript => true, Chrome skips script execution entirely, making the render more deterministic and predictable. The performance impact depends entirely on what JavaScript the page contains.
  • Hardening / scraping. When using chrome-php to crawl untrusted pages purely for their HTML (link extraction, content scraping, metadata harvesting), disabling JS at the browser level cuts off an entire class of side effects without having to remember to do it per-page.

Implementation

The option flows from the factory down to each newly created page:

  1. BrowserFactorydisableJavascript is documented in the options docblock alongside the other negative-boolean options.
  2. BrowserProcess::start() — after the browser instance is created, if the option is true the process calls setPageScriptExecutionDisabled(true) on the browser. The CLI approach (--blink-settings or any command-line flag) is intentionally not used.
  3. Browser — exposes a new setter setPageScriptExecutionDisabled(bool) that toggles a protected pageScriptExecutionDisabled flag. This mirrors the existing setPagePreScript() pattern already used to inject behaviour on every new page.
  4. Browser::getPage() — when a brand-new page is created, after Page.enable / Network.enable / Runtime.enable / Page.setLifecycleEventsEnabled and before the page is returned to the caller, the flag is checked and Page::setScriptExecution(false) is invoked (with await()). The CDP method used is Emulation.setScriptExecutionDisabled with value: true.

The default value is false, so existing code that does not pass disableJavascript keeps the previous behaviour exactly — JavaScript remains enabled and no extra CDP message is sent. Zero regression.

The README.md options table has a new row documenting the option, following the formatting of the adjacent disableNotifications row.

Tests

Two new integration tests were added in tests/BrowserFactoryTest.php, exercising a real Chrome instance against the existing tests/resources/static-web/javascript.html fixture (the same fixture used by PageTest::testSetScriptExecution):

  • testDisableJavascriptOption() — creates a browser with disableJavascript => true, navigates to the fixture, and asserts that the rendered body text is "javascript disabled" (the <noscript> content), proving that the inline <script> was not executed.
  • testDisableJavascriptOptionDefault() — creates a browser without the option and asserts that the rendered body text is "javascript enabled", proving that the default behaviour is unchanged.

Both new tests pass. The full test suite was also re-run and the only failures are three pre-existing flaky tests unrelated to this change (BrowsingTest::testGetPagesClose, DomTest::testRootNodeIdIsUpdatedAfterReload, BrowserFactoryTest::testConnectToBrowser) — they fail in isolation on the base branch as well, with no involvement of disableJavascript.

Manual testing

The change was also exercised visually with a small headed-mode demo (not committed) that opens a real Chrome window pointing to a page which:

  • shows a <noscript> red banner saying "JavaScript is DISABLED",
  • contains an inline <script> that unhides a green "ENABLED" banner, starts a setInterval-driven counter, and wires a button to alert().

Running the demo with disableJavascript => false shows the green banner, the counter ticking, and the button firing the alert. Running with disableJavascript => true shows only the red <noscript> banner, with no counter and no working button — confirming end-to-end that the CDP toggle is taking effect from the very first paint, with no leakage from the inline script.

Public API surface

  • BrowserFactory — one new option key documented in the options docblock and in the README table. No new methods.
  • Browser — one new public method setPageScriptExecutionDisabled(bool $disabled = true): void, symmetric with the existing setPagePreScript(). This is additive only.
  • No existing public API was changed or removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant