fix: Tokyo UX stripped when mock is active

Two bugs caused the Tokyo UX to be stripped/broken when mock mode was ON:

1. **Double callback firing**: the interceptor's `setTimeout` called both
   `onreadystatechange.call(self)` AND `dispatchEvent(new Event('readystatechange'))`.
   Since `dispatchEvent` already fires `onXxx` property handlers via the DOM event
   model, the page's callback fired twice, causing a second DOM re-render that could
   overwrite Tokyo UX modifications. Fixed by relying on `dispatchEvent` only (with
   `ProgressEvent` for load events), with a direct-call fallback only for environments
   that lack `dispatchEvent`.

2. **No double-injection guard**: if `injectedJavaScriptBeforeContentLoaded` caused
   the interceptor to run more than once (e.g., `onPageStarted` fires multiple times
   on Android), `_origOpen` would capture the already-patched version, leading to
   unexpected behaviour. Fixed by adding `if (window.__jrsMockActive) return;` at the
   top of the interceptor IIFE.

3. **Belt-and-suspenders injection**: the interceptor is now also prepended to
   `injectedJavaScript` (Tokyo UX script) via the restored `mockApiConfig` parameter
   on `injectJavascriptData`. The `__jrsMockActive` guard ensures it's a no-op when
   `injectedJavaScriptBeforeContentLoaded` already ran it, but guarantees the
   interceptor is active on platforms where IJBCL is unreliable — before
   `MoveDisplayStation` triggers the first train-data poll via `onLoadEnd`.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
harukin-expo-dev-env
2026-05-01 11:40:11 +00:00
co-authored by Copilot
parent 8ee546277c
commit c6aecd151c
3 changed files with 65 additions and 19 deletions
+26 -4
View File
@@ -27,8 +27,15 @@ export interface InjectJavascriptOptions {
* 未指定または null の場合、インターセプターは無効で通常通り公式APIに接続します。
*/
mockApiConfig?: MockApiConfig | null;
/**
* バックエンドAPIのベースURL。省略時は本番URL。
* experimental 環境では https://jr-shikoku-backend-api-v1-beta.haruk.in を渡す。
*/
backendApiBaseUrl?: string;
}
const PRODUCTION_BACKEND_BASE = 'https://jr-shikoku-backend-api-v1.haruk.in';
export const injectJavascriptData = ({
mapSwitch,
iconSetting,
@@ -38,7 +45,9 @@ export const injectJavascriptData = ({
useUnyohub,
useElesite,
isDark,
}: Omit<InjectJavascriptOptions, 'mockApiConfig'>): string => {
backendApiBaseUrl,
mockApiConfig,
}: InjectJavascriptOptions): string => {
// 一番上のメニュー非表示 地図スイッチによって切り替え
const topMenu =
@@ -1399,7 +1408,7 @@ setStationMenuDialog.observe(document.querySelector('#disp'), {
});
`
: ``;
return (
let result =
bootData +
topMenu +
trainIcon +
@@ -1407,8 +1416,21 @@ setStationMenuDialog.observe(document.querySelector('#disp'), {
makeTrainView +
makeTrainMenu +
textInsert +
makeStationMenu
);
makeStationMenu;
if (backendApiBaseUrl && backendApiBaseUrl !== PRODUCTION_BACKEND_BASE) {
result = result.split(PRODUCTION_BACKEND_BASE).join(backendApiBaseUrl);
}
// Prepend XHR interceptor as a fallback for platforms where
// injectedJavaScriptBeforeContentLoaded doesn't run reliably.
// The __jrsMockActive guard in the interceptor prevents double-patching
// when IJBCL already installed it.
if (mockApiConfig) {
result = generateXhrInterceptorJs(mockApiConfig) + '\n' + result;
}
return result;
};
/**