//----------- // FUNCTIONS //----------- function getInactiveTabs(callback) { chrome.storage.local.get(['sleepyTabsInactiveTabs'], (result) => { callback(result.sleepyTabsInactiveTabs || {}); }); } function getOptions(callback) { chrome.storage.local.get(['sleepyTabsOptions'], (result) => { callback(result.sleepyTabsOptions || { suspendAfterMins: 30, suspendIncognito: false, suspendAudible: false, whitelistPages: [], whitelistDomains: [], }); }); } function inWhitelist(url, options) { let inWhiteList = false; options.whitelistPages.every((whitelistUrl, index) => { if(url.replace(/\?.*$/, '') == whitelistUrl) { inWhiteList = true; return false; } return true; }); if(!inWhiteList) { options.whitelistDomains.every((whitelistUrl, index) => { if(url.match(whitelistUrl)) { inWhiteList = true; return false; } return true; }); } return inWhiteList; } function setInactiveTabs(inactiveTabs, callback) { chrome.storage.local.set({ sleepyTabsInactiveTabs: inactiveTabs }, callback); } function setOptions(options, callback) { chrome.storage.local.set({ sleepyTabsOptions: options }, callback); } function suspendTab(_tab) { getInactiveTabs((inactiveTabs) => { let _params = new URLSearchParams(); _params.append('url', _tab.url); _params.append('title', _tab.title); _params.append('favIconUrl', _tab.favIconUrl); chrome.tabs.update(_tab.id, { url: 'suspended.html?' + _params }); delete inactiveTabs[_tab.id]; setInactiveTabs(inactiveTabs); }); } function unSuspendTab(_tab, _updateInactiveTabList = true) { getInactiveTabs((inactiveTabs) => { if(_tab.url.match('^chrome-extension://' + chrome.runtime.id + '/suspended.html')) { // check if currently suspended chrome.tabs.sendMessage(_tab.id, {type: 'UNSUSPEND'}, () => { if(_updateInactiveTabList) { inactiveTabs[_tab.id] = Date.now(); setInactiveTabs(inactiveTabs); } }); } }); } function startAlarm() { stopAlarm(); // just to make sure it's not already running setInactiveTabs({}); // re-start timer for inactive tabs chrome.alarms.create('sleepyTabsAlarm', { when: Date.now(), periodInMinutes: 1 }); } function stopAlarm() { chrome.alarms.clear('sleepyTabsAlarm'); } //----------- // LISTENERS //----------- // check to suspend tabs (once per minute) chrome.alarms.onAlarm.addListener((alarm) => { if(alarm.name == 'sleepyTabsAlarm') { const _currentTime = Date.now(); getOptions((options) => { if(options.suspendAfterMins > 0) { getInactiveTabs((inactiveTabs) => { chrome.windows.getAll({populate: true}, (_windows) => { _windows.forEach((_window, _windowIndex) => { _window.tabs.forEach((_tab, _tabIndex) => { if(!_tab.active && !_tab.url.match(/^chrome/) && !inWhitelist(_tab.url, options) && !(options.suspendIncognito && _tab.incognito) && !(options.suspendAudible && _tab.audible)) { if(!inactiveTabs[_tab.id]) { inactiveTabs[_tab.id] = _currentTime; setInactiveTabs(inactiveTabs); } else if(_currentTime - inactiveTabs[_tab.id] >= options.suspendAfterMins * 60000) { suspendTab(_tab); } } }); }); }); }); } }); } }); // remove tab from inactive list when activated chrome.tabs.onActivated.addListener((activeInfo) => { getInactiveTabs((inactiveTabs) => { delete inactiveTabs[activeInfo.tabId]; setInactiveTabs(inactiveTabs); }); }); // remove tab from inactive list when removed chrome.tabs.onRemoved.addListener((tabId, removeInfo) => { getInactiveTabs((inactiveTabs) => { delete inactiveTabs[tabId]; setInactiveTabs(inactiveTabs); }); }); // listen for messages chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { getOptions((options) => { switch(request.type) { case 'GET_OPTIONS': sendResponse(options); break; case 'SET_OPTIONS': setOptions(request.options); if(options.suspendAfterMins > 0 && request.options.suspendAfterMins == 0) { stopAlarm(); } else if(options.suspendAfterMins == 0 && request.options.suspendAfterMins > 0) { startAlarm(); } sendResponse(); break; case 'SUSPEND_TAB': suspendTab(request.tab); sendResponse(); break; case 'UNSUSPEND_TAB': unSuspendTab(request.tab); sendResponse(); break; case 'SUSPEND_OTHER_TABS': chrome.tabs.query({windowId: request.tab.windowId}, (_tabs) => { _tabs.forEach((_tab, _tabIndex) => { if(_tab.id != request.tab.id && !_tab.url.match(/^chrome/)) { suspendTab(_tab); sendResponse(); } }); }); break; case 'UNSUSPEND_ALL_TABS': setInactiveTabs({}); chrome.tabs.query({windowId: request.tab.windowId}, (_tabs) => { _tabs.forEach((_tab, _tabIndex) => { unSuspendTab(_tab, false); sendResponse(); }); }); break; case 'NEVER_SUSPEND_PAGE': options.whitelistPages.push(request.tab.url.replace(/(^.*)\?.*$/, '$1')); setOptions(options); sendResponse(); break; case 'REMOVE_NEVER_SUSPEND_PAGE': options.whitelistPages.splice(options.whitelistPages.indexOf(request.tab.url.replace(/(^.*)\?.*$/, '$1'))); setOptions(options); sendResponse(); break; case 'NEVER_SUSPEND_DOMAIN': options.whitelistDomains.push(request.tab.url.replace(/(^https?:\/\/[^/]+).*$/, '$1')); setOptions(options); sendResponse(); break; case 'REMOVE_NEVER_SUSPEND_DOMAIN': options.whitelistDomains.splice(options.whitelistDomains.indexOf(request.tab.url.replace(/(^https?:\/\/[^/]+).*$/, '$1'))); setOptions(options); sendResponse(); break; } }); return true; }); //------ // INIT //------ chrome.runtime.onInstalled.addListener(() => { getOptions((options) => { if(options.suspendAfterMins > 0) { startAlarm(); } }); });