Files

183 lines
5.1 KiB
JavaScript

(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);