Skip to content

Commit

Permalink
Merge pull request greatsuspender#1238 from greatsuspender/screenshot…
Browse files Browse the repository at this point in the history
…_overhaul

Screenshot overhaul
  • Loading branch information
greatsuspender committed Oct 18, 2020
2 parents 47e2cbe + c2ee316 commit 03ea20d
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 62 deletions.
4 changes: 4 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
"html_options_suspend_force_screen_capture_tooltip_line5": { "message": "- Maximum height of screen capture limited to 5000px" },
"html_options_suspend_force_screen_capture_tooltip_line6": { "message": "Enabling high quality screen capture mode may significantly increase CPU load" },
"html_options_suspend_force_screen_capture_tooltip_line7": { "message": "and memory usage." },
"html_options_clean_screen_captures": { "message": "Clean screencaps" },
"html_options_clean_screen_captures_line1": { "message": "Enabling this option will block advertisements and other annoyances during suspension of a tab." },
"html_options_clean_screen_captures_line2": { "message": "This will help with the recongnizability of a screencaptured website and save resources." },
"html_options_clean_screen_captures_line3": { "message": "It will make use of the following host block list: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts." },
"html_options_whitelist_title": { "message": "Never suspend tabs with URLs from the following list:" },
"html_options_whitelist_tooltip_line1": { "message": "Add the URL of each page you want to whitelist on a new line. For example:" },
"html_options_whitelist_tooltip_line2": { "message": "To whitelist multiple sites in one line you can specify part of the url instead:" },
Expand Down
61 changes: 31 additions & 30 deletions src/js/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -1610,37 +1610,37 @@ var tgs = (function() {
request.action
);

if (request.action === 'reportTabState') {
var contentScriptStatus =
request && request.status ? request.status : null;
if (
contentScriptStatus === 'formInput' ||
contentScriptStatus === 'tempWhitelist'
) {
chrome.tabs.update(sender.tab.id, { autoDiscardable: false });
} else if (!sender.tab.autoDiscardable) {
chrome.tabs.update(sender.tab.id, { autoDiscardable: true });
}
// If tab is currently visible then update popup icon
if (sender.tab && isCurrentFocusedTab(sender.tab)) {
calculateTabStatus(sender.tab, contentScriptStatus, function(status) {
setIconStatus(status, sender.tab.id);
});
}
sendResponse();
return false;
}

if (request.action === 'savePreviewData') {
gsTabSuspendManager.handlePreviewImageResponse(
sender.tab,
request.previewUrl,
request.errorMsg
); // async. unhandled promise
sendResponse();
return false;
switch (request.action) {
case 'loadCleanScreencaptureBlocklist':
gsCleanScreencaps.loadList()
case 'reportTabState':
var contentScriptStatus =
request && request.status ? request.status : null;
if (
contentScriptStatus === 'formInput' ||
contentScriptStatus === 'tempWhitelist'
) {
chrome.tabs.update(sender.tab.id, { autoDiscardable: false });
} else if (!sender.tab.autoDiscardable) {
chrome.tabs.update(sender.tab.id, { autoDiscardable: true });
}
// If tab is currently visible then update popup icon
if (sender.tab && isCurrentFocusedTab(sender.tab)) {
calculateTabStatus(sender.tab, contentScriptStatus, function (status) {
setIconStatus(status, sender.tab.id);
});
}
sendResponse();
return false;
case 'savePreviewData':
gsTabSuspendManager.handlePreviewImageResponse(
sender.tab,
request.previewUrl,
request.errorMsg
); // async. unhandled promise
sendResponse();
return false;
}

// Fallback to empty response to ensure callback is made
sendResponse();
return false;
Expand Down Expand Up @@ -1896,6 +1896,7 @@ Promise.resolve()
gsTabCheckManager.initAsPromised(),
gsTabDiscardManager.initAsPromised(),
gsSession.initAsPromised(),
gsCleanScreencaps.initAsPromised()
]);
})
.catch(error => {
Expand Down
91 changes: 91 additions & 0 deletions src/js/gsCleanScreencaps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
var gsCleanScreencaps = {
// this will be filled with domain entries for O(1) lookups during screencaps
blacklist: {},

// listeners for request coming from a tab that is being suspended
listeners: {},

// load blacklist on initialization if option is enabled
initAsPromised: async ()=>
{
const useCleanScreencap = gsStorage.getOption(
gsStorage.ENABLE_CLEAN_SCREENCAPS
);

if (useCleanScreencap) {
await gsCleanScreencaps.loadList()
}

return;
},

addListener: (tabId) => {
// remove a listener if there is already one present. That might not be the case, but the function checks for that case.
gsCleanScreencaps.removeListener(tabId);

const listener = (details) => {
try {
const host = new URL(details.url).host
if (gsCleanScreencaps.blacklist[host]) { return { cancel: true }; }
} catch (err) {
gsUtils.log('background', 'error while trying to block in gsCleanScreencaps', err)
}
}

chrome.webRequest.onBeforeRequest.addListener(
listener,
{ urls: ["<all_urls>"], types: ['image'], tabId: tabId },
["blocking"]
);

// place a callback that will remove the listener as soon as the suspension
// of the tab succeeded or failed
gsCleanScreencaps.listeners[tabId] = () => chrome.webRequest.onBeforeRequest.removeListener(listener)
},

// call the remove listener func and remove it from the hashmap
removeListener: (tabId) => {
let tmp;
if (tmp = gsCleanScreencaps.listeners[tabId]) {
delete gsCleanScreencaps[tabId];
tmp();
}
},

// do nothing but get the data out of the chrome.local.storage
storageData: () => {
return new Promise((res, _) => {
chrome.storage.local.get('gsCleanScreencapsBlacklist', (storage) => res(storage.gsCleanScreencapsBlacklist))
})
},

loadList: async () => {
const stored = await gsCleanScreencaps.storageData();
// take the blocklist out of storage if it's not existent or newer than 30 days
if (!stored || stored.time + (3600 * 24 * 30) <= new Date().getTime()) {
const rex = /^0.0.0.0 (.*)(?:$|#)/
let resp = await fetch('https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts').then(resp => resp.text())
let m;

try {
const blockedHosts = resp
.split(/\n/)
.reduce((res, e) => {
if (m = rex.exec(e)) {
res[m[1]] = true;
}
return res;
}, {});

gsCleanScreencaps.blacklist = blockedHosts;
chrome.storage.local.set({ gsCleanScreencapsBlacklist: { time: new Date().getTime(), blockedHosts } })
return blockedHosts;
} catch (err) {
gsUtils.log('background', 'error while loading blocklist for clean screencapture:', err)
}
} else {
gsCleanScreencaps.blacklist = stored.blockedHosts;
return stored;
}
}
}
12 changes: 7 additions & 5 deletions src/js/gsStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const gsStorageSettings = {

DISCARD_AFTER_SUSPEND: 'discardAfterSuspend',
DISCARD_IN_PLACE_OF_SUSPEND: 'discardInPlaceOfSuspend',
USE_ALT_SCREEN_CAPTURE_LIB: 'useAlternateScreenCaptureLib'
USE_ALT_SCREEN_CAPTURE_LIB: 'useAlternateScreenCaptureLib',
ENABLE_CLEAN_SCREENCAPS: 'cleanScreencaps',
};

var gsStorage = {
Expand Down Expand Up @@ -64,6 +65,7 @@ var gsStorage = {
defaults[gsStorage.NO_NAG] = false;
defaults[gsStorage.WHITELIST] = '';
defaults[gsStorage.THEME] = 'light';
defaults[gsStorage.ENABLE_CLEAN_SCREENCAPS] = false;

return defaults;
},
Expand Down Expand Up @@ -180,14 +182,14 @@ var gsStorage = {
* I did this because I think the key is easier to interpret for someone
* editing the managed storage manually.
*/
checkManagedStorageAndOverride() {
checkManagedStorageAndOverride() {
const settingsList = Object.keys(gsStorageSettings);
chrome.storage.managed.get(settingsList, result => {
const settings = gsStorage.getSettings();

Object.keys(result).forEach(key => {
if (key === "WHITELIST") {
settings[gsStorage[key]] = result[key].replace(/[\s\n]+/g, "\n");
if (key === 'WHITELIST') {
settings[gsStorage[key]] = result[key].replace(/[\s\n]+/g, '\n');
} else {
settings[gsStorage[key]] = result[key];
}
Expand Down Expand Up @@ -457,5 +459,5 @@ var gsStorage = {
*
* @param option The option name, such as "gsWhitelist" (not "WHITELIST")
*/
isOptionManaged: option => managedOptions.includes(option)
isOptionManaged: option => managedOptions.includes(option),
};
35 changes: 25 additions & 10 deletions src/js/gsTabSuspendManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ var gsTabSuspendManager = (function() {
}

async function handlePreviewImageResponse(tab, previewUrl, errorMsg) {
// remove listener if there is any
gsCleanScreencaps.removeListener(tab.id);

const queuedTabDetails = getQueuedTabDetails(tab);
if (!queuedTabDetails) {
gsUtils.log(
Expand Down Expand Up @@ -255,6 +258,7 @@ var gsTabSuspendManager = (function() {
tab,
queuedTabDetails.executionProps.suspendedUrl
);

queuedTabDetails.executionProps.resolveFn(success);
}

Expand All @@ -274,8 +278,7 @@ var gsTabSuspendManager = (function() {
gsUtils.log(
tab.id,
QUEUE_ID,
`Tab took more than ${
_suspensionQueue.getQueueProperties().jobTimeout
`Tab took more than ${_suspensionQueue.getQueueProperties().jobTimeout
}ms to suspend. Will force suspension.`
);
const success = await executeTabSuspension(
Expand Down Expand Up @@ -493,6 +496,9 @@ var gsTabSuspendManager = (function() {
const useAlternateScreenCaptureLib = gsStorage.getOption(
gsStorage.USE_ALT_SCREEN_CAPTURE_LIB
);
const useCleanScreencap = gsStorage.getOption(
gsStorage.ENABLE_CLEAN_SCREENCAPS
);
const screenCaptureLib = useAlternateScreenCaptureLib
? 'js/dom-to-image.js'
: 'js/html2canvas.min.js';
Expand All @@ -501,6 +507,11 @@ var gsTabSuspendManager = (function() {
QUEUE_ID,
`Injecting ${screenCaptureLib} into content script`
);

if (useCleanScreencap) {
gsCleanScreencaps.addListener(tab.id)
}

gsMessages.executeScriptOnTab(tab.id, screenCaptureLib, error => {
if (error) {
handlePreviewImageResponse(tab, null, 'Failed to executeScriptOnTab'); //async. unhandled promise.
Expand Down Expand Up @@ -560,14 +571,16 @@ var gsTabSuspendManager = (function() {
if (useAlternateScreenCaptureLib) {
// console.log('Generating via dom-to-image..');
generateCanvas = () => {
return domtoimage.toCanvas(document.body, {width: width, height: height}).then(canvas => {
const croppedCanvas = document.createElement('canvas');
const context = croppedCanvas.getContext('2d');
croppedCanvas.width = width;
croppedCanvas.height = height;
context.drawImage(canvas, 0, 0);
return croppedCanvas;
});
return domtoimage
.toCanvas(document.body, { width: width, height: height })
.then(canvas => {
const croppedCanvas = document.createElement('canvas');
const context = croppedCanvas.getContext('2d');
croppedCanvas.width = width;
croppedCanvas.height = height;
context.drawImage(canvas, 0, 0);
return croppedCanvas;
});
};
} else {
// console.log('Generating via html2canvas..');
Expand All @@ -578,6 +591,7 @@ var gsTabSuspendManager = (function() {
logging: false,
imageTimeout: 10000,
removeContainer: false,
foreignObjectRendering: true,
async: true,
});
};
Expand Down Expand Up @@ -645,3 +659,4 @@ var gsTabSuspendManager = (function() {
getQueuedTabDetails,
};
})();

47 changes: 33 additions & 14 deletions src/js/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
var elementPrefMap = {
preview: gsStorage.SCREEN_CAPTURE,
forceScreenCapture: gsStorage.SCREEN_CAPTURE_FORCE,
cleanScreenCaptures: gsStorage.ENABLE_CLEAN_SCREENCAPS,
suspendInPlaceOfDiscard: gsStorage.SUSPEND_IN_PLACE_OF_DISCARD,
onlineCheck: gsStorage.IGNORE_WHEN_OFFLINE,
batteryCheck: gsStorage.IGNORE_WHEN_CHARGING,
Expand Down Expand Up @@ -41,7 +42,7 @@

// Used to prevent options set in managed storage from being changed
function blockOption(element) {
element.setAttribute("disabled", "");
element.setAttribute('disabled', '');
}

//populate settings from synced storage
Expand All @@ -63,6 +64,9 @@
setForceScreenCaptureVisibility(
gsStorage.getOption(gsStorage.SCREEN_CAPTURE) !== '0'
);
setCleanScreenCaptureVisibility(
gsStorage.getOption(gsStorage.SCREEN_CAPTURE) !== '0'
);
setAutoSuspendOptionsVisibility(
parseFloat(gsStorage.getOption(gsStorage.SUSPEND_TIME)) > 0
);
Expand Down Expand Up @@ -117,6 +121,14 @@
}
}

function setCleanScreenCaptureVisibility(visible) {
if (visible) {
document.getElementById('cleanScreenCapturesContainer').style.display = 'block';
} else {
document.getElementById('cleanScreenCapturesContainer').style.display = 'none';
}
}

function setSyncNoteVisibility(visible) {
if (visible) {
document.getElementById('syncNote').style.display = 'block';
Expand All @@ -140,25 +152,32 @@

function handleChange(element) {
return function() {
var pref = elementPrefMap[element.id],
let prefKey = elementPrefMap[element.id],
interval;

//add specific screen element listeners
if (pref === gsStorage.SCREEN_CAPTURE) {
setForceScreenCaptureVisibility(getOptionValue(element) !== '0');
} else if (pref === gsStorage.SUSPEND_TIME) {
interval = getOptionValue(element);
setAutoSuspendOptionsVisibility(interval > 0);
} else if (pref === gsStorage.SYNC_SETTINGS) {
// we only really want to show this on load. not on toggle
if (getOptionValue(element)) {
setSyncNoteVisibility(false);
}
switch (prefKey) {
case gsStorage.SCREEN_CAPTURE:
setForceScreenCaptureVisibility(getOptionValue(element) !== '0');
setCleanScreenCaptureVisibility(getOptionValue(element) !== '0');
break;
case gsStorage.SUSPEND_TIME:
interval = getOptionValue(element);
setAutoSuspendOptionsVisibility(interval > 0);
break;
case gsStorage.SYNC_SETTINGS:
if (getOptionValue(element)) {
setSyncNoteVisibility(false);
}
break;
case gsStorage.ENABLE_CLEAN_SCREENCAPS:
if (getOptionValue(element)) {
chrome.runtime.sendMessage({ action: 'loadCleanScreencaptureBlocklist' })
}
break;
}

var [oldValue, newValue] = saveChange(element);
if (oldValue !== newValue) {
var prefKey = elementPrefMap[element.id];
gsUtils.performPostSaveUpdates(
[prefKey],
{ [prefKey]: oldValue },
Expand Down
Loading

0 comments on commit 03ea20d

Please sign in to comment.