A background.js => background.js +43 -0
@@ 0,0 1,43 @@
+const damURL = chrome.runtime.getURL("dam/dam.html")
+const extensionOrigin = new URL(damURL).origin;
+
+async function dam(requestDetails) {
+ if (requestDetails.initiator === extensionOrigin) {
+ await chrome.storage.local.set({ dammedURL: null });
+ } else {
+ await chrome.storage.local.set({ dammedURL: requestDetails.url });
+ const [tab] = await chrome.tabs.query({ active: true});
+ await chrome.tabs.update(
+ tab.id,
+ { url: damURL },
+ );
+ }
+}
+
+
+async function updateListener(dammed) {
+ await chrome.webRequest.onBeforeRequest.removeListener(dam);
+ // Don't use an empty urls Array or dam will execute on *every* request
+ if (dammed.length > 0) {
+ await chrome.webRequest.onBeforeRequest.addListener(
+ dam,
+ { urls: dammed, types: ["main_frame"] }
+ );
+ }
+}
+
+
+async function messageListener(request, sender, sendResponse) {
+ if (request.updatedDammed) {
+ await updateListener(request.updatedDammed);
+ }
+}
+
+
+async function main() {
+ const items = await chrome.storage.sync.get({ dammed: [] });
+ await updateListener(items.dammed);
+ await chrome.runtime.onMessage.addListener(messageListener);
+}
+
+main();
M dam/dam.html => dam/dam.html +0 -5
@@ 9,11 9,6 @@
<textarea id="entry"></textarea>
<br>
<button id="save-and-view-journal">Save & View Journal</button>
- <br>
- <label for="domain">Domain to continue to</label>
- <br>
- <input id="domain"></input>
- <br>
<button id="save-and-continue">Save & Continue</button>
</body>
</html>
M dam/dam.js => dam/dam.js +6 -6
@@ 1,9 1,11 @@
async function save(text) {
- let items = await chrome.storage.local.get({ entries: [] });
+ const items = await chrome.storage.local.get(
+ { entries: [], dammedURL: null}
+ );
// Can't store a Map, use an Object instead
const entry = {
"date": Date(),
- "hostname": location.hostname,
+ "hostname": new URL(items.dammedURL).hostname,
"text": text,
};
items.entries.push(entry);
@@ 21,15 23,13 @@ async function saveAndViewJournal() {
async function saveAndContinue() {
- // XXX: Does not validate domain input
- const domain = document.getElementById("domain").value;
- const destination = new URL(`https://${domain}`);
const text = document.getElementById("entry").value;
const numWords = text.trim().split(" ").length;
const items = await chrome.storage.sync.get({ minWords: 1 });
if (text.length > 0 && numWords >= items.minWords) {
await save(text);
- window.open(destination, "_self");
+ const localitems = await chrome.storage.local.get({ dammedURL: null });
+ window.open(localitems.dammedURL, "_self");
} else {
window.alert(`Please write at least ${items.minWords} words.`);
}
M journal/journal.js => journal/journal.js +11 -14
@@ 1,31 1,28 @@
-let container = document.getElementById("entries");
+const container = document.getElementById("entries");
-const addEntry = (entry) => {
- let entryDiv = document.createElement("div");
+function addEntry(entry) {
+ const entryDiv = document.createElement("div");
container.append(entryDiv);
- let date = document.createElement("p");
+ const date = document.createElement("p");
date.textContent = entry.date;
entryDiv.append(date);
- let hostname = document.createElement("p");
+ const hostname = document.createElement("p");
hostname.textContent = entry.hostname;
entryDiv.append(hostname);
- let text = document.createElement("p");
+ const text = document.createElement("p");
text.textContent = entry.text;
entryDiv.append(text);
-};
+}
-const loadEntries = () => {
- chrome.storage.local.get(
- { entries: [] }
- ).then((items) => {
- items.entries.forEach(addEntry);
- });
-};
+async function loadEntries() {
+ items = await chrome.storage.local.get({ entries: [] });
+ items.entries.forEach(addEntry);
+}
document.addEventListener("DOMContentLoaded", loadEntries);
M manifest.json => manifest.json +5 -2
@@ 2,15 2,18 @@
"manifest_version": 3,
"name": "Beaver's Dam",
"description": "Base Level Extension",
- "version": "0.4",
+ "version": "0.5",
"permissions": [
"storage",
"tabs",
- "declarativeNetRequestWithHostAccess"
+ "webRequest"
],
"host_permissions": [
"<all_urls>"
],
+ "background": {
+ "service_worker": "background.js"
+ },
"action": {
"default_popup": "popup/popup.html"
},
M options/options.js => options/options.js +29 -29
@@ 1,3 1,19 @@
+const minWordsInput = document.getElementById("min-words");
+const importInput = document.getElementById("import");
+
+
+async function loadOptions() {
+ const items = await chrome.storage.sync.get({ minWords: 1 });
+ minWordsInput.value = items.minWords;
+}
+
+
+async function saveOptions() {
+ // TODO: type check before saving
+ await chrome.storage.sync.set({ minWords: minWordsInput.value });
+}
+
+
async function exportData() {
const data = JSON.stringify({
sync: await chrome.storage.sync.get(null),
@@ 11,30 27,12 @@ async function exportData() {
a.remove();
};
-const minWordsInput = document.getElementById("min-words");
-// Load saved options
-chrome.storage.sync.get(
- { minWords: 0 }
-).then((items) => {
- minWordsInput.value = items.minWords;
-});
-
-document.getElementById("save").addEventListener("click", () => {
- // TODO: type check before saving
- chrome.storage.sync.set(
- { minWords: minWordsInput.value }
- );
-});
-
-document.getElementById("export").addEventListener("click", exportData);
-
-const importInput = document.getElementById("import");
-importInput.addEventListener("change", () => {
+async function importData() {
const [file] = importInput.files;
if (file) {
const reader = new FileReader();
- reader.addEventListener("load", () => {
+ reader.addEventListener("load", async () => {
let data;
let parsed = false;
try {
@@ 48,15 46,17 @@ importInput.addEventListener("change", () => {
// only contain primative values, `Array`s, `Date`s, and `Regex`s,
// which can be serialised by the Chrome Storage API.
// See docs for StorageArea.set
- chrome.storage.sync.set(data["sync"]).then(
- () => chrome.storage.local.set(data["local"]).then(
- () => {
- window.alert("Import successful");
- location.reload();
- }
- )
- );
+ await chrome.storage.sync.set(data["sync"]);
+ await chrome.storage.local.set(data["local"]);
+ window.alert("Import successful");
+ location.reload();
});
reader.readAsText(file);
}
-});
+}
+
+
+document.getElementById("save").addEventListener("click", saveOptions);
+document.getElementById("export").addEventListener("click", exportData);
+importInput.addEventListener("change", importData);
+loadOptions();
M => +23 -43
@@ 1,53 1,30 @@
async function updateRules(domains) {
await chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{
id: 1,
action: {
type: "redirect",
redirect: {extensionPath: "/dam/dam.html"},
},
condition: {
requestDomains: domains,
resourceTypes: ["main_frame"],
excludedInitiatorDomains: [
new URL(chrome.runtime.getURL("dam/dam.html")).hostname
],
},
}],
removeRuleIds: [1],
});
}
async function setToggleDammedBehaviour() {
const toggleDammedButton= document.getElementById("toggle-dammed");
const [tab] = await chrome.tabs.query({ active: true});
const domain = new URL(tab.url).hostname;
const url = new URL(tab.url);
const match_pattern = url.origin + "/*";
const [rule] = await chrome.declarativeNetRequest.getDynamicRules(
{ ruleIds: [1] }
);
let dammed;
if (rule === undefined) {
// No rules set yet
dammed = [];
} else {
dammed = rule.condition.requestDomains;
}
const items = await chrome.storage.sync.get({ dammed: []});
let dammed = items.dammed;
if (dammed.includes(domain)) {
toggleDammedButton.textContent = `Undam ${domain}`;
const index = dammed.indexOf(domain);
let toggleDammed;
if (dammed.includes(match_pattern)) {
toggleDammedButton.textContent = `Undam ${url.hostname}`;
const index = dammed.indexOf(match_pattern);
dammed.splice(index, 1);
} else {
toggleDammedButton.textContent = `Dam ${domain}`;
dammed.push(domain);
toggleDammedButton.textContent = `Dam ${url.hostname}`;
dammed.push(match_pattern);
}
toggleDammedButton.addEventListener("click", async () => {
await updateRules(dammed);
chrome.tabs.reload(tab.id).then(window.close);
await chrome.storage.sync.set({ dammed: dammed });
// Get background.js to update onBeforeRequest listener
await chrome.runtime.sendMessage(
{ updatedDammed: dammed }
);
await chrome.tabs.reload(tab.id);
window.close();
});
}
@@ 64,9 41,12 @@ document.getElementById("open-options").addEventListener(
document.getElementById("clear").addEventListener(
"click",
() => {
chrome.storage.sync.clear();
chrome.storage.local.clear();
async () => {
await chrome.storage.sync.clear();
await chrome.storage.local.clear();
await chrome.runtime.sendMessage(
{ updatedDammed: [] }
);
},
);
M todo => todo +5 -7
@@ 1,15 1,13 @@
-programmatically modify matching pages in background.js?
-
Custom prompt (different prompt for undamming?)
Custom style (colours, font)
Different prompt, word count per domain?
-Make it work with youtube
- - dam doesn't overlay everything with default z-index
- - media autoplays
- - takes ages to load
+Type check before saving options
+
+Fix dammedURL being overwritten when dam for one site is left
+and in the meantime another site is dammed.
-Type check before saving options
+Forbid damming the extension