-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
150 lines (127 loc) · 4.49 KB
/
Copy pathbackground.js
File metadata and controls
150 lines (127 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
let sessionState = {
activeDomain: null,
lastTimestamp: Date.now(),
sessionStartTime: Date.now(),
totals: {},
isIdle: false,
windowFocused: true,
isActiveTabAudible: false
};
const IDLE_DETECTION_INTERVAL = 60; // 60 seconds
let isInitialized = false;
let initPromise = loadState();
async function loadState() {
const data = await chrome.storage.session.get('sessionState');
if (data.sessionState) {
sessionState = data.sessionState;
if (!sessionState.sessionStartTime) sessionState.sessionStartTime = Date.now();
await _processStateChange();
} else {
chrome.idle.setDetectionInterval(IDLE_DETECTION_INTERVAL);
sessionState.lastTimestamp = Date.now();
await _processStateChange();
}
isInitialized = true;
}
async function saveState() {
await chrome.storage.session.set({ sessionState });
}
function getDomain(url) {
if (!url || (!url.startsWith('http:') && !url.startsWith('https:'))) return null;
try {
const urlObj = new URL(url);
let hostname = urlObj.hostname.toLowerCase();
// Strip common subdomains for cleaner analytics
return hostname.replace(/^(www\.|m\.|en\.)/, '');
} catch (e) {
return null;
}
}
async function processStateChange(updates = {}) {
if (!isInitialized) await initPromise;
return _processStateChange(updates);
}
async function _processStateChange(updates = {}) {
const now = Date.now();
// 1. Calculate and aggregate elapsed time using PREVIOUS state
const wasEffectivelyActive = sessionState.activeDomain &&
(sessionState.windowFocused || isPopupOpen) &&
(!sessionState.isIdle || sessionState.isActiveTabAudible);
if (wasEffectivelyActive) {
const elapsedSeconds = Math.floor((now - sessionState.lastTimestamp) / 1000);
// Safety limit: if elapsed time is unrealistically large (> 24 hours), ignore it
if (elapsedSeconds > 0 && elapsedSeconds < 86400) {
if (!sessionState.totals[sessionState.activeDomain]) {
sessionState.totals[sessionState.activeDomain] = 0;
}
sessionState.totals[sessionState.activeDomain] += elapsedSeconds;
}
}
// 2. Apply state updates from the event BEFORE determining the new domain
if (updates.windowFocused !== undefined) {
sessionState.windowFocused = updates.windowFocused;
}
if (updates.isIdle !== undefined) {
sessionState.isIdle = updates.isIdle;
}
// 3. Determine new active domain and audible state safely
let newDomain = sessionState.activeDomain; // Prevent false wipes if query fails
let newAudible = sessionState.isActiveTabAudible;
try {
// Get the last focused normal window (ignores extension popups)
const window = await chrome.windows.getLastFocused({ populate: true, windowTypes: ['normal'] });
if (window && window.tabs) {
const activeTab = window.tabs.find(t => t.active);
if (activeTab) {
if (!activeTab.incognito) {
newAudible = activeTab.audible || false;
newDomain = getDomain(activeTab.url);
} else {
newDomain = null;
}
}
}
} catch (e) {
console.error("Error querying windows:", e);
}
// 4. Update state
sessionState.activeDomain = newDomain;
sessionState.isActiveTabAudible = newAudible;
sessionState.lastTimestamp = now;
await saveState();
}
// Listeners
chrome.tabs.onActivated.addListener(() => processStateChange());
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (tab.active && (changeInfo.url !== undefined || changeInfo.audible !== undefined)) {
processStateChange();
}
});
let isPopupOpen = false;
chrome.runtime.onConnect.addListener((port) => {
if (port.name === 'popup') {
isPopupOpen = true;
processStateChange();
port.onDisconnect.addListener(() => {
isPopupOpen = false;
processStateChange();
});
}
});
chrome.windows.onFocusChanged.addListener((windowId) => {
const isFocused = (windowId !== chrome.windows.WINDOW_ID_NONE);
processStateChange({ windowFocused: isFocused });
});
chrome.idle.onStateChanged.addListener((newState) => {
const isIdle = (newState !== 'active');
processStateChange({ isIdle: isIdle });
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'reset_session') {
sessionState.totals = {};
sessionState.lastTimestamp = Date.now();
sessionState.sessionStartTime = Date.now();
saveState().then(() => sendResponse({success: true}));
return true; // indicates async response
}
});