(function (root) { 'use strict'; var S = root.AutoMirrorShared; var STORAGE_KEY = 'runtimeState'; var loaded = false; var loading = false; var callbacks = []; var memory = { lastMainFrameUrls: {}, tabStates: {} }; function getStorageArea() { if (chrome.storage && chrome.storage.session) return chrome.storage.session; return chrome.storage.local; } function tabKey(tabId) { return String(tabId); } function plainMap(value) { var result = {}; if (!value || typeof value !== 'object') return result; Object.keys(value).forEach(function (key) { if (value[key]) result[key] = value[key]; }); return result; } function normalizeTabState(state) { state = state && typeof state === 'object' ? state : {}; return { ruleId: state.ruleId || '', path: state.path || '', startedAt: Number(state.startedAt || 0), triedHosts: plainMap(state.triedHosts), redirects: Number(state.redirects || 0), lastRedirectFrom: state.lastRedirectFrom || '', lastRedirectTo: state.lastRedirectTo || '', lastRedirectAt: Number(state.lastRedirectAt || 0), lastTriggerUrl: state.lastTriggerUrl || '', lastTriggerAt: Number(state.lastTriggerAt || 0) }; } function normalizeRuntimeState(raw) { raw = raw && typeof raw === 'object' ? raw : {}; var tabStates = {}; Object.keys(raw.tabStates || {}).forEach(function (key) { tabStates[key] = normalizeTabState(raw.tabStates[key]); }); return { lastMainFrameUrls: plainMap(raw.lastMainFrameUrls), tabStates: tabStates }; } function flushCallbacks() { var pending = callbacks.slice(); callbacks = []; pending.forEach(function (callback) { callback(); }); } function load(callback) { if (loaded) { callback(); return; } callbacks.push(callback); if (loading) return; loading = true; try { var defaults = {}; defaults[STORAGE_KEY] = memory; getStorageArea().get(defaults, function (items) { memory = normalizeRuntimeState(items && items[STORAGE_KEY]); loaded = true; loading = false; flushCallbacks(); }); } catch (e) { memory = normalizeRuntimeState(memory); loaded = true; loading = false; flushCallbacks(); } } function save() { if (!loaded) return; try { var payload = {}; payload[STORAGE_KEY] = normalizeRuntimeState(memory); getStorageArea().set(payload); } catch (e) { // Runtime state is a convenience layer; redirect events can rebuild it. } } function getLastMainFrameUrl(tabId, callback) { load(function () { callback(memory.lastMainFrameUrls[tabKey(tabId)] || ''); }); } function setLastMainFrameUrl(tabId, url) { load(function () { memory.lastMainFrameUrls[tabKey(tabId)] = String(url || ''); save(); }); } function getTabState(tabId, rule, sourceUrlObj, callback) { load(function () { var key = tabKey(tabId); var path = S.getPath(sourceUrlObj); var now = Date.now(); var oldState = normalizeTabState(memory.tabStates[key]); if ( !oldState.startedAt || oldState.ruleId !== rule.id || oldState.path !== path || now - oldState.startedAt > 5 * 60 * 1000 ) { oldState = { ruleId: rule.id, path: path, startedAt: now, triedHosts: {}, redirects: 0, lastRedirectFrom: '', lastRedirectTo: '', lastRedirectAt: 0, lastTriggerUrl: '', lastTriggerAt: 0 }; memory.tabStates[key] = oldState; save(); } else { memory.tabStates[key] = oldState; } callback(oldState); }); } function saveTabState(tabId, state) { load(function () { memory.tabStates[tabKey(tabId)] = normalizeTabState(state); save(); }); } function clearTab(tabId) { load(function () { delete memory.lastMainFrameUrls[tabKey(tabId)]; delete memory.tabStates[tabKey(tabId)]; save(); }); } root.AutoMirrorRuntimeState = { getLastMainFrameUrl: getLastMainFrameUrl, setLastMainFrameUrl: setLastMainFrameUrl, getTabState: getTabState, saveTabState: saveTabState, clearTab: clearTab }; })(typeof globalThis !== 'undefined' ? globalThis : this);