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 @@
+
+
+
+
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 @@
+
+
+
+
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 @@
+
+
+
+
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