Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions apps/desktop/electron/main/browser-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,18 @@ export class BrowserPane {
wc.on("did-navigate", (_event, url) => {
if (this.acceptNativeNavigation(url)) push();
});
wc.on("did-navigate-in-page", (_event, url) => {
if (this.acceptNativeNavigation(url)) push();
wc.on("did-navigate-in-page", (_event, url, isMainFrame, processId, routingId) => {
// Same-document navigation has no will-navigate event. Accept only the
// active main frame's current URL, without reopening invalidated sessions.
if (
!isMainFrame ||
!this.hasCurrentStateEventScope() ||
url !== wc.getURL() ||
processId !== wc.mainFrame.processId ||
routingId !== wc.mainFrame.routingId
) return;
this.enableStateEvents(this.navigationEpoch, url);
push();
});
wc.on("page-title-updated", push);
wc.on(
Expand Down
46 changes: 46 additions & 0 deletions apps/desktop/test/browser-pane-navigation.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const electron = `data:text/javascript,${encodeURIComponent(`
constructor() {
this.webContents = Object.assign(new EventEmitter(), {
url: "https://fixture.invalid/previous",
mainFrame: { processId: 7, routingId: 11 },
pendingLoads: [],
loadURL(url) {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -100,3 +101,48 @@ test("late native navigation events cannot publish after the session is invalida
assert.equal(published.length, 1);
assert.equal(published[0].url, "https://fixture.invalid/second");
});


async function loadedPage() {
const published = [];
const pane = new BrowserPane((state) => published.push(state));
const request = pane.navigateAndWait("https://fixture.invalid/page");
const wc = WebContentsView.instances.at(-1).webContents;
wc.url = "https://fixture.invalid/page";
wc.pendingLoads.shift().resolve();
await request;
return { pane, wc, published };
}

for (const suffix of ["#details", "?route=settings"]) {
test(`same-document navigation publishes ${suffix} and settles loading`, async () => {
const { wc, published } = await loadedPage();
wc.isLoading = () => true;
wc.emit("did-start-loading");
wc.url += suffix;
wc.emit("did-navigate-in-page", {}, wc.url, true, 7, 11);
wc.isLoading = () => false;
wc.emit("did-stop-loading");
assert.equal(published.at(-1).url, wc.url);
assert.equal(published.at(-1).isLoading, false);
});
}

test("in-page events from subframes, replaced frames, or old URLs are ignored", async () => {
const { wc, published } = await loadedPage();
const currentUrl = wc.url;
wc.emit("did-navigate-in-page", {}, currentUrl, false, 7, 11);
wc.emit("did-navigate-in-page", {}, currentUrl, true, 8, 11);
wc.emit("did-navigate-in-page", {}, currentUrl, true, 7, 12);
wc.emit("did-navigate-in-page", {}, currentUrl + "#old", true, 7, 11);
assert.deepEqual(published, []);
});

test("in-page completion cannot republish an invalidated session", async () => {
const { pane, wc, published } = await loadedPage();
pane.invalidateNavigation();
wc.url += "#late";
wc.emit("did-navigate-in-page", {}, wc.url, true, 7, 11);
wc.emit("did-stop-loading");
assert.deepEqual(published, []);
});
5 changes: 5 additions & 0 deletions docs/spec/04-ux/08-component-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,11 @@ entirely inside the plugin's isolated page:
A failed switch or one exceeding the existing 15-second load wait remains
hidden until retried; a late network completion does not automatically reveal it.

- Main-frame same-document navigation (fragment links and History API routes) updates
the browser address, history controls, and loading state without requiring a
full document load. Subframe events and events from an invalidated session or
replaced main frame must not publish browser state.

### 5.3 States

| State | Behavior |
Expand Down
14 changes: 14 additions & 0 deletions docs/spec/06-delivery/04-e2e-test-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -3518,6 +3518,20 @@ identify the platform validation still needed.
controlled native-browser/Host boundaries, and deterministic timers. Native
Electron compositing and the reporter's live sessions are not covered.

#### E2E-BROWSER-in-page-navigation: Browser chrome follows same-document navigation

- **Preconditions**: Browser plugin enabled; a local page has a fragment link
and a History API route control.
- **Steps**: Open the page; click the fragment link; change the route with
`history.pushState`; use Back, Forward, and Reload. Switch to another session
and verify late events from the old page cannot update the new preview.
- **Expected**: The address matches the main document URL and history buttons
reflect its history. Loading settles back to Reload. Subframe, replaced-frame,
and invalidated-session events cannot change the toolbar.
- **Coverage**: `browser-pane-navigation.test.mjs` for published state and event
isolation; native Electron click-through for actual same-document events.
- **Specs linked**: `04-ux/08-component-spec.md` §5.3.

#### E2E-060: Files tab browsing stays inside the workspace

- **Preconditions**: Workspace with file artifacts for nested source, large
Expand Down
4 changes: 4 additions & 0 deletions docs/zh-CN/spec/04-ux/08-component-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,10 @@ vendor 进来的 `pi.file-manager` 视图在插件自己的隔离页面内完成
同一会话内的正常导航仍保留本会话当前显示的内容。切换导航失败或超过既有 15 秒等待
上限时保持隐藏,需重试;迟到的网络加载完成不会自动显示。

- 主框架的同文档导航(锚点链接和 History API 路由)无需完整加载文档,
也必须更新浏览器地址、历史导航按钮及加载状态。子框架、失效会话或
已被替换主框架的事件不得发布浏览器状态。

### 5.3 状态

| 状态 | 行为 |
Expand Down
11 changes: 11 additions & 0 deletions docs/zh-CN/spec/06-delivery/04-e2e-test-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,17 @@ MainChat 弥补了缺口。 Maximized/fullscreen 调用保留最新的
覆盖生产 BrowserHost/BrowserPane 服务路径,控制原生浏览器与 Host 边界,采用确定性
时钟。尚不覆盖原生 Electron 合成显示或报告者的真实会话。

#### E2E-BROWSER-in-page-navigation:浏览器工具栏跟随同文档导航

- **前置条件**:启用 Browser 插件;本地网页包含锚点链接及 History API 路由控件。
- **步骤**:打开网页;点击锚点链接;通过 `history.pushState` 切换路由;
使用后退、前进和刷新。切换至另一会话,检查旧页面的迟到事件不能更新新预览。
- **预期**:地址与主文档 URL 一致,历史按钮状态正确;加载结束后恢复刷新按钮。
子框架、已替换框架及失效会话的事件不能改变工具栏。
- **覆盖**:`browser-pane-navigation.test.mjs` 验证状态发布与事件隔离;
原生 Electron 操作验证真实同文档导航事件。
- **关联规范**:`04-ux/08-component-spec.md` §5.3。

#### E2E-060:文件选项卡浏览停留在工作区中

- **先决条件**:具有嵌套源文件工件的工作区,大型
Expand Down
Loading