From 45086cf67f5693844a63970bc3d66134ed3b0536 Mon Sep 17 00:00:00 2001 From: Gerben Date: Mon, 12 Mar 2018 15:42:02 +0100 Subject: [PATCH] Add pageAction button to enable/disable per origin --- .babelrc | 3 ++- extension/manifest.json | 8 ++++++- package.json | 1 + src/background_script.js | 7 +++++++ src/content_script.js | 20 +++++++++++++----- src/listenableStorage.js | 27 ++++++++++++++++++++++++ src/pageActionButton.js | 31 +++++++++++++++++++++++++++ src/perOrigin.js | 45 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 src/listenableStorage.js create mode 100644 src/pageActionButton.js create mode 100644 src/perOrigin.js diff --git a/.babelrc b/.babelrc index 866d941..1086dc9 100644 --- a/.babelrc +++ b/.babelrc @@ -5,6 +5,7 @@ "firefox": "55", "chrome": "60" } - }] + }], + "stage-3" ] } diff --git a/extension/manifest.json b/extension/manifest.json index 0185ead..d5cf20a 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -2,6 +2,10 @@ "name": "bannerbanner", "description": "Hide view-reducing screen junk from websites.", "version": "1.0.0", + "page_action": { + "browser_style": true, + "default_title": "Ban banners" + }, "background": { "scripts": ["background_script.js"] }, @@ -11,7 +15,9 @@ "run_at": "document_end" }], "permissions": [ - "" + "", + "storage", + "tabs" ], "applications": { "gecko": {"id": "bannerbanner@bannerbanner"} diff --git a/package.json b/package.json index d3c0328..f1ecfbf 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "devDependencies": { "babel-preset-env": "^1.6.1", + "babel-preset-stage-3": "^6.24.1", "babelify": "^7.3.0", "browserify": "^14.3.0", "crx": "^3.2.1", diff --git a/src/background_script.js b/src/background_script.js index 5c5e0f0..e4a5be7 100644 --- a/src/background_script.js +++ b/src/background_script.js @@ -1 +1,8 @@ import browser from 'webextension-polyfill' + +import pageActionButton from './pageActionButton' +import { toggleEnabled, isEnabled } from './perOrigin' + + +pageActionButton({ predicate: tab => true }) +browser.pageAction.onClicked.addListener(tab => toggleEnabled(tab.url)) diff --git a/src/content_script.js b/src/content_script.js index 7d89aaa..d184bec 100644 --- a/src/content_script.js +++ b/src/content_script.js @@ -1,6 +1,9 @@ import browser from 'webextension-polyfill' import throttle from 'lodash/throttle' +import { onEnabledChange, isEnabled } from './perOrigin' + + function hideBanners() { const revertSteps = [] @@ -47,20 +50,27 @@ function hideBanners() { return revertSteps } - function main() { let revertSteps = [] - function onScroll(event) { - if (window.scrollY > 20) { + // XXX We read the location only once, but it may be changed by page scripts. + // Fortunately we only need the origin, which should remain the same. + const url = window.location.href + + async function run() { + const enabled = await isEnabled(url) + if (enabled && window.scrollY > 20) { + // Hide everything that looks annoying. Remember the steps to revert this. revertSteps = hideBanners().concat(revertSteps) - } else if (window.scrollY <= 0) { + } else if (!enabled || window.scrollY <= 0) { + // Unhide everything we have hidden. revertSteps.forEach(step => step()) revertSteps = [] } } - window.addEventListener('scroll', throttle(onScroll, 1000)) + window.addEventListener('scroll', throttle(run, 300)) + onEnabledChange(url, run) } main() diff --git a/src/listenableStorage.js b/src/listenableStorage.js new file mode 100644 index 0000000..0e86cdf --- /dev/null +++ b/src/listenableStorage.js @@ -0,0 +1,27 @@ +/* Augments the WebExtension storage API to allow listening for changes on a + * single variable of a single storage area (i.e. local or sync). + * + * Usage example: + * const storage = listenableStorage(browser.storage) + * storage.local.onChanged('someVar', newValue => { .... }) + */ + +export default storage => { + const mkOnChange = storageName => { + return (key, cb) => { + const listener = (changes, areaName) => { + if (changes[key]) { + const newValue = changes[key].newValue + cb(newValue) + } + } + storage.onChanged.addListener(listener) + } + } + + return storage && ({ + ...storage, + local: storage.local && { ...storage.local, onChanged: mkOnChange('local') }, + sync: storage.sync && { ...storage.sync, onChanged: mkOnChange('sync') }, + }) +} diff --git a/src/pageActionButton.js b/src/pageActionButton.js new file mode 100644 index 0000000..29adb6f --- /dev/null +++ b/src/pageActionButton.js @@ -0,0 +1,31 @@ +/* Show a page action button on a tab whenever the given predicate is true. + * The predicate is passed a Tab object, and is evaluated whenever the tab has + * changed. + * + * Example usage: + * pageAction({ predicate: tab => tab.url.startsWith('https') }) + */ + +import browser from 'webextension-polyfill' + + +export default async function pageActionButton({ predicate }) { + async function maybeShowButton(tab) { + if (await predicate(tab)) { + browser.pageAction.show(tab.id) + } else { + browser.pageAction.hide(tab.id) + } + } + + // Show/hide the button on current tabs. + const tabs = await browser.tabs.query({}) + for (const tab of tabs) { + maybeShowButton(tab) + } + + // Show/hide the button whenever a tab changes. + browser.tabs.onUpdated.addListener((id, changeInfo, tab) => { + maybeShowButton(tab) + }); +} diff --git a/src/perOrigin.js b/src/perOrigin.js new file mode 100644 index 0000000..242806d --- /dev/null +++ b/src/perOrigin.js @@ -0,0 +1,45 @@ +/* Tells or toggles for any URL whether the extension should be enabled. + * Groups URL by their origin; a whole origin is enabled or disabled. + */ + +import browser from 'webextension-polyfill' + +import listenableStorage from './listenableStorage' + +const browserStorage = listenableStorage(browser.storage) + +// Whichever is supported. +// XXX Causes data loss when the browser suddenly supports the sync storage area. +const storage = browserStorage.sync || browserStorage.local + +const keyForUrl = url => { + const origin = new URL(url).origin + return `enable_on_origin_${origin}` +} + +export async function disable(url) { + await storage.set({ [keyForUrl(url)]: false }) +} + +export async function enable(url) { + await storage.set({ [keyForUrl(url)]: true }) +} + +export async function toggleEnabled(url) { + const enabled = await isEnabled(url) + if (enabled) { + await disable(url) + } else { + await enable(url) + } +} + +export async function isEnabled(url, defaultValue = true) { + const key = keyForUrl(url) + const result = await storage.get({ [key]: defaultValue }) + return result[key] +} + +export function onEnabledChange(url, cb) { + storage.onChanged(keyForUrl(url), cb) +}