Compare commits
3 Commits
master
...
insertcss2
Author | SHA1 | Date | |
---|---|---|---|
|
7d094847f6 | ||
|
62053316a2 | ||
|
aff4707bf0 |
|
@ -12,10 +12,11 @@ var browserCommands, contextMenus;
|
||||||
chrome.runtime.onMessage.addListener(onRuntimeMessage);
|
chrome.runtime.onMessage.addListener(onRuntimeMessage);
|
||||||
|
|
||||||
{
|
{
|
||||||
const listener =
|
const [listener] = [
|
||||||
URLS.chromeProtectsNTP
|
[webNavigationListenerChrome, CHROME],
|
||||||
? webNavigationListenerChrome
|
[webNavigationListenerFF, FIREFOX],
|
||||||
: webNavigationListener;
|
[webNavigationListener, true],
|
||||||
|
].find(([, selected]) => selected);
|
||||||
|
|
||||||
chrome.webNavigation.onBeforeNavigate.addListener(data =>
|
chrome.webNavigation.onBeforeNavigate.addListener(data =>
|
||||||
listener(null, data));
|
listener(null, data));
|
||||||
|
@ -44,7 +45,6 @@ if (chrome.contextMenus) {
|
||||||
chrome.contextMenus.onClicked.addListener((info, tab) =>
|
chrome.contextMenus.onClicked.addListener((info, tab) =>
|
||||||
contextMenus[info.menuItemId].click(info, tab));
|
contextMenus[info.menuItemId].click(info, tab));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chrome.commands) {
|
if (chrome.commands) {
|
||||||
// Not available in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1240350
|
// Not available in Firefox - https://bugzilla.mozilla.org/show_bug.cgi?id=1240350
|
||||||
chrome.commands.onCommand.addListener(command => browserCommands[command]());
|
chrome.commands.onCommand.addListener(command => browserCommands[command]());
|
||||||
|
@ -81,6 +81,24 @@ prefs.subscribe(['iconset'], () => updateIcon({id: undefined}, {}));
|
||||||
browserUIlanguage: chrome.i18n.getUILanguage(),
|
browserUIlanguage: chrome.i18n.getUILanguage(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!FIREFOX && chrome.declarativeContent) {
|
||||||
|
chrome.declarativeContent.onPageChanged.removeRules(null, () => {
|
||||||
|
chrome.declarativeContent.onPageChanged.addRules([{
|
||||||
|
conditions: [
|
||||||
|
new chrome.declarativeContent.PageStateMatcher({
|
||||||
|
pageUrl: {urlContains: ':'},
|
||||||
|
})
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
new chrome.declarativeContent.RequestContentScript({
|
||||||
|
js: ['/content/apply.js'],
|
||||||
|
allFrames: true,
|
||||||
|
matchAboutBlank: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// bind for 60 seconds max and auto-unbind if it's a normal run
|
// bind for 60 seconds max and auto-unbind if it's a normal run
|
||||||
chrome.runtime.onInstalled.addListener(onInstall);
|
chrome.runtime.onInstalled.addListener(onInstall);
|
||||||
|
@ -175,9 +193,37 @@ window.addEventListener('storageReady', function _() {
|
||||||
|
|
||||||
updateIcon({id: undefined}, {});
|
updateIcon({id: undefined}, {});
|
||||||
|
|
||||||
|
if (FIREFOX) {
|
||||||
|
queryTabs().then(tabs =>
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
if (!tab.width) {
|
||||||
|
// skip lazy-loaded tabs (width = 0) that seem to start loading on message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tabId = tab.id;
|
||||||
|
const frameUrls = {0: tab.url};
|
||||||
|
styleViaAPI.allFrameUrls.set(tabId, frameUrls);
|
||||||
|
chrome.webNavigation.getAllFrames({tabId}, frames => frames &&
|
||||||
|
frames.forEach(({frameId, parentFrameId, url}) => {
|
||||||
|
if (frameId) {
|
||||||
|
frameUrls[frameId] = url === 'about:blank' ? frameUrls[parentFrameId] : url;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const NTP = 'chrome://newtab/';
|
const NTP = 'chrome://newtab/';
|
||||||
const ALL_URLS = '<all_urls>';
|
const ALL_URLS = '<all_urls>';
|
||||||
const contentScripts = chrome.runtime.getManifest().content_scripts;
|
const contentScripts = chrome.runtime.getManifest().content_scripts;
|
||||||
|
contentScripts.push({
|
||||||
|
js: ['content/apply.js'],
|
||||||
|
matches: ['<all_urls>'],
|
||||||
|
run_at: 'document_start',
|
||||||
|
match_about_blank: true,
|
||||||
|
all_frames: true
|
||||||
|
});
|
||||||
|
|
||||||
// expand * as .*?
|
// expand * as .*?
|
||||||
const wildcardAsRegExp = (s, flags) => new RegExp(
|
const wildcardAsRegExp = (s, flags) => new RegExp(
|
||||||
s.replace(/[{}()[\]/\\.+?^$:=!|]/g, '\\$&')
|
s.replace(/[{}()[\]/\\.+?^$:=!|]/g, '\\$&')
|
||||||
|
@ -209,13 +255,9 @@ window.addEventListener('storageReady', function _() {
|
||||||
};
|
};
|
||||||
|
|
||||||
queryTabs().then(tabs =>
|
queryTabs().then(tabs =>
|
||||||
tabs.forEach(tab => {
|
tabs.forEach(tab => tab.width &&
|
||||||
// skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF
|
|
||||||
if (!FIREFOX || tab.width) {
|
|
||||||
contentScripts.forEach(cs =>
|
contentScripts.forEach(cs =>
|
||||||
setTimeout(pingCS, 0, cs, tab));
|
setTimeout(pingCS, 0, cs, tab))));
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
@ -243,32 +285,60 @@ function webNavigationListener(method, {url, tabId, frameId}) {
|
||||||
|
|
||||||
|
|
||||||
function webNavigationListenerChrome(method, data) {
|
function webNavigationListenerChrome(method, data) {
|
||||||
|
const {tabId, frameId, url} = data;
|
||||||
|
if (url.startsWith('https://www.google.') && url.includes('/_/chrome/newtab?')) {
|
||||||
// Chrome 61.0.3161+ doesn't run content scripts on NTP
|
// Chrome 61.0.3161+ doesn't run content scripts on NTP
|
||||||
if (
|
getTab(tabId).then(tab => {
|
||||||
!data.url.startsWith('https://www.google.') ||
|
data.url = tab.url === 'chrome://newtab/' ? tab.url : url;
|
||||||
!data.url.includes('/_/chrome/newtab?')
|
webNavigationListener(method, data);
|
||||||
) {
|
});
|
||||||
|
} else {
|
||||||
|
webNavigationListener(method, data);
|
||||||
|
// chrome.declarativeContent doesn't inject scripts in about:blank iframes
|
||||||
|
if (method && frameId && url === 'about:blank') {
|
||||||
|
chrome.tabs.executeScript(tabId, {
|
||||||
|
file: '/content/apply.js',
|
||||||
|
runAt: 'document_start',
|
||||||
|
matchAboutBlank: true,
|
||||||
|
frameId,
|
||||||
|
}, ignoreChromeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function webNavigationListenerFF(method, data) {
|
||||||
|
const {tabId, frameId, url} = data;
|
||||||
|
//console.log(method, data);
|
||||||
|
if (frameId === 0 || url !== 'about:blank') {
|
||||||
|
if ((!method || method === 'styleApply') &&
|
||||||
|
styleViaAPI.getFrameUrl(tabId, frameId) !== url) {
|
||||||
|
styleViaAPI.cache.delete(tabId);
|
||||||
|
}
|
||||||
|
styleViaAPI.setFrameUrl(tabId, frameId, url);
|
||||||
webNavigationListener(method, data);
|
webNavigationListener(method, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getTab(data.tabId).then(tab => {
|
//const frames = styleViaAPI.allFrameUrls.get(tabId);
|
||||||
if (tab.url === 'chrome://newtab/') {
|
//if (Object.keys(frames).length === 1) {
|
||||||
data.url = tab.url;
|
// frames[frameId] = frames['0'];
|
||||||
}
|
// webNavigationListener(method, data);
|
||||||
webNavigationListener(method, data);
|
// return;
|
||||||
});
|
//}
|
||||||
|
//chrome.webNavigation.getFrame({tabId, frameId}, info => {
|
||||||
|
// const hasParent = !chrome.runtime.lastError && info.parentFrameId >= 0;
|
||||||
|
// frames[frameId] = hasParent ? frames[info.parentFrameId] : url;
|
||||||
|
// webNavigationListener(method, data);
|
||||||
|
//});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function webNavUsercssInstallerFF(data) {
|
function webNavUsercssInstallerFF(data) {
|
||||||
const {tabId} = data;
|
const {tabId} = data;
|
||||||
Promise.all([
|
|
||||||
sendMessage({tabId, method: 'ping'}),
|
|
||||||
// we need tab index to open the installer next to the original one
|
// we need tab index to open the installer next to the original one
|
||||||
// and also to skip the double-invocation in FF which assigns tab url later
|
// and also to skip the double-invocation in FF which assigns tab url later
|
||||||
getTab(tabId),
|
getTab(tabId).then(tab => {
|
||||||
]).then(([pong, tab]) => {
|
if (tab.url !== 'about:blank') {
|
||||||
if (pong !== true && tab.url !== 'about:blank') {
|
|
||||||
usercssHelper.openInstallPage(tab, {direct: true});
|
usercssHelper.openInstallPage(tab, {direct: true});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -367,10 +437,6 @@ function onRuntimeMessage(request, sender, sendResponseInternal) {
|
||||||
.catch(() => sendResponse(false));
|
.catch(() => sendResponse(false));
|
||||||
return KEEP_CHANNEL_OPEN;
|
return KEEP_CHANNEL_OPEN;
|
||||||
|
|
||||||
case 'styleViaAPI':
|
|
||||||
styleViaAPI(request, sender);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 'download':
|
case 'download':
|
||||||
download(request.url)
|
download(request.url)
|
||||||
.then(sendResponse)
|
.then(sendResponse)
|
||||||
|
|
|
@ -1,230 +1,220 @@
|
||||||
/* global getStyles */
|
/* global getStyles */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const styleViaAPI = !CHROME && (() => {
|
// eslint-disable-next-line no-var
|
||||||
|
var styleViaAPI = !CHROME &&
|
||||||
|
(() => {
|
||||||
const ACTIONS = {
|
const ACTIONS = {
|
||||||
styleApply,
|
styleApply,
|
||||||
styleDeleted,
|
styleDeleted,
|
||||||
styleUpdated,
|
styleUpdated,
|
||||||
styleAdded,
|
styleAdded,
|
||||||
styleReplaceAll,
|
styleReplaceAll: styleApply,
|
||||||
prefChanged,
|
prefChanged,
|
||||||
|
ping,
|
||||||
};
|
};
|
||||||
const NOP = Promise.resolve(new Error('NOP'));
|
const NOP = Promise.resolve(new Error('NOP'));
|
||||||
const onError = () => {};
|
const PONG = Promise.resolve(true);
|
||||||
|
const onError = () => NOP;
|
||||||
|
|
||||||
/* <tabId>: Object
|
|
||||||
<frameId>: Object
|
|
||||||
url: String, non-enumerable
|
|
||||||
<styleId>: Array of strings
|
|
||||||
section code */
|
|
||||||
const cache = new Map();
|
const cache = new Map();
|
||||||
|
const allFrameUrls = new Map();
|
||||||
|
|
||||||
let observingTabs = false;
|
chrome.tabs.onRemoved.addListener(onTabRemoved);
|
||||||
|
chrome.tabs.onReplaced.addListener(onTabReplaced);
|
||||||
|
|
||||||
return (request, sender) => {
|
return {
|
||||||
const action = ACTIONS[request.action];
|
process,
|
||||||
return !action ? NOP :
|
getFrameUrl,
|
||||||
action(request, sender)
|
setFrameUrl,
|
||||||
.catch(onError)
|
allFrameUrls,
|
||||||
.then(maybeToggleObserver);
|
cache,
|
||||||
};
|
};
|
||||||
|
|
||||||
function styleApply({id = null, ignoreUrlCheck}, {tab, frameId, url}) {
|
//region public methods
|
||||||
if (prefs.get('disableAll')) {
|
|
||||||
|
function process(request, sender) {
|
||||||
|
console.log(request.action || request.method, request.prefs || request.styles || request.style, sender.tab, sender.frameId);
|
||||||
|
const action = ACTIONS[request.action || request.method];
|
||||||
|
if (!action) {
|
||||||
return NOP;
|
return NOP;
|
||||||
}
|
}
|
||||||
const {tabFrames, frameStyles} = getCachedData(tab.id, frameId);
|
const {tab} = sender;
|
||||||
if (id === null && !ignoreUrlCheck && frameStyles.url === url) {
|
if (!isNaN(sender.frameId)) {
|
||||||
return NOP;
|
const result = action(request, sender);
|
||||||
|
return result ? result.catch(onError) : NOP;
|
||||||
}
|
}
|
||||||
return getStyles({id, matchUrl: url, enabled: true, asHash: true}).then(styles => {
|
return browser.webNavigation.getAllFrames({tabId: tab.id}).then(frames =>
|
||||||
const tasks = [];
|
Promise.all((frames || []).map(({frameId}) =>
|
||||||
for (const styleId in styles) {
|
(action(request, {tab, frameId}) || NOP).catch(onError)))
|
||||||
if (isNaN(parseInt(styleId))) {
|
).catch(onError);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// shallow-extract code from the sections array in order to reuse references
|
|
||||||
// in other places whereas the combined string gets garbage-collected
|
function getFrameUrl(tabId, frameId = 0) {
|
||||||
const styleSections = styles[styleId].map(section => section.code);
|
const frameUrls = allFrameUrls.get(tabId);
|
||||||
const code = styleSections.join('\n');
|
return frameUrls && frameUrls[frameId] || '';
|
||||||
if (!code) {
|
|
||||||
delete frameStyles[styleId];
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (code === (frameStyles[styleId] || []).join('\n')) {
|
|
||||||
continue;
|
function setFrameUrl(tabId, frameId, url) {
|
||||||
|
const frameUrls = allFrameUrls.get(tabId);
|
||||||
|
if (frameUrls) {
|
||||||
|
frameUrls[frameId] = url;
|
||||||
|
} else {
|
||||||
|
allFrameUrls.set(tabId, {[frameId]: url});
|
||||||
}
|
}
|
||||||
frameStyles[styleId] = styleSections;
|
|
||||||
tasks.push(
|
|
||||||
browser.tabs.insertCSS(tab.id, {
|
|
||||||
code,
|
|
||||||
frameId,
|
|
||||||
runAt: 'document_start',
|
|
||||||
matchAboutBlank: true,
|
|
||||||
}).catch(onError));
|
|
||||||
}
|
}
|
||||||
if (!removeFrameIfEmpty(tab.id, frameId, tabFrames, frameStyles)) {
|
|
||||||
Object.defineProperty(frameStyles, 'url', {value: url, configurable: true});
|
//endregion
|
||||||
tabFrames[frameId] = frameStyles;
|
//region actions
|
||||||
cache.set(tab.id, tabFrames);
|
|
||||||
|
function styleApply({styles, disableAll}, sender) {
|
||||||
|
if (disableAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {tab: {id: tabId}, frameId, url} = sender;
|
||||||
|
if (!styles || styles === 'DIY') {
|
||||||
|
return requestStyles({matchUrl: url || getFrameUrl(tabId, frameId)}, sender);
|
||||||
|
}
|
||||||
|
const {tabFrames, frameStyles} = getCachedData(tabId, frameId);
|
||||||
|
const newSorted = getSortedById(styles);
|
||||||
|
if (!sameArrays(frameStyles, newSorted, sameArrays)) {
|
||||||
|
tabFrames[frameId] = newSorted;
|
||||||
|
cache.set(tabId, tabFrames);
|
||||||
|
return replaceCSS(tabId, frameId, frameStyles, newSorted);
|
||||||
}
|
}
|
||||||
return Promise.all(tasks);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function styleDeleted({id}, {tab, frameId}) {
|
function styleDeleted({id}, {tab, frameId}) {
|
||||||
const {tabFrames, frameStyles, styleSections} = getCachedData(tab.id, frameId, id);
|
const {frameStyles} = getCachedData(tab.id, frameId);
|
||||||
const code = styleSections.join('\n');
|
const index = frameStyles.findIndex(item => item.id === id);
|
||||||
if (code && !duplicateCodeExists({frameStyles, id, code})) {
|
if (index >= 0) {
|
||||||
delete frameStyles[id];
|
const oldStyles = frameStyles.slice();
|
||||||
removeFrameIfEmpty(tab.id, frameId, tabFrames, frameStyles);
|
frameStyles.splice(index, 1);
|
||||||
return removeCSS(tab.id, frameId, code);
|
return replaceCSS(tab.id, frameId, oldStyles, frameStyles);
|
||||||
} else {
|
|
||||||
return NOP;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function styleUpdated({style}, sender) {
|
function styleUpdated({style}, sender) {
|
||||||
if (!style.enabled) {
|
return (style.enabled ? styleApply : styleDeleted)(style, sender);
|
||||||
return styleDeleted(style, sender);
|
|
||||||
}
|
|
||||||
const {tab, frameId} = sender;
|
|
||||||
const {frameStyles, styleSections} = getCachedData(tab.id, frameId, style.id);
|
|
||||||
const code = styleSections.join('\n');
|
|
||||||
return styleApply(style, sender).then(code && (() => {
|
|
||||||
if (!duplicateCodeExists({frameStyles, code, id: null})) {
|
|
||||||
return removeCSS(tab.id, frameId, code);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function styleAdded({style}, sender) {
|
function styleAdded({style: {enabled}}, sender) {
|
||||||
return style.enabled ? styleApply(style, sender) : NOP;
|
return enabled && styleApply({}, sender);
|
||||||
}
|
|
||||||
|
|
||||||
function styleReplaceAll(request, sender) {
|
|
||||||
const {tab, frameId} = sender;
|
|
||||||
const oldStylesCode = getFrameStylesJoined(sender);
|
|
||||||
return styleApply({ignoreUrlCheck: true}, sender).then(() => {
|
|
||||||
const newStylesCode = getFrameStylesJoined(sender);
|
|
||||||
const tasks = oldStylesCode
|
|
||||||
.filter(code => !newStylesCode.includes(code))
|
|
||||||
.map(code => removeCSS(tab.id, frameId, code));
|
|
||||||
return Promise.all(tasks);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function prefChanged({prefs}, sender) {
|
function prefChanged({prefs}, sender) {
|
||||||
if ('disableAll' in prefs) {
|
if ('disableAll' in prefs) {
|
||||||
if (!prefs.disableAll) {
|
disableAll(prefs.disableAll, sender);
|
||||||
return styleApply({}, sender);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ping() {
|
||||||
|
return PONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region action helpers
|
||||||
|
|
||||||
|
function disableAll(state, sender) {
|
||||||
|
if (state) {
|
||||||
const {tab, frameId} = sender;
|
const {tab, frameId} = sender;
|
||||||
const {tabFrames, frameStyles} = getCachedData(tab.id, frameId);
|
const {tabFrames, frameStyles} = getCachedData(tab.id, frameId);
|
||||||
if (isEmpty(frameStyles)) {
|
|
||||||
return NOP;
|
|
||||||
}
|
|
||||||
removeFrameIfEmpty(tab.id, frameId, tabFrames, {});
|
|
||||||
const tasks = Object.keys(frameStyles)
|
|
||||||
.map(id => removeCSS(tab.id, frameId, frameStyles[id].join('\n')));
|
|
||||||
return Promise.all(tasks);
|
|
||||||
} else {
|
|
||||||
return NOP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* utilities */
|
|
||||||
|
|
||||||
function maybeToggleObserver() {
|
|
||||||
let method;
|
|
||||||
if (!observingTabs && cache.size) {
|
|
||||||
method = 'addListener';
|
|
||||||
} else if (observingTabs && !cache.size) {
|
|
||||||
method = 'removeListener';
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
observingTabs = !observingTabs;
|
|
||||||
chrome.webNavigation.onCommitted[method](onNavigationCommitted);
|
|
||||||
chrome.tabs.onRemoved[method](onTabRemoved);
|
|
||||||
chrome.tabs.onReplaced[method](onTabReplaced);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onNavigationCommitted({tabId, frameId}) {
|
|
||||||
if (frameId === 0) {
|
|
||||||
onTabRemoved(tabId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tabFrames = cache.get(tabId);
|
|
||||||
if (frameId in tabFrames) {
|
|
||||||
delete tabFrames[frameId];
|
delete tabFrames[frameId];
|
||||||
if (isEmpty(tabFrames)) {
|
return removeCSS(tab.id, frameId, frameStyles);
|
||||||
onTabRemoved(tabId);
|
} else {
|
||||||
}
|
return styleApply({}, sender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region observer
|
||||||
|
|
||||||
function onTabRemoved(tabId) {
|
function onTabRemoved(tabId) {
|
||||||
cache.delete(tabId);
|
cache.delete(tabId);
|
||||||
maybeToggleObserver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTabReplaced(addedTabId, removedTabId) {
|
function onTabReplaced(addedTabId, removedTabId) {
|
||||||
onTabRemoved(removedTabId);
|
cache.delete(removedTabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFrameIfEmpty(tabId, frameId, tabFrames, frameStyles) {
|
//endregion
|
||||||
if (isEmpty(frameStyles)) {
|
//region browser API
|
||||||
delete tabFrames[frameId];
|
|
||||||
if (isEmpty(tabFrames)) {
|
function replaceCSS(tabId, frameId, oldStyles, newStyles) {
|
||||||
cache.delete(tabId);
|
console.log.apply(null, arguments);
|
||||||
|
return insertCSS(tabId, frameId, newStyles).then(() =>
|
||||||
|
removeCSS(tabId, frameId, oldStyles));
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
function insertCSS(tabId, frameId, frameStyles) {
|
||||||
|
const code = getFrameCode(frameStyles);
|
||||||
|
return !code ? NOP :
|
||||||
|
browser.tabs.insertCSS(tabId, {
|
||||||
|
code,
|
||||||
|
frameId,
|
||||||
|
runAt: 'document_start',
|
||||||
|
matchAboutBlank: true,
|
||||||
|
}).catch(onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeCSS(tabId, frameId, frameStyles) {
|
||||||
|
const code = getFrameCode(frameStyles);
|
||||||
|
return !code ? NOP :
|
||||||
|
browser.tabs.removeCSS(tabId, {
|
||||||
|
code,
|
||||||
|
frameId,
|
||||||
|
matchAboutBlank: true
|
||||||
|
}).catch(onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region utilities
|
||||||
|
|
||||||
|
function requestStyles(options, sender) {
|
||||||
|
options.matchUrl = options.matchUrl || sender.url;
|
||||||
|
options.enabled = true;
|
||||||
|
options.asHash = true;
|
||||||
|
return getStyles(options).then(styles =>
|
||||||
|
styleApply({styles}, sender));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSortedById(styleHash) {
|
||||||
|
const styles = [];
|
||||||
|
let needsSorting = false;
|
||||||
|
let prevKey = -1;
|
||||||
|
for (let k in styleHash) {
|
||||||
|
k = parseInt(k);
|
||||||
|
if (!isNaN(k)) {
|
||||||
|
const sections = styleHash[k].map(({code}) => code);
|
||||||
|
styles.push(sections);
|
||||||
|
defineProperty(sections, 'id', k);
|
||||||
|
needsSorting |= k < prevKey;
|
||||||
|
prevKey = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return needsSorting ? styles.sort((a, b) => a.id - b.id) : styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCachedData(tabId, frameId, styleId) {
|
function getCachedData(tabId, frameId, styleId) {
|
||||||
const tabFrames = cache.get(tabId) || {};
|
const tabFrames = cache.get(tabId) || {};
|
||||||
const frameStyles = tabFrames[frameId] || {};
|
const frameStyles = tabFrames[frameId] || [];
|
||||||
const styleSections = styleId && frameStyles[styleId] || [];
|
const styleSections = styleId && frameStyles.find(s => s.id === styleId) || [];
|
||||||
return {tabFrames, frameStyles, styleSections};
|
return {tabFrames, frameStyles, styleSections};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFrameStylesJoined({
|
function getFrameCode(frameStyles) {
|
||||||
tab,
|
// we cache a shallow copy of code from the sections array in order to reuse references
|
||||||
frameId,
|
// in other places whereas the combined string gets garbage-collected
|
||||||
frameStyles = getCachedData(tab.id, frameId).frameStyles,
|
return typeof frameStyles === 'string' ? frameStyles : [].concat(...frameStyles).join('\n');
|
||||||
}) {
|
|
||||||
return Object.keys(frameStyles).map(id => frameStyles[id].join('\n'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function duplicateCodeExists({
|
function defineProperty(obj, name, value) {
|
||||||
tab,
|
return Object.defineProperty(obj, name, {value, configurable: true});
|
||||||
frameId,
|
|
||||||
frameStyles = getCachedData(tab.id, frameId).frameStyles,
|
|
||||||
frameStylesCode = {},
|
|
||||||
id,
|
|
||||||
code = frameStylesCode[id] || frameStyles[id].join('\n'),
|
|
||||||
}) {
|
|
||||||
id = String(id);
|
|
||||||
for (const styleId in frameStyles) {
|
|
||||||
if (id !== styleId &&
|
|
||||||
code === (frameStylesCode[styleId] || frameStyles[styleId].join('\n'))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCSS(tabId, frameId, code) {
|
function sameArrays(a, b, fn) {
|
||||||
return browser.tabs.removeCSS(tabId, {frameId, code, matchAboutBlank: true})
|
return a.length === b.length && a.every((el, i) => fn ? fn(el, b[i]) : el === b[i]);
|
||||||
.catch(onError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmpty(obj) {
|
//endregion
|
||||||
for (const k in obj) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -23,10 +23,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestStyles(options, callback = applyStyles) {
|
function requestStyles(options, callback = applyStyles) {
|
||||||
if (!chrome.app && document instanceof XMLDocument) {
|
|
||||||
chrome.runtime.sendMessage({method: 'styleViaAPI', action: 'styleApply'});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var matchUrl = location.href;
|
var matchUrl = location.href;
|
||||||
if (!matchUrl.match(/^(http|file|chrome|ftp)/)) {
|
if (!matchUrl.match(/^(http|file|chrome|ftp)/)) {
|
||||||
// dynamic about: and javascript: iframes don't have an URL yet
|
// dynamic about: and javascript: iframes don't have an URL yet
|
||||||
|
@ -63,17 +59,6 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chrome.app && document instanceof XMLDocument && request.method !== 'ping') {
|
|
||||||
request.action = request.method;
|
|
||||||
request.method = 'styleViaAPI';
|
|
||||||
request.styles = null;
|
|
||||||
if (request.style) {
|
|
||||||
request.style.sections = null;
|
|
||||||
}
|
|
||||||
chrome.runtime.sendMessage(request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case 'styleDeleted':
|
case 'styleDeleted':
|
||||||
removeStyle(request);
|
removeStyle(request);
|
||||||
|
|
|
@ -150,6 +150,13 @@ function sendMessage(msg, callback) {
|
||||||
- enabled by passing a second param
|
- enabled by passing a second param
|
||||||
*/
|
*/
|
||||||
const {tabId, frameId} = msg;
|
const {tabId, frameId} = msg;
|
||||||
|
if (tabId >= 0 && FIREFOX) {
|
||||||
|
// FF: reroute all tabs messages to styleViaAPI
|
||||||
|
const msgForBG = BG === window ? msg : BG.deepCopy(msg);
|
||||||
|
const sender = {tab: {id: tabId}, frameId};
|
||||||
|
const task = BG.styleViaAPI.process(msgForBG, sender);
|
||||||
|
return callback ? task.then(callback) : task;
|
||||||
|
}
|
||||||
const fn = tabId >= 0 ? chrome.tabs.sendMessage : chrome.runtime.sendMessage;
|
const fn = tabId >= 0 ? chrome.tabs.sendMessage : chrome.runtime.sendMessage;
|
||||||
const args = tabId >= 0 ? [tabId, msg, {frameId}] : [msg];
|
const args = tabId >= 0 ? [tabId, msg, {frameId}] : [msg];
|
||||||
if (callback) {
|
if (callback) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"webNavigation",
|
"webNavigation",
|
||||||
"contextMenus",
|
"contextMenus",
|
||||||
"storage",
|
"storage",
|
||||||
|
"declarativeContent",
|
||||||
"<all_urls>"
|
"<all_urls>"
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
|
@ -43,13 +44,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
|
||||||
"matches": ["<all_urls>"],
|
|
||||||
"run_at": "document_start",
|
|
||||||
"all_frames": true,
|
|
||||||
"match_about_blank": true,
|
|
||||||
"js": ["content/apply.js"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],
|
"matches": ["http://userstyles.org/*", "https://userstyles.org/*"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user