* API.styles.* * API.usercss.* * API.sync.* * API.worker.* * API.updater.* * simplify db: resolve with result * remove API.download * simplify download() * remove noCode param as it wastes more time/memory on copying * styleManager: switch style<->data names to reflect their actual contents * inline method bodies to avoid indirection and enable better autocomplete/hint/jump support in IDE
104 lines
3.2 KiB
JavaScript
104 lines
3.2 KiB
JavaScript
/* global
|
|
CHROME
|
|
FIREFOX
|
|
ignoreChromeError
|
|
msg
|
|
URLS
|
|
*/
|
|
'use strict';
|
|
|
|
(() => {
|
|
/** @type {Set<function(data: Object, type: string)>} */
|
|
const listeners = new Set();
|
|
/** @type {NavigatorUtil} */
|
|
const navigatorUtil = window.navigatorUtil = new Proxy({
|
|
onUrlChange(fn) {
|
|
listeners.add(fn);
|
|
},
|
|
}, {
|
|
get(target, prop) {
|
|
return target[prop] ||
|
|
(target = chrome.webNavigation[prop]).addListener.bind(target);
|
|
},
|
|
});
|
|
|
|
navigatorUtil.onCommitted(onNavigation.bind('committed'));
|
|
navigatorUtil.onHistoryStateUpdated(onFakeNavigation.bind('history'));
|
|
navigatorUtil.onReferenceFragmentUpdated(onFakeNavigation.bind('hash'));
|
|
navigatorUtil.onCommitted(runGreasyforkContentScript, {
|
|
// expose style version on greasyfork/sleazyfork 1) info page and 2) code page
|
|
url: ['greasyfork', 'sleazyfork'].map(host => ({
|
|
hostEquals: host + '.org',
|
|
urlMatches: '/scripts/\\d+[^/]*(/code)?([?#].*)?$',
|
|
})),
|
|
});
|
|
if (FIREFOX) {
|
|
navigatorUtil.onDOMContentLoaded(runMainContentScripts, {
|
|
url: [{
|
|
urlEquals: 'about:blank',
|
|
}],
|
|
});
|
|
}
|
|
|
|
/** @this {string} type */
|
|
async function onNavigation(data) {
|
|
if (CHROME &&
|
|
URLS.chromeProtectsNTP &&
|
|
data.url.startsWith('https://www.google.') &&
|
|
data.url.includes('/_/chrome/newtab?')) {
|
|
// Modern Chrome switched to WebUI NTP so this is obsolete, but there may be exceptions
|
|
// TODO: investigate, and maybe use a separate listener for CHROME <= ver
|
|
const tab = await browser.tabs.get(data.tabId);
|
|
const url = tab.pendingUrl || tab.url;
|
|
if (url === 'chrome://newtab/') {
|
|
data.url = url;
|
|
}
|
|
}
|
|
listeners.forEach(fn => fn(data, this));
|
|
}
|
|
|
|
/** @this {string} type */
|
|
function onFakeNavigation(data) {
|
|
onNavigation.call(this, data);
|
|
msg.sendTab(data.tabId, {method: 'urlChanged'}, {frameId: data.frameId})
|
|
.catch(msg.ignoreError);
|
|
}
|
|
|
|
/** FF misses some about:blank iframes so we inject our content script explicitly */
|
|
async function runMainContentScripts({tabId, frameId}) {
|
|
if (frameId &&
|
|
!await msg.sendTab(tabId, {method: 'ping'}, {frameId}).catch(ignoreChromeError)) {
|
|
for (const file of chrome.runtime.getManifest().content_scripts[0].js) {
|
|
chrome.tabs.executeScript(tabId, {
|
|
frameId,
|
|
file,
|
|
matchAboutBlank: true,
|
|
}, ignoreChromeError);
|
|
}
|
|
}
|
|
}
|
|
|
|
function runGreasyforkContentScript({tabId}) {
|
|
chrome.tabs.executeScript(tabId, {
|
|
file: '/content/install-hook-greasyfork.js',
|
|
runAt: 'document_start',
|
|
});
|
|
}
|
|
})();
|
|
|
|
/**
|
|
* @typedef NavigatorUtil
|
|
* @property {NavigatorUtilEvent} onBeforeNavigate
|
|
* @property {NavigatorUtilEvent} onCommitted
|
|
* @property {NavigatorUtilEvent} onCompleted
|
|
* @property {NavigatorUtilEvent} onCreatedNavigationTarget
|
|
* @property {NavigatorUtilEvent} onDOMContentLoaded
|
|
* @property {NavigatorUtilEvent} onErrorOccurred
|
|
* @property {NavigatorUtilEvent} onHistoryStateUpdated
|
|
* @property {NavigatorUtilEvent} onReferenceFragmentUpdated
|
|
* @property {NavigatorUtilEvent} onTabReplaced
|
|
*/
|
|
/**
|
|
* @typedef {function(cb: function, filters: WebNavigationEventFilter?)} NavigatorUtilEvent
|
|
*/
|