From 5e5011f2084a8aacb561ba29449f25b136725736 Mon Sep 17 00:00:00 2001 From: kkapsner Date: Tue, 11 Sep 2018 23:54:59 +0200 Subject: [PATCH] Added setting to controll to save notification details Fixes #236 --- _locales/de/messages.json | 47 +++++++- _locales/en/messages.json | 47 +++++++- icons/pageAction-ignorelist.svg | 84 +++++++++++++ icons/pageAction-whitelist.svg | 74 ++++++++++++ icons/pageAction-whitelistTemporarily.svg | 94 +++++++++++++++ lib/frame.js | 8 +- lib/settingDefinitions.js | 4 + options/settingsDisplay.js | 10 ++ pageAction/domainNotification.js | 46 ++++++-- pageAction/gui.js | 19 ++- pageAction/pageAction-dark.css | 8 ++ pageAction/pageAction-default.css | 8 ++ pageAction/pageAction-light.css | 8 ++ pageAction/pageAction.css | 50 ++++++++ pageAction/pageAction.js | 137 +++++++++++----------- releaseNotes.txt | 2 + 16 files changed, 556 insertions(+), 90 deletions(-) create mode 100644 icons/pageAction-ignorelist.svg create mode 100644 icons/pageAction-whitelist.svg create mode 100644 icons/pageAction-whitelistTemporarily.svg diff --git a/_locales/de/messages.json b/_locales/de/messages.json index c65c861..0383a9f 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -595,24 +595,52 @@ "message": "ignoriere URL", "description": "" }, + "selectIgnore": { + "message": "Wählen Sie die Domain oder URL aus, die zur Ignorierliste hinzugefügt werden soll:", + "description": "" + }, + "inputIgnore": { + "message": "Geben Sie die Domain oder URL \"RegExp\" ein, die zur Ignorierliste hinzugefügt werden soll:", + "description": "" + }, "inputIgnoreDomain": { "message": "Geben Sie die Domain ein, die zur Ignorierliste hinzugefügt werden soll:", "description": "" }, - "inputWhitelistDomain": { - "message": "Geben Sie die URL \"RegExp\" ein, die erlaubt werden soll:", + "inputIgnoreURL": { + "message": "Geben Sie die URL \"RegExp\" ein, die zur Ignorierliste hinzugefügt werden soll:", "description": "" }, - "inputWhitelistURL": { + "selectWhitelist": { + "message": "Wählen Sie die Domain oder URL aus, die erlaubt werden soll:", + "description": "" + }, + "inputWhitelist": { + "message": "Geben Sie die Domain oder die URL \"RegExp\" ein, die erlaubt werden soll:", + "description": "" + }, + "inputWhitelistDomain": { "message": "Geben Sie die Domain ein, die erlaubt werden soll:", "description": "" }, + "inputWhitelistURL": { + "message": "Geben Sie die URL \"RegExp\" ein, die erlaubt werden soll:", + "description": "" + }, + "selectSessionWhitelist": { + "message": "Wählen Sie die Domain oder URL aus, die für diese Sitzung erlaubt werden soll:", + "description": "" + }, + "inputSessionWhitelist": { + "message": "Geben Sie die Domain oder URL \"RegExp\" ein, die für dieses Sitzung erlaubt werden soll:", + "description": "" + }, "inputSessionWhitelistDomain": { - "message": "Geben Sie die URL \"RegExp\" ein, die für diese Sitzung erlaubt werden soll:", + "message": "Geben Sie die Domain ein, die für diese Sitzung erlaubt werden soll:", "description": "" }, "inputSessionWhitelistURL": { - "message": "Geben Sie die Domain ein, die für diese Sitzung erlaubt werden soll:", + "message": "Geben Sie die URL \"RegExp\" ein, die für diese Sitzung erlaubt werden soll:", "description": "" }, "settings": { @@ -754,6 +782,15 @@ "description": "" }, + "storeNotificationData_title": { + "message": "Details der Benachrichtigungen speichern", + "description": "" + }, + "storeNotificationData_description": { + "message": "", + "description": "" + }, + "inspectImage": { "message": "Bild betrachten", "description": "" diff --git a/_locales/en/messages.json b/_locales/en/messages.json index be4358a..5f63b7a 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -594,24 +594,52 @@ "message": "ignore URL", "description": "" }, + "selectIgnore": { + "message": "Select domain or URL to add to ignore list:", + "description": "" + }, + "inputIgnore": { + "message": "Input domain or URL \"RegExp\" to add to ignore list:", + "description": "" + }, "inputIgnoreDomain": { "message": "Input domain to add to ignore list:", "description": "" }, - "inputWhitelistDomain": { - "message": "Input URL \"RegExp\" to add to white list:", + "inputIgnoreURL": { + "message": "Input URL \"RegExp\" to add to ignore list:", "description": "" }, - "inputWhitelistURL": { + "selectWhitelist": { + "message": "Select domain or URL to add to white list:", + "description": "" + }, + "inputWhitelist": { + "message": "Input domain or URL \"RegExp\" to add to white list:", + "description": "" + }, + "inputWhitelistDomain": { "message": "Input domain to add to white list:", "description": "" }, + "inputWhitelistURL": { + "message": "Input URL \"RegExp\" to add to white list:", + "description": "" + }, + "selectSessionWhitelist": { + "message": "Select domain or URL to add to the session white list:", + "description": "" + }, + "inputSessionWhitelist": { + "message": "Input domain or URL \"RegExp\" to add to the session white list:", + "description": "" + }, "inputSessionWhitelistDomain": { - "message": "Input URL \"RegExp\" to add to the session white list:", + "message": "Input domain to add to the session white list:", "description": "" }, "inputSessionWhitelistURL": { - "message": "Input domain to add to the session white list:", + "message": "Input URL \"RegExp\" to add to the session white list:", "description": "" }, "settings": { @@ -753,6 +781,15 @@ "description": "" }, + "storeNotificationData_title": { + "message": "Store detail data of the notifications", + "description": "" + }, + "storeNotificationData_description": { + "message": "", + "description": "" + }, + "inspectImage": { "message": "inspect image", "description": "" diff --git a/icons/pageAction-ignorelist.svg b/icons/pageAction-ignorelist.svg new file mode 100644 index 0000000..7f84189 --- /dev/null +++ b/icons/pageAction-ignorelist.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + www. + + + + + + diff --git a/icons/pageAction-whitelist.svg b/icons/pageAction-whitelist.svg new file mode 100644 index 0000000..c15cd63 --- /dev/null +++ b/icons/pageAction-whitelist.svg @@ -0,0 +1,74 @@ + + + + + + + + + + image/svg+xml + + + + + + + www. + + + diff --git a/icons/pageAction-whitelistTemporarily.svg b/icons/pageAction-whitelistTemporarily.svg new file mode 100644 index 0000000..b5ebc34 --- /dev/null +++ b/icons/pageAction-whitelistTemporarily.svg @@ -0,0 +1,94 @@ + + + + + + + + + + image/svg+xml + + + + + + + www. + + + + + + + + diff --git a/lib/frame.js b/lib/frame.js index a9393c6..7509cb1 100644 --- a/lib/frame.js +++ b/lib/frame.js @@ -68,9 +68,13 @@ } }); var notifications = []; + var notificationCounter = {}; function notify(data){ if (!settings.ignoredAPIs[data.api]){ - notifications.push(data); + if (settings.storeNotificationData){ + notifications.push(data); + } + notificationCounter[data.messageId] = (notificationCounter[data.messageId] || 0) + 1; port.postMessage({"canvasBlocker-notify": data}); } } @@ -154,6 +158,8 @@ notice("sending notifications:", notifications); browser.runtime.sendMessage({ sender: tabId, + url: window.location.href, + "canvasBlocker-notificationCounter": notificationCounter, "canvasBlocker-notifications": notifications }); notice("notifications sent"); diff --git a/lib/settingDefinitions.js b/lib/settingDefinitions.js index de80588..3db615b 100644 --- a/lib/settingDefinitions.js +++ b/lib/settingDefinitions.js @@ -184,6 +184,10 @@ name: "displayBadge", defaultValue: true }, + { + name: "storeNotificationData", + defaultValue: false + }, { name: "storeImageForInspection", defaultValue: false diff --git a/options/settingsDisplay.js b/options/settingsDisplay.js index e5fa469..f4c0d09 100644 --- a/options/settingsDisplay.js +++ b/options/settingsDisplay.js @@ -209,11 +209,21 @@ { "name": "highlightBrowserAction" }, + { + "name": "storeNotificationData", + "displayDependencies": [ + { + "showNotifications": [true], + "displayAdvancedSettings": [true] + } + ] + }, { "name": "storeImageForInspection", "displayDependencies": [ { "showNotifications": [true], + "storeNotificationData": [true], "displayAdvancedSettings": [true] } ] diff --git a/pageAction/domainNotification.js b/pageAction/domainNotification.js index 53a6ffd..d96bab8 100644 --- a/pageAction/domainNotification.js +++ b/pageAction/domainNotification.js @@ -25,14 +25,27 @@ }; }(); - const DomainNotification = function DomainNotification(domain, messageId){ + const DomainNotification = function DomainNotification(domain, messageId, count = 0){ + if (domain instanceof URL){ + this.urls().add(domain.href); + domain = domain.hostname; + } this.domain = domain; this.messageId = messageId; + this.count = count; this.extraNotifications = 0; addToContainer(this); this.update(); }; + DomainNotification.prototype.urls = function urls(){ + const urls = new Set(); + this.urls = function(){ + return urls; + }; + return urls; + }; + DomainNotification.prototype.notifications = function notifications(){ const notifications = []; this.notifications = function(){ @@ -47,6 +60,7 @@ } else { this.notifications().push(notification); + this.urls().add(notification.url.href); this.notificationsNode().appendChild(notification.node()); } this.update(); @@ -77,6 +91,7 @@ }; DomainNotification.prototype.update = function update(){ this.updateTextNode(); + this.node().classList[this.notifications().length? "remove": "add"]("empty"); this.notifications().forEach(function(notification){ notification.update(); }); @@ -110,28 +125,29 @@ DomainNotification.prototype.updateTextNode = function updateTextNode(){ const node = this.textNode(); const notifications = this.notifications(); - const urls = notifications.map(function(not){ - return not.url; - }).filter(function(url, i, urls){ - return urls.indexOf(url) === i; - }).join("\n"); + const urls = Array.from(this.urls()).join("\n"); node.querySelectorAll(".url").forEach((urlSpan) => { - urlSpan.title = urls + (this.extraNotifications? "\n...": ""); + urlSpan.title = urls; }); node.title = notifications.map(function(notification){ return notification.timestamp + ": " + notification.functionName; - }).join("\n") + this.extraNotifications? "\n...": ""; + }).join("\n") + (this.extraNotifications? "\n...": ""); node.querySelectorAll(".count").forEach((countSpan) => { - countSpan.textContent = notifications.length + this.extraNotifications; + if (this.count){ + countSpan.textContent = this.count; + } + else { + countSpan.textContent = notifications.length + this.extraNotifications; + } }); }; DomainNotification.prototype.actionsNode = function actionsNode(){ const node = document.createElement("div"); node.className = "actions"; - createActionButtons(node, actions, this.domain); + createActionButtons(node, actions, {domain: this.domain, urls: this.urls()}); this.actionsNode = function(){ return node; }; @@ -148,12 +164,18 @@ }; const domains = new Map(); - const domainNotification = function(domain, messageId){ + const domainNotification = function(url, messageId, count = 0){ + const domain = url.hostname; var domainNotification = domains.get(domain + messageId); if (!domainNotification){ - domainNotification = new DomainNotification(domain, messageId); + domainNotification = new DomainNotification(url, messageId, count); domains.set(domain + messageId, domainNotification); } + else { + domainNotification.count += count; + domainNotification.urls().add(url.href); + domainNotification.update(); + } return domainNotification; }; domainNotification.addAction = addAction; diff --git a/pageAction/gui.js b/pageAction/gui.js index 16008e9..0b9f0c1 100644 --- a/pageAction/gui.js +++ b/pageAction/gui.js @@ -57,13 +57,30 @@ } }); }; - + scope.modalChoice = function modalChoice(messageText, choices){ + message("open modal choice"); + return new Promise(function(resolve, reject){ + document.body.innerHTML = ""; + document.body.className = "modal"; + document.body.appendChild(document.createTextNode(messageText)); + choices.forEach(function(choice){ + const button = document.createElement("button"); + button.addEventListener("click", function(){ + resolve(choice.value || choice); + message("modal choice closed with value", choice.value || choice); + }); + button.appendChild(document.createTextNode(choice.text || choice)); + document.body.appendChild(button); + }); + }); + }; scope.modalPrompt = function modalPrompt(messageText, defaultValue){ message("open modal prompt"); return new Promise(function(resolve, reject){ document.body.innerHTML = ""; + document.body.className = "modal"; document.body.appendChild(document.createTextNode(messageText)); var input = document.createElement("input"); input.value = defaultValue; diff --git a/pageAction/pageAction-dark.css b/pageAction/pageAction-dark.css index 500c8f8..56ced99 100644 --- a/pageAction/pageAction-dark.css +++ b/pageAction/pageAction-dark.css @@ -13,4 +13,12 @@ body { .hasHiddenActions:hover, .hasHiddenActions .actions { background-color: rgb(92, 92, 97); +} + +.modal button { + border-color: rgb(92, 92, 97); +} + +.modal button:active, .modal button:hover, .modal button:focus { + background-color: rgb(92, 92, 97); } \ No newline at end of file diff --git a/pageAction/pageAction-default.css b/pageAction/pageAction-default.css index 97540d6..38d0bc9 100644 --- a/pageAction/pageAction-default.css +++ b/pageAction/pageAction-default.css @@ -13,4 +13,12 @@ body { .hasHiddenActions:hover, .hasHiddenActions .actions { background-color: rgb(236, 237, 236); +} + +.modal button { + border-color: rgb(236, 237, 236); +} + +.modal button:active, .modal button:hover, .modal button:focus { + background-color: rgb(236, 237, 236); } \ No newline at end of file diff --git a/pageAction/pageAction-light.css b/pageAction/pageAction-light.css index 43b1c43..ea5ef7a 100644 --- a/pageAction/pageAction-light.css +++ b/pageAction/pageAction-light.css @@ -13,4 +13,12 @@ body { .hasHiddenActions:hover, .hasHiddenActions .actions { background-color: rgb(240, 240, 240); +} + +.modal button { + border-color: rgb(240, 240, 240); +} + +.modal button:active, .modal button:hover, .modal button:focus { + background-color: rgb(240, 240, 240); } \ No newline at end of file diff --git a/pageAction/pageAction.css b/pageAction/pageAction.css index 23b2be8..1842a56 100644 --- a/pageAction/pageAction.css +++ b/pageAction/pageAction.css @@ -6,6 +6,7 @@ body { margin: 0.5em; padding: 2px; padding-right: 23px; + padding-bottom: 1.5em; white-space: nowrap; position: relative; } @@ -85,6 +86,9 @@ button.action img { position: relative; margin: 0; } +.collapsible.empty .collapser { + display: none; +} .collapsible.collapsed .collapser { display: inline-block; width: 20px; @@ -108,6 +112,9 @@ button.action img { .collapsible .collapser .less { display: inline; } +.collapsible.empty .collapser, .collapsible.collapsed.empty .collapser { + display: none; +} .collapsible.collapsed .collapsing { height: 0px; overflow: hidden; @@ -117,4 +124,47 @@ button.action img { height: initial; width: 100%; box-sizing: border-box; +} + +/* modal display*/ + +body.modal { + padding: 2px; +} + +.modal input { + display: block; + box-sizing: border-box; + width: 100%; +} + +.modal button { + display: block; + padding: 0.5em; + background-color: transparent; + border: 1px solid currentColor; + cursor: pointer; + width: 100%; + text-align: left; + height: auto; + z-index: 1; + position: relative; + white-space: nowrap; + color: currentColor; +} + +.modal button + .modal button { + border-top-width: 0; +} + +.modal button:first-child { + border-radius: 3px 3px 0 0; +} + +.modal button:last-child { + border-radius: 0 0 3px 3px; +} + +.modal button:active, .modal button:hover, .modal button:focus { + z-index: 10; } \ No newline at end of file diff --git a/pageAction/pageAction.js b/pageAction/pageAction.js index 666a1e1..69831ea 100644 --- a/pageAction/pageAction.js +++ b/pageAction/pageAction.js @@ -11,7 +11,7 @@ const domainNotification = require("./domainNotification"); const Notification = require("./Notification"); - const {createActionButtons, modalPrompt} = require("./gui"); + const {createActionButtons, modalPrompt, modalChoice} = require("./gui"); const lists = require("./lists"); Promise.all([ @@ -69,19 +69,44 @@ throw new Error("tooManyTabsFound"); } - + function domainOrUrlPicker(domain, urls, selectText, urlInputText){ + const choices = Array.from(urls).map(function(url){ + return { + text: url, + value: "^" + url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "$" + }; + }); + choices.unshift(domain); + return modalChoice( + selectText, + choices + ).then(function(choice){ + if (choice.startsWith("^")){ + return modalPrompt( + urlInputText, + choice + ); + } + else { + return choice; + } + }); + } + verbose("registering domain actions"); [ { - name: "ignorelistDomain", + name: "ignorelist", isIcon: true, - callback: function(domain){ - modalPrompt( - browser.i18n.getMessage("inputIgnoreDomain"), - domain - ).then(function(domain){ - if (domain){ - settings.set("showNotifications", false, domain).then(function(){ + callback: function({domain, urls}){ + domainOrUrlPicker( + domain, + urls, + browser.i18n.getMessage("selectIgnore"), + browser.i18n.getMessage("inputIgnoreURL") + ).then(function(choice){ + if (choice){ + settings.set("showNotifications", false, choice).then(function(){ window.close(); }); } @@ -92,15 +117,17 @@ } }, { - name: "whitelistDomain", + name: "whitelist", isIcon: true, - callback: function(domain){ - modalPrompt( - browser.i18n.getMessage("inputWhitelistURL"), - domain - ).then(function(domain){ - if (domain){ - settings.set("blockMode", "allow", domain).then(function(){ + callback: function({domain, urls}){ + domainOrUrlPicker( + domain, + urls, + browser.i18n.getMessage("selectWhitelist"), + browser.i18n.getMessage("inputWhitelistURL") + ).then(function(choice){ + if (choice){ + settings.set("blockMode", "allow", choice).then(function(){ window.close(); }); } @@ -111,15 +138,17 @@ } }, { - name: "whitelistDomainTemporarily", + name: "whitelistTemporarily", isIcon: true, - callback: function(domain){ - modalPrompt( - browser.i18n.getMessage("inputSessionWhitelistURL"), - domain - ).then(function(domain){ - if (domain){ - lists.appendTo("sessionWhite", domain).then(function(){ + callback: function({domain, urls}){ + domainOrUrlPicker( + domain, + urls, + browser.i18n.getMessage("selectSessionWhitelist"), + browser.i18n.getMessage("inputSessionWhitelistURL") + ).then(function(choice){ + if (choice){ + lists.appendTo("sessionWhite", choice).then(function(){ window.close(); }); } @@ -148,44 +177,6 @@ callback: function({errorStack}){ alert(parseErrorStack(errorStack)); } - }, - { - name: "whitelistURL", - isIcon: true, - callback: function({url}){ - modalPrompt( - browser.i18n.getMessage("inputWhitelistDomain"), - "^" + url.href.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "$" - ).then(function(url){ - if (url){ - settings.set("blockMode", "allow", url).then(function(){ - window.close(); - }); - } - else { - window.close(); - } - }); - } - }, - { - name: "whitelistURLTemporarily", - isIcon: true, - callback: function({url}){ - modalPrompt( - browser.i18n.getMessage("inputSessionWhitelistDomain"), - "^" + url.href.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "$" - ).then(function(url){ - if (url){ - lists.appendTo("sessionWhite", url).then(function(){ - window.close(); - }); - } - else { - window.close(); - } - }); - } } ].forEach(function(action){ Notification.addAction(action); @@ -193,7 +184,20 @@ var tab = tabs[0]; browser.runtime.onMessage.addListener(function(data){ - if (Array.isArray(data["canvasBlocker-notifications"])){ + if (data["canvasBlocker-notificationCounter"]){ + const url = new URL(data.url); + Object.keys(data["canvasBlocker-notificationCounter"]).forEach(function(key){ + const notification = domainNotification( + url, + key, + data["canvasBlocker-notificationCounter"][key] + ); + }); + } + if ( + Array.isArray(data["canvasBlocker-notifications"]) && + data["canvasBlocker-notifications"].length + ){ message("got notifications"); const notifications = data["canvasBlocker-notifications"]; let i = 0; @@ -205,13 +209,14 @@ else { for (var delta = 0; delta < 20 && i + delta < length; delta += 1){ let notification = notifications[i + delta]; + verbose(notification); if (settings.ignoredAPIs[notification.api]){ continue; } verbose(notification); notification.url = new URL(notification.url); domainNotification( - notification.url.hostname, + notification.url, notification.messageId ).addNotification(new Notification(notification)); } diff --git a/releaseNotes.txt b/releaseNotes.txt index 6489f34..dfdbc70 100644 --- a/releaseNotes.txt +++ b/releaseNotes.txt @@ -1,10 +1,12 @@ Version 0.5.4: changes: - converted "API whitelist" to "protected API features" (automatic settings migration) + - notification details are not stored by default new features: - added save/load directly to/from file option - added protection for DOMRect (getClientRects) + - added setting to control if notification details should be stored fixes: - window and audio API were always blocked when using any of the "block ..." modes