Skip to content

Block content with rule lists (Canvas 37) - #76

Merged
shannah merged 4 commits into
masterfrom
claude/exciting-edison-eawj15
Nov 4, 2026
Merged

shannah merged 4 commits into
masterfrom
claude/exciting-edison-eawj15

Conversation

@shannah

@shannah shannah commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Network-level content blocking on all three engines, in WebKit's content-blocker JSON format. Matching requests are refused before they are sent (block, block-cookies), and matching elements are hidden (css-display-none). This is the library half of a Brave-style ad blocker for agentic-app-framework's browser.

  • WebViewContentRules compiles, looks up and removes a named list in a store directory the application chooses. The compiled list is kept there across launches. isSupported() reports whether the loaded native and engine can apply lists.
  • WebViewComponent gains addContentRuleList, removeContentRuleList, removeAllContentRuleLists and getContentRuleLists. Lists added before display are applied before the first navigation. Popups inherit the opener's lists natively, and an adopted popup's component is seeded with them in Java.
  • macOS: WKContentRuleListStore + addContentRuleList: on the view's WKUserContentController; lists are copied onto popup children at creation.
  • Linux: WebKitUserContentFilterStore + webkit_user_content_manager_add_filter. The store symbols are optional loader symbols, so WebKitGTK older than 2.24 still loads and reports unsupported.
  • Windows: WebView2 has no rule compiler.
    • A WebResourceRequested hook (* filter, added only once a list is applied) asks a portable Java matcher on every http(s)/ws(s) request: ContentRuleMatcher, indexed by 3-grams of each rule's required literal text.
    • Hide rules travel as one replaceable document-created script.
    • Popup children get the opener's hook (their own global ref) and script inside the NewWindowRequested deferral. Both are removed at adoption.

The SPDD documents are story 13, the analysis, and Canvas 37 (spdd/prompt/37-…-Block-Content-With-Rule-Lists.md).

Fixes found during on-device testing

  • AWT before the JAWT preload (Canvas 8). WebViewNative's static initializer SIGSEGVed in libawt_lwawt JNI_OnLoad on macOS when it ran before AWT. This was latent until WebViewContentRules became a native entry point reachable before any Swing component exists. The initializer now calls Toolkit.getDefaultToolkit() first, except when headless.
  • WKErrorDomain codes (Canvas 37 D12). The codes are LookUpFailed 7, RemoveFailed 8 and VersionMismatch 9. A lookup of a missing list, or of one compiled by another WebKit version, now answers null; removing a missing list completes normally.
  • Windows 403 (Canvas 37 D9). WebView2 cannot fail a request with a network error from its request hook, so a blocked request is answered with HTTP 403. Nothing is sent, but the page's fetch() resolves with ok == false instead of rejecting. The demo's in-page check accepts either outcome, and the README documents it.

Known differences and limits (documented in the README and the Canvas)

  • Windows third-party test: decided on an approximation of the public suffix list.
  • Windows load-context: a rule limited to child-frame never matches, because the frame of a subresource is unknown.
  • Windows make-https / notify: accepted and ignored.
  • Windows blocked-request result: the page sees a 403 rather than a network error.
  • Linux shared manager: an adopted popup shares its opener's content manager. Removing a list from either view removes it from both.
  • Blocked-request reporting: not provided; no engine reports it portably.

Verification

  • JUnit: mvn test passes, 311 tests including the new ContentRuleMatcherTest and ContentRuleSetTest. 50,000 rules decide 1,000 URLs well under a second.
  • CRDEMO_AUTO=1 demo prints PASS on all three platforms:
    • Linux: xvfb-run -a env CRDEMO_AUTO=1 ./run-linux-content-rules-demo.sh (lightweight, the only Linux mode).
    • macOS: CRDEMO_AUTO=1 ./run-mac-content-rules-demo.sh on Apple Silicon (Corretto 8).
    • Windows: set CRDEMO_AUTO=1 then run-windows-content-rules-demo.bat on x64 (WebView2 156).
  • The demo checks:
    • AC1/AC2/AC5: the /ads/ image, script and fetch beacon and the third-party script never reach the server; /ok/ and first-party requests do.
    • AC3: the ad slots are hidden.
    • AC6: lookup finds the stored list, and an unknown name answers null.
    • AC7: an invalid list is reported.
    • AC8: after removing the list and reloading, the blocked requests arrive.
    • Remove: lookup afterwards answers null.
  • AC9 (adopted popups): not exercised by the auto demo on any platform.

Release note

Downstream, the agentic-app-framework ad blocker needs this release, and webview-run must re-shade it in lockstep before AAF's pins can move.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SZMcbkWZkcYkmBAauwZXxZ

Adds network-level content blocking in WebKit's content-blocker JSON
format on all three engines, so an application (an ad or tracker
blocker) can stop matching requests before they are sent and hide
matching elements:

- WebViewContentRules: compile / lookup / remove a named list in a store
  directory, kept across launches; isSupported() probe.
- WebViewComponent.addContentRuleList / removeContentRuleList /
  removeAllContentRuleLists / getContentRuleLists, replayed before the
  first navigation and inherited by popups.
- macOS: WKContentRuleListStore + WKUserContentController.
- Linux: WebKitUserContentFilterStore (optional symbols, WebKitGTK 2.24+).
- Windows: a WebResourceRequested hook answered by a portable Java
  matcher (ContentRuleMatcher, 3-gram indexed), plus one replaceable
  document-created script for css-display-none.

Story 13, analysis and Canvas 37; JUnit tests for the matcher and the
per-component set; WebViewContentRulesDemo with Linux/macOS/Windows run
scripts. Verified on Linux (lightweight, Xvfb): CRDEMO_AUTO passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SZMcbkWZkcYkmBAauwZXxZ
@shannah
shannah marked this pull request as ready for review November 3, 2026 19:44
On macOS (Corretto 8, arm64) WebViewNative's static initializer SIGSEGVed
in libawt_lwawt's JNI_OnLoad when it ran before AWT was initialized:
loading libjawt pulls in libawt_lwawt, which needs the state
Toolkit.getDefaultToolkit() sets up. It was latent while every native
entry point came after a Swing component existed; Canvas 37's
WebViewContentRules.isSupported()/compile() are reached before any UI,
which is how the content rules demo crashed at launch.

The initializer now calls Toolkit.getDefaultToolkit() first unless the
JVM is headless, ignoring any failure so loading proceeds as before.
Canvas 8 (Requirements, Approach, Operation 4, Safeguards) records it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SZMcbkWZkcYkmBAauwZXxZ
Looking up a never-compiled list on macOS failed with "WKErrorDomain
error 7": the codes were off by one. WKError.h has LookUpFailed 7,
RemoveFailed 8 and VersionMismatch 9. Lookup now answers "not found" for
7 and 9 (a list compiled by another WebKit must be recompiled), and
remove answers "not found" for 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SZMcbkWZkcYkmBAauwZXxZ
…d (Canvas 37 D9)

Verified on Windows: every blocked request (ads, beacon, third-party
script) never reached the server, but the page's fetch() resolved with
the synthetic 403 rather than rejecting, because WebView2 cannot fail a
request with a network error from WebResourceRequested. The demo's
in-page AC2 check now counts a rejected fetch or a non-ok response as
failed, and Canvas 37 D9 and the README record the difference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SZMcbkWZkcYkmBAauwZXxZ
@shannah
shannah merged commit 7aeb4a0 into master Nov 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants