M5: BridgeController 激活闸门 setActive(双 Web 基础件)

- BridgeController 加 active 状态 + setActive/isActive:
  - 非激活:onLoadIntercept 拦截 yy:// 但不分发(入站忽略)、dispatch 直接返回(出站抑制)
  - 保证双 Web 下"同时只有一个 Web 执行桥接口",且去激活端不会收到 native 推送
- 去激活顺序约定:先 callHandler(appservice,'2')(彼时仍激活),再 setActive(false)
- 单测新增 inactive_gate_suppresses_outbound_and_inbound;scripts/test.sh 通过
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-25 23:25:49 +08:00
parent f190fe69e3
commit a865ac766b
2 changed files with 40 additions and 0 deletions
+20
View File
@@ -188,6 +188,26 @@ export default function bridgeTest() {
expect(bridge.getRegistry().has('a')).assertFalse(); expect(bridge.getRegistry().has('a')).assertFalse();
}); });
it('inactive_gate_suppresses_outbound_and_inbound', 0, () => {
const fake = new FakeWebController();
const bridge = new BridgeController(fake);
bridge.onPageEnd();
let got = '';
bridge.registerHandler('echo', (data, cb) => { got = data; cb('R'); });
// 去激活:出站抑制
bridge.setActive(false);
bridge.callHandler('appservice', '2');
expect(fake.scripts.length).assertEqual(0);
// 去激活:入站忽略——yy:// 仍拦截(返回 true)但不 flush
const intercepted = bridge.onLoadIntercept('yy://__queue__/');
expect(intercepted).assertTrue();
expect(fake.scripts.length).assertEqual(0);
// 重新激活:恢复出站
bridge.setActive(true);
bridge.callHandler('appservice', '1');
expect(fake.scripts.some((s) => s.includes('appservice'))).assertTrue();
});
it('default_handler_unregistered_no_throw', 0, () => { it('default_handler_unregistered_no_throw', 0, () => {
const fake = new FakeWebController(); const fake = new FakeWebController();
const bridge = new BridgeController(fake); const bridge = new BridgeController(fake);
@@ -26,6 +26,8 @@ export class BridgeController {
private startupMessages: Message[] | null = []; private startupMessages: Message[] | null = [];
private uniqueId: number = 0; private uniqueId: number = 0;
private bridgeJs: string = ''; private bridgeJs: string = '';
/** 激活闸门(M5 双 Web):非激活时入站不分发、出站不下发,保证同时只有一个 Web 在执行桥接口。 */
private active: boolean = true;
constructor(web: IWebController, registry?: HandlerRegistry) { constructor(web: IWebController, registry?: HandlerRegistry) {
this.web = web; this.web = web;
@@ -41,6 +43,16 @@ export class BridgeController {
this.bridgeJs = js; this.bridgeJs = js;
} }
/** 设激活态(M5 双 Web 激活协议)。非激活:入站忽略、出站抑制。
* 注意:去激活前应先下发 appservice('2')(彼时仍激活),再 setActive(false)。 */
setActive(active: boolean): void {
this.active = active;
}
isActive(): boolean {
return this.active;
}
/** 释放:清空回执表/注册表/启动队列,断开与已注册能力闭包的引用,防泄漏。 /** 释放:清空回执表/注册表/启动队列,断开与已注册能力闭包的引用,防泄漏。
* 容器 aboutToDisappear 调用(注意:IWebController 由容器另行 dispose)。 */ * 容器 aboutToDisappear 调用(注意:IWebController 由容器另行 dispose)。 */
dispose(): void { dispose(): void {
@@ -57,6 +69,10 @@ export class BridgeController {
} catch (e) { } catch (e) {
url = rawUrl; url = rawUrl;
} }
// 非激活:拦截 yy:// 但不处理(非激活 Web 的桥接口不执行;H5 已由 appservice('2') 进入后台态)
if (!this.active) {
return url.startsWith(YY_SCHEMA);
}
if (url.startsWith(YY_RETURN)) { if (url.startsWith(YY_RETURN)) {
this.handleReturnData(url); this.handleReturnData(url);
return true; return true;
@@ -172,6 +188,10 @@ export class BridgeController {
} }
private dispatch(m: Message): void { private dispatch(m: Message): void {
// 非激活:抑制出站(native→H5 不下发到非激活 Web)。激活时机由 WebSlotManager 控制顺序。
if (!this.active) {
return;
}
// lzyzsd 二次转义(复刻 \ 与 ");再补转义单引号——lzyzsd 原版遗漏,而出站脚本用 // lzyzsd 二次转义(复刻 \ 与 ");再补转义单引号——lzyzsd 原版遗漏,而出站脚本用
// 单引号包裹 _handleMessageFromNative('...')data 含 ' 会提前闭合字符串。补转义后 // 单引号包裹 _handleMessageFromNative('...')data 含 ' 会提前闭合字符串。补转义后
// H5 JSON.parse 得到的仍是原始 data,对 H5 完全透明(边界行为不变)。 // H5 JSON.parse 得到的仍是原始 data,对 H5 完全透明(边界行为不变)。