MSFX/chrome/utils/userChrome.jsm

222 lines
8.1 KiB
JavaScript

let EXPORTED_SYMBOLS = [];
const Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
const { xPref } = ChromeUtils.import('chrome://userchromejs/content/xPref.jsm');
const { Management } = ChromeUtils.import('resource://gre/modules/Extension.jsm');
const { AppConstants } = ChromeUtils.import('resource://gre/modules/AppConstants.jsm');
let UC = {
webExts: new Map(),
sidebar: new Map()
};
let _uc = {
ALWAYSEXECUTE: 'rebuild_userChrome.uc.js',
BROWSERCHROME: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'chrome://messenger/content/messenger.xhtml' : 'chrome://browser/content/browser.xhtml',
BROWSERTYPE: AppConstants.MOZ_APP_NAME == 'thunderbird' ? 'mail:3pane' : 'navigator:browser',
BROWSERNAME: AppConstants.MOZ_APP_NAME.charAt(0).toUpperCase() + AppConstants.MOZ_APP_NAME.slice(1),
PREF_ENABLED: 'userChromeJS.enabled',
PREF_SCRIPTSDISABLED: 'userChromeJS.scriptsDisabled',
chromedir: Services.dirsvc.get('UChrm', Ci.nsIFile),
scriptsDir: '',
sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
getScripts: function () {
this.scripts = {};
let files = this.chromedir.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator);
while (files.hasMoreElements()) {
let file = files.getNext().QueryInterface(Ci.nsIFile);
if (/\.uc\.js$/i.test(file.leafName)) {
_uc.getScriptData(file);
}
}
},
getScriptData: function (aFile) {
let aContent = this.readFile(aFile);
let header = (aContent.match(/^\/\/ ==UserScript==\s*\n(?:.*\n)*?\/\/ ==\/UserScript==\s*\n/m) || [''])[0];
let match, rex = {
include: [],
exclude: []
};
let findNextRe = /^\/\/ @(include|exclude)\s+(.+)\s*$/gm;
while ((match = findNextRe.exec(header))) {
rex[match[1]].push(match[2].replace(/^main$/i, _uc.BROWSERCHROME).replace(/\*/g, '.*?'));
}
if (!rex.include.length) {
rex.include.push(_uc.BROWSERCHROME);
}
let exclude = rex.exclude.length ? '(?!' + rex.exclude.join('$|') + '$)' : '';
let def = ['', ''];
let author = (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1];
let filename = aFile.leafName || '';
return this.scripts[filename] = {
filename: filename,
file: aFile,
url: Services.io.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromDir(this.chromedir) + filename,
name: (header.match(/\/\/ @name\s+(.+)\s*$/im) || def)[1],
description: (header.match(/\/\/ @description\s+(.+)\s*$/im) || def)[1],
version: (header.match(/\/\/ @version\s+(.+)\s*$/im) || def)[1],
author: (header.match(/\/\/ @author\s+(.+)\s*$/im) || def)[1],
regex: new RegExp('^' + exclude + '(' + (rex.include.join('|') || '.*') + ')$', 'i'),
id: (header.match(/\/\/ @id\s+(.+)\s*$/im) || ['', filename.split('.uc.js')[0] + '@' + (author || 'userChromeJS')])[1],
homepageURL: (header.match(/\/\/ @homepageURL\s+(.+)\s*$/im) || def)[1],
downloadURL: (header.match(/\/\/ @downloadURL\s+(.+)\s*$/im) || def)[1],
updateURL: (header.match(/\/\/ @updateURL\s+(.+)\s*$/im) || def)[1],
optionsURL: (header.match(/\/\/ @optionsURL\s+(.+)\s*$/im) || def)[1],
startup: (header.match(/\/\/ @startup\s+(.+)\s*$/im) || def)[1],
shutdown: (header.match(/\/\/ @shutdown\s+(.+)\s*$/im) || def)[1],
onlyonce: /\/\/ @onlyonce\b/.test(header),
isRunning: false,
get isEnabled() {
return (xPref.get(_uc.PREF_SCRIPTSDISABLED) || '').split(',').indexOf(this.filename) == -1;
}
}
},
readFile: function (aFile, metaOnly = false) {
let stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
stream.init(aFile, 0x01, 0, 0);
let cvstream = Cc['@mozilla.org/intl/converter-input-stream;1'].createInstance(Ci.nsIConverterInputStream);
cvstream.init(stream, 'UTF-8', 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
let content = '',
data = {};
while (cvstream.readString(4096, data)) {
content += data.value;
if (metaOnly && content.indexOf('// ==/UserScript==') > 0) {
break;
}
}
cvstream.close();
return content.replace(/\r\n?/g, '\n');
},
everLoaded: [],
loadScript: function (script, win) {
if (!script.regex.test(win.location.href) || (script.filename != this.ALWAYSEXECUTE && !script.isEnabled)) {
return;
}
if (script.onlyonce && script.isRunning) {
if (script.startup) {
eval(script.startup);
}
return;
}
try {
Services.scriptloader.loadSubScript(script.url + '?' + script.file.lastModifiedTime,
script.onlyonce ? { window: win } : win);
script.isRunning = true;
if (script.startup) {
eval(script.startup);
}
if (!script.shutdown) {
this.everLoaded.push(script.id);
}
} catch (ex) {
Cu.reportError(ex);
}
},
windows: function (fun, onlyBrowsers = true) {
let windows = Services.wm.getEnumerator(onlyBrowsers ? this.BROWSERTYPE : null);
while (windows.hasMoreElements()) {
let win = windows.getNext();
if (!win._uc)
continue;
if (!onlyBrowsers) {
let frames = win.docShell.getAllDocShellsInSubtree(Ci.nsIDocShellTreeItem.typeAll, Ci.nsIDocShell.ENUMERATE_FORWARDS);
let res = frames.some(frame => {
let fWin = frame.domWindow;
let {document, location} = fWin;
if (fun(document, fWin, location))
return true;
});
if (res)
break;
} else {
let {document, location} = win;
if (fun(document, win, location))
break;
}
}
},
createElement: function (doc, tag, atts, XUL = true) {
let el = XUL ? doc.createXULElement(tag) : doc.createElement(tag);
for (let att in atts) {
el.setAttribute(att, atts[att]);
}
return el
}
};
if (xPref.get(_uc.PREF_ENABLED) === undefined) {
xPref.set(_uc.PREF_ENABLED, true, true);
}
if (xPref.get(_uc.PREF_SCRIPTSDISABLED) === undefined) {
xPref.set(_uc.PREF_SCRIPTSDISABLED, '', true);
}
let UserChrome_js = {
observe: function (aSubject) {
aSubject.addEventListener('DOMContentLoaded', this, {once: true});
},
handleEvent: function (aEvent) {
let document = aEvent.originalTarget;
let window = document.defaultView;
let location = window.location;
if (!this.sharedWindowOpened && location.href == 'chrome://extensions/content/dummy.xhtml') {
this.sharedWindowOpened = true;
Management.on('extension-browser-inserted', function (topic, browser) {
browser.messageManager.addMessageListener('Extension:ExtensionViewLoaded', this.messageListener.bind(this));
}.bind(this));
} else if (/^(chrome:(?!\/\/global\/content\/commonDialog\.x?html)|about:(?!blank))/i.test(location.href)) {
window.UC = UC;
window._uc = _uc;
window.xPref = xPref;
if (window._gBrowser) // bug 1443849
window.gBrowser = window._gBrowser;
if (xPref.get(_uc.PREF_ENABLED)) {
Object.values(_uc.scripts).forEach(script => {
_uc.loadScript(script, window);
});
} else if (!UC.rebuild) {
_uc.loadScript(_uc.scripts[_uc.ALWAYSEXECUTE], window);
}
}
},
messageListener: function (msg) {
const browser = msg.target;
const { addonId } = browser._contentPrincipal;
browser.messageManager.removeMessageListener('Extension:ExtensionViewLoaded', this.messageListener);
if (browser.ownerGlobal.location.href == 'chrome://extensions/content/dummy.xhtml') {
UC.webExts.set(addonId, browser);
Services.obs.notifyObservers(null, 'UCJS:WebExtLoaded', addonId);
} else {
let win = browser.ownerGlobal.windowRoot.ownerGlobal;
UC.sidebar.get(addonId)?.set(win, browser) || UC.sidebar.set(addonId, new Map([[win, browser]]));
Services.obs.notifyObservers(win, 'UCJS:SidebarLoaded', addonId);
}
}
};
if (!Services.appinfo.inSafeMode) {
_uc.chromedir.append(_uc.scriptsDir);
_uc.getScripts();
Services.obs.addObserver(UserChrome_js, 'chrome-document-global-created', false);
}