diff --git a/_locales/en_US/messages.json b/_locales/en_US/messages.json index 6f27b6a..e67f620 100644 --- a/_locales/en_US/messages.json +++ b/_locales/en_US/messages.json @@ -355,6 +355,14 @@ "description": "text in Cookies 'delete all' dialog", "message": "You are about to delete all cookies, are you sure ?" }, + "whitelistCookieDomain": { + "description": "text when hovering over Whitelist button for Cookie Domain", + "message": "This button adds all cookies from a domain to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" + }, + "whitelistSublistCookie": { + "description": "text when hovering over Whitelist button for Sublist Cookies", + "message": "This button adds a specific cookie to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" + }, "additionalPermissions": { "description": "Label for requesting additional permission setting", "message": "Additional permissions" diff --git a/css/img/check-mark-circle.svg b/css/img/check-mark-circle.svg new file mode 100644 index 0000000..850f5fd --- /dev/null +++ b/css/img/check-mark-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/css/img/check-mark-filled.svg b/css/img/check-mark-filled.svg new file mode 100644 index 0000000..2ff5a78 --- /dev/null +++ b/css/img/check-mark-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/css/main.css b/css/main.css index 14bbab8..cb3e008 100644 --- a/css/main.css +++ b/css/main.css @@ -288,6 +288,10 @@ button[role="checkbox"][aria-checked="true"] > span:last-child #dialog[data-dialog="cookie-edit"] #dialog-content-cookie-form, #dialog[data-dialog="cookies-delete-all"] #dialog-content-cookie-delete-all, #dialog[data-dialog="setting-info"] #dialog-content-setting-info +{ + display: block; +}#dialog[data-dialog="cookies-delete-all"] #dialog-content-cookie-delete-all, +#dialog[data-dialog="setting-info"] #dialog-content-setting-info { display: block; } @@ -527,6 +531,34 @@ button.icon.edit:after background-image: url(/css/img/edit.svg); } +.tableList > li > div .icon.whitelist::after +{ + background-image: url(/css/img/check-mark-circle.svg); + background-size: 100%; + background-repeat: no-repeat; +} + +.tableList li > ul li > div .icon.whitelist::after +{ + background-image: url(/css/img/check-mark-circle.svg); + background-size: 100%; + background-repeat: no-repeat; +} + +.tableList > li[data-whitelist="true"] > div .icon.whitelist::after +{ + background-image: url(/css/img/check-mark-filled.svg); + background-size: 100%; + background-repeat: no-repeat; +} + +.tableList li > ul li[data-whitelist="true"] > div .icon.whitelist::after +{ + background-image: url(/css/img/check-mark-filled.svg); + background-size: 100%; + background-repeat: no-repeat; +} + button.icon.edit:hover:after { background-image: url(/css/img/edit-hover.svg); diff --git a/js/back.js b/js/back.js index ce54e7b..4d5b007 100644 --- a/js/back.js +++ b/js/back.js @@ -23,16 +23,22 @@ global.collectedRequests = []; const requestCollectionLength = 500; - function profileStart() - { - getStorage("settingList", function(data) + function profileStart() { + getStorage("cookieWhitelist", function (data) + { + if (!data || !data.cookieWhitelist) + { + setStorage({"cookieWhitelist": {} }) + } + }) + getStorage("settingList", function (data) { deleteBrowsingData(data.settingList); }); } //TODO: Find a solution to avoide duplication - getStorage("settingList", function(data) + getStorage("settingList", function (data) { if (data.settingList && data.settingList.collectHeaders) startCollectingRequests(); @@ -41,80 +47,88 @@ addBlockAgentListener(); }); - function deleteBrowsingData(data) + function deleteBrowsingData(data) { if (!data) return; // Filter "data" object to only match properties from "browsingData". - var browsingDataObj = Object.keys(data).filter(function(key) + var browsingDataObj = Object.keys(data).filter(function (key) { return browsingData.includes(key); - }).reduce(function(accumulator, dataType) + }).reduce(function (accumulator, dataType) { accumulator[dataType] = data[dataType]; return accumulator; }, {}); - if (browsingDataObj.removeAll == true) + if (browsingDataObj.removeAll == true) { - var browsingDataObj = browsingData.reduce(function(accumulator, dataType) + var browsingDataObj = browsingData.reduce(function (accumulator, dataType) { if (dataType != "removeAll") accumulator[dataType] = true; return accumulator; }, {}); + + if (browsingDataObj.cookies) + { + deleteCookies() + } + browsingDataObj.cookies = false; chrome.browsingData.remove({}, browsingDataObj); } - else - { - delete browsingDataObj.removeAll; + else { + if (browsingDataObj.cookies) + { + deleteCookies() + } + browsingDataObj.cookies = false; chrome.browsingData.remove({}, browsingDataObj); } } - global.startCollectingRequests = function() + global.startCollectingRequests = function () { addRequestListener(onSendHeaders, onHeadersReceived); }; - global.stopCollectingRequests = function() + global.stopCollectingRequests = function () { removeRequestListener(onSendHeaders, onHeadersReceived); }; - function onSendHeaders(details) + function onSendHeaders(details) { updateRequestObj(details, "send"); addToRequestArray(details); } - function onHeadersReceived(details) + function onHeadersReceived(details) { updateRequestObj(details, "receive"); addToRequestArray(details); } - function addToRequestArray(details) + function addToRequestArray(details) { - if(collectedRequests.length > requestCollectionLength) + if (collectedRequests.length > requestCollectionLength) collectedRequests.shift(); collectedRequests.push(details); } - chrome.storage.onChanged.addListener(function(change) + chrome.storage.onChanged.addListener(function (change) { - if (change.settingList) - { - chrome.permissions.contains(additionalPermission, function(result) + if (change.settingList) { + chrome.permissions.contains(additionalPermission, function (result) { var newValue = change.settingList.newValue.collectHeaders; var oldValue = change.settingList.oldValue; - if (oldValue && newValue != oldValue.collectHeaders) + if (oldValue && newValue != oldValue.collectHeaders) { - if(result && newValue) + if (result && newValue) startCollectingRequests(); else stopCollectingRequests(); @@ -122,9 +136,9 @@ var newValue = change.settingList.newValue.blockUserAgent; var oldValue = change.settingList.oldValue; - if (oldValue && newValue != oldValue.blockUserAgent) + if (oldValue && newValue != oldValue.blockUserAgent) { - if(result && newValue) + if (result && newValue) addBlockAgentListener(); else removeBlockAgentListener(); @@ -133,12 +147,13 @@ } }); - chrome.permissions.onRemoved.addListener(function(result) - { + chrome.permissions.onRemoved.addListener(function (result) + { removeBlockAgentListener(); removeRequestListener(onSendHeaders, onHeadersReceived); }); // Fired on a profile start up + chrome.runtime.onInstalled.addListener(profileStart); chrome.runtime.onStartup.addListener(profileStart); })(this); diff --git a/js/common.js b/js/common.js index 35006cf..138aaea 100644 --- a/js/common.js +++ b/js/common.js @@ -26,6 +26,8 @@ const browsingData = ["removeAll", "appcache", "cache", "cookies", "downloads", "fileSystems", "formData", "history", "indexedDB", "localStorage", "serverBoundCertificates", "passwords", "pluginData", "serviceWorkers", "webSQL"]; +const getAllCookies = chrome.cookies.getAll; +const removeCookie = chrome.cookies.remove; function getStorage(keys, callback) { @@ -38,6 +40,10 @@ function setStorage(items, callback) chrome.storage.local.set(items, callback); } +function getUrl(domain, path, isSecure) +{ + return "http" + (isSecure ? "s" : "") + "://" + domain + path; +} function addRequestListener(onSendHeadersCallback, onHeadersReceivedCallback) { chrome.webRequest.onSendHeaders.addListener(onSendHeadersCallback, @@ -84,3 +90,31 @@ function blockUserAgent(details) } return {requestHeaders: details.requestHeaders}; } + +function removeStartDot(string) +{ + return string.replace(/^\./, ""); +} + +function deleteCookies() +{ + // delete cookies here + ignore whitelisted cookies + getStorage("cookieWhitelist", function (data) + { + let domainList = data.cookieWhitelist + getAllCookies({}, function (cookies) + { + let callbackCount = 0; + for (let cookie of cookies) + { + let url = getUrl(cookie.domain, cookie.path, cookie.secure); + // replace leading dots sometimes present in cookie domains + let domainWhitelist = domainList[removeStartDot(cookie.domain)] + if (!domainWhitelist || (!domainWhitelist.cookies.includes(cookie.name) && !domainWhitelist.domainWhitelist)) + { + removeCookie({ "url": url, "name": cookie.name }); + } + } + }); + }); +} diff --git a/js/ui/common.js b/js/ui/common.js index feaa478..529b58f 100644 --- a/js/ui/common.js +++ b/js/ui/common.js @@ -709,6 +709,13 @@ TableList.prototype._updateListElem = function(itemObj, listElem) if (textElement) textElement.textContent = textsObj[name]; } + var titleObjs = itemObj.titles; + for (var title in titleObjs) + { + var titleElement = listElem.querySelector("[data-text='"+ title +"']"); + if (titleElement) + titleElement.title = titleObjs[title]; + } // Set default tabindex to the first list Element if (this.listElem.childElementCount == 0) diff --git a/js/ui/tab_cookies.js b/js/ui/tab_cookies.js index 7fd6bfb..3c32eb2 100644 --- a/js/ui/tab_cookies.js +++ b/js/ui/tab_cookies.js @@ -25,6 +25,9 @@ const getCookie = chrome.cookies.get; const setCookie = chrome.cookies.set; const onCookieChange = chrome.cookies.onChanged; + const onStorageChange = chrome.storage.onChanged; + const cookieWhitelistButtonTitle = getMsg("whitelistSublistCookie") + const domainWhitelistButtonTitle = getMsg("whitelistCookieDomain") const activeTabCookieId = "activeTabCookies"; @@ -128,6 +131,11 @@ } domainObjs.push(createDomainObj(lastDomain, cookiesNumber)); tableList.addItems(domainObjs); + // update whitelist + getStorage("cookieWhitelist", function(cookieWhitelist) + { + updateWhitelistInList(cookieWhitelist["cookieWhitelist"]); + }); }); } @@ -154,6 +162,55 @@ tableList.addSubItem(createCookieSubitemObj(cookie), domain); } + // update whitelist + getStorage("cookieWhitelist", function(cookieWhitelist) + { + let domainCookies = {} + domainCookies[domain] = cookieWhitelist.cookieWhitelist[domain] + if (domainCookies[domain]) + updateWhitelistInList(domainCookies); + }); + }); + break; + case "whitelist-cookie-domain": + var domain = getParentData(element, "data-access"); + + getStorage("cookieWhitelist", function(cookieWhitelist) + { + let whitelist = cookieWhitelist["cookieWhitelist"] + if (!(domain in whitelist)) + { + whitelist[domain] = {domainWhitelist: true, cookies: []} + } + else + { + whitelist[domain].domainWhitelist = !whitelist[domain].domainWhitelist; + } + setStorage(cookieWhitelist) + }); + break; + case "whitelist-sublist-cookie": + var accessObj = JSON.parse(getParentData(element, "data-access")); + var domain = getParentData(getParentData(element, "data-access", true). + parentElement, "data-access"); + getStorage("cookieWhitelist", function(cookieWhitelist) + { + let whitelist = cookieWhitelist["cookieWhitelist"] + if (!(domain in whitelist)) + { + whitelist[domain] = {domainWhitelist: false, cookies: [accessObj.cookie]} + } + else if (whitelist[domain].cookies.includes(accessObj.cookie)) + { + // filter out cookie name + whitelist[domain].cookies = whitelist[domain].cookies.filter(el => el !== accessObj.cookie) + } + else + { + // add cookie name + whitelist[domain].cookies.push(accessObj.cookie) + } + setStorage(cookieWhitelist) }); break; case "close-expanded-domain": @@ -266,10 +323,7 @@ }); break; case "delete-all-cookies": - chrome.browsingData.removeCookies({}, function() - { - populateDomainList(); - }); + deleteCookies() closeDialog(); break; } @@ -315,7 +369,7 @@ /** * Enable/disable control elements - * @param {Boolean} disabled + * @pa\ram {Boolean} disabled */ function disableControls(disabled) { @@ -367,23 +421,94 @@ texts: { name: cookie.name, value: cookie.value + }, + titles: { + whitelist: cookieWhitelistButtonTitle } }; } - function createDomainObj(domain, cookienum) + /** + * Create a Table List Item Structure Object + * @param {String} domain Domain name + * @param {Number} cookienum Number of domain cookies + * @param {Boolean} whitelist specifies whether domain is whitelisted + */ + function createDomainObj(domain, cookienum, whitelist = false) { return { dataset: { - access: domain + access: domain, + whitelist: whitelist }, texts: { domain: domain, cookienum: cookienum + " Cookies" + }, + titles: { + whitelist: domainWhitelistButtonTitle } }; } - + function updateWhitelistInList(whitelistChange) + { + for (var domain in whitelistChange) + { + let domainElement = tableList.getItem(domain); + if (domainElement) + { + domainElement.dataset.whitelist = whitelistChange[domain].domainWhitelist; + tableList.updateItem(domainElement, domain); + let cookies = whitelistChange[domain].cookies; + if (domainElement.subItems) + { + for (let subElement of domainElement.subItems) + { + if (cookies.includes(subElement.texts.name)) + { + subElement.dataset.whitelist = true; + let cookieAccessor = "li[data-access='" + subElement.dataset.access + "']" + document.querySelector(cookieAccessor).dataset.whitelist = true + } + else + { + subElement.dataset.whitelist = false; + let cookieAccessor = "li[data-access='" + subElement.dataset.access + "']" + document.querySelector(cookieAccessor).dataset.whitelist = false + } + } + } + } + } + }; + onStorageChange.addListener(function(changeInfo) + { + if ("cookieWhitelist" in changeInfo) + { + let newValue = changeInfo.cookieWhitelist.newValue + let oldValue = changeInfo.cookieWhitelist.oldValue + let changed = {} + if (oldValue) + { + changed = Object.keys(newValue).reduce((acc, domain) => + { + let oldDomainObj = oldValue[domain] + let newDomainObj = newValue[domain] + if (!oldDomainObj || newDomainObj.domainWhitelist != oldDomainObj.domainWhitelist || + JSON.stringify(newDomainObj.cookies) != JSON.stringify(oldDomainObj.cookies)) + { + acc[domain] = newValue[domain]; + } + return acc; + }, {}); + } + else + { + changed = newValue + } + updateWhitelistInList(changed); + } + }); onCookieChange.addListener(function(changeInfo) { var cookie = changeInfo.cookie; diff --git a/manifest.json b/manifest.json index 0bca27f..12d350a 100644 --- a/manifest.json +++ b/manifest.json @@ -20,5 +20,5 @@ "permissions": ["privacy", "cookies", "browsingData", "tabs", "webRequest", "webRequestBlocking", "storage"], "incognito": "split", "update_url": "https://clients2.google.com/service/update2/crx", - "version": "3.3.0" + "version": "3.4.0" } diff --git a/popup.html b/popup.html index 5ced4f9..cdb579f 100644 --- a/popup.html +++ b/popup.html @@ -116,6 +116,7 @@

+ @@ -135,6 +136,7 @@

+