Repository navigation
fix(shell): enforce the navigation policy when a top-frame load starts - #179
Merged
Merged
Conversation
The navigation policy is also enforced when a top-frame load starts, so every top-frame load follows it. decideTopFrameLoad judges the URL the WebView reports at a load start or a load end, never the event alone: a page on the server's origin stays, as does anything the policy loads in the WebView; a page of an active sign-in excursion is the excursion's to judge. Any other page is stopped, sent where the policy sends it (the in-app browser or the OS), and the WebView is re-created on the server where the Console was. A second stop before a Console is ready shows the failure page instead of re-creating again. Reports from a WebView that is being re-created are dropped. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
A top-frame load the policy does not keep in the WebView is stopped, opened where the policy sends it (nothing for a blocked page), and the WebView goes back to the server where the Console was; at load end too, without counting that load end as the Console's. The server's own pages, an in-app history change, a route a native screen owns, a trusted host and a sign-in excursion's pages stay; the excursion ends by its own rules. A second stop before a Console is ready shows the failure page. Reports from a WebView being re-created are dropped. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
The WebView mock keeps each mounted WebView's latest props, so a test reports through the instance that was replaced, as its native side would, including the render it got for a new server before a switch replaced it. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
…case Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
… start; changeset Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
|
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv Seat verdict: ACCEPT, landed by the seat under the maintainer ruling on #160 and the maintainer's in-session ruling for this order. Head-sha: 维护者速读
席位核对
|
os-justin
pushed a commit
that referenced
this pull request
Nov 3, 2026
Stack both intents with #179 (navigation policy at load start) and #180 (pickImage, share, openFile): BRIDGE_METHODS carries both sets, the app lock also refuses pickImage, share and openFile while locked, every locale key from both sides is in all ten bundles, and the iOS permission texts carry camera, photos and Face ID. pnpm-lock.yaml regenerated by pnpm from main's. READ_EXTERNAL_STORAGE is no longer blocked: the image picker and the file system module declare it too. Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Clause-②: no
Metadata-protocol: none
维护者速读
components/shell/ConsoleWebView.tsx的observeTopFrame与「被替换 WebView 的报告丢弃」守卫;lib/shell/navigation-policy.ts的decideTopFrameLoad;components/shell/OfflineFallback.tsx的新失败页。npx jest --ci __tests__/components/ConsoleWebView.test.tsx __tests__/lib/shell/navigation-policy.test.ts,预期 2 个套件、108 个用例全部通过。What changes
decideTopFrameLoad(lib/shell/navigation-policy.ts) judges the URL the WebView reports with a load start or a load end, never the event alone (Android reports an in-app history change as a load start). A page on the server's origin stays, a route a native screen owns included, and so does anythingdecideNavigationloads in the top frame (a trusted host,about:,blob:).stopLoading()), sent where the policy sends that URL, the same as for a request: the in-app browser for a web page, the OS for an app scheme, nowhere for a blocked page. The WebView is then re-created on the server, at the route the Console was on (the load state machine'srecreate). Re-create rather than reload: a reload would load the stopped page again, while a new WebView drops the page and its back history, the path an excursion takes when it ends without returning.policyvariant ofOfflineFallback, "Can't show this page in the app", with Try again and Switch organization, in all ten locales. A Console that becomes ready (or a switch) clears it.docs/HYBRID-SHELL.md: the policy is enforced when a navigation is requested and again when a top-frame load starts. Changeset:mobilepatch.Tests
__tests__/lib/shell/navigation-policy.test.ts→decideTopFrameLoad: keeps the server's own pages (a native-owned route and the default port included); keeps trusted hosts,about:,blob:; stops any other page with where to open it; stops a blocked page opening nothing; judges every load as the top frame's.__tests__/components/ConsoleWebView.test.tsx→ new block "the navigation policy at load start": a load the policy does not keep is stopped, opened where the policy sends it, and the WebView goes back to the server at the Console's route; the same at load end, without that load end lifting the cover; a blocked load opens nothing; the server's own pages, an in-app history change and a native-owned route stay; a trusted host stays; a repeat before ready shows the failure page and opens nothing more; a ready Console in between allows a new re-create; the replaced WebView's reports are dropped, after a stop and after a switch.Acceptance notes
docs/HYBRID-SHELL.mdgains one sentence. The failure-page variant and the re-create choice are described in code comments and the changeset.onError/onHttpErrorfrom a WebView that is being replaced can still put up the failure page. This PR does not change that, and every re-create path already has it.components/shell/ConsoleWebView.tsx,lib/shell/navigation-policy.tsand the touched tests follow Prettier in the changed hunks. Lines that were already off-format onmainin__tests__/components/ConsoleWebView.test.tsxanddocs/HYBRID-SHELL.mdare left as they were.Verification
All readings on head
328a832,origin/main=668ed7e(an ancestor of the head).Gates (exit codes, captured before any pipe)
Ablation 1: the load-start check removed (8 red)
if (decision.kind === "stay") return;replaced byreturn;inobserveTopFrame(anchor 1 → 0, blob2227a64e9d0a→eeec8e8fdce7), restored to the HEAD blob with an emptygit diff HEAD.Ablation 2: the replaced-WebView guard removed (4 red)
The four
if (documentRef.current !== instance) return;guards replaced by a no-op (anchor 4 → 0, blob2227a64e9d0a→15d1976a663a), restored to the HEAD blob with an emptygit diff HEAD.Generated by Claude Code