@@ -5,6 +5,7 @@ | |||||
"firefox": "55", | "firefox": "55", | ||||
"chrome": "60" | "chrome": "60" | ||||
} | } | ||||
}] | |||||
}], | |||||
"stage-3" | |||||
] | ] | ||||
} | } |
@@ -2,6 +2,10 @@ | |||||
"name": "bannerbanner", | "name": "bannerbanner", | ||||
"description": "Hide view-reducing screen junk from websites.", | "description": "Hide view-reducing screen junk from websites.", | ||||
"version": "1.0.0", | "version": "1.0.0", | ||||
"page_action": { | |||||
"browser_style": true, | |||||
"default_title": "Ban banners" | |||||
}, | |||||
"background": { | "background": { | ||||
"scripts": ["background_script.js"] | "scripts": ["background_script.js"] | ||||
}, | }, | ||||
@@ -11,7 +15,9 @@ | |||||
"run_at": "document_end" | "run_at": "document_end" | ||||
}], | }], | ||||
"permissions": [ | "permissions": [ | ||||
"<all_urls>" | |||||
"<all_urls>", | |||||
"storage", | |||||
"tabs" | |||||
], | ], | ||||
"applications": { | "applications": { | ||||
"gecko": {"id": "bannerbanner@bannerbanner"} | "gecko": {"id": "bannerbanner@bannerbanner"} | ||||
@@ -15,6 +15,7 @@ | |||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
"babel-preset-env": "^1.6.1", | "babel-preset-env": "^1.6.1", | ||||
"babel-preset-stage-3": "^6.24.1", | |||||
"babelify": "^7.3.0", | "babelify": "^7.3.0", | ||||
"browserify": "^14.3.0", | "browserify": "^14.3.0", | ||||
"crx": "^3.2.1", | "crx": "^3.2.1", | ||||
@@ -1 +1,8 @@ | |||||
import browser from 'webextension-polyfill' | 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)) |
@@ -1,6 +1,9 @@ | |||||
import browser from 'webextension-polyfill' | import browser from 'webextension-polyfill' | ||||
import throttle from 'lodash/throttle' | import throttle from 'lodash/throttle' | ||||
import { onEnabledChange, isEnabled } from './perOrigin' | |||||
function hideBanners() { | function hideBanners() { | ||||
const revertSteps = [] | const revertSteps = [] | ||||
@@ -47,20 +50,27 @@ function hideBanners() { | |||||
return revertSteps | return revertSteps | ||||
} | } | ||||
function main() { | function main() { | ||||
let revertSteps = [] | 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) | 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.forEach(step => step()) | ||||
revertSteps = [] | revertSteps = [] | ||||
} | } | ||||
} | } | ||||
window.addEventListener('scroll', throttle(onScroll, 1000)) | |||||
window.addEventListener('scroll', throttle(run, 300)) | |||||
onEnabledChange(url, run) | |||||
} | } | ||||
main() | main() |
@@ -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') }, | |||||
}) | |||||
} |
@@ -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) | |||||
}); | |||||
} |
@@ -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) | |||||
} |