From 9711de94638139b54bc31588e5fbb60a25e1cac6 Mon Sep 17 00:00:00 2001 From: Niklas Gollenstede Date: Sun, 26 May 2019 14:29:57 +0200 Subject: [PATCH] handle toolkit.legacyUserProfileCustomizations.stylesheets (close #22) --- background/chrome/index.js | 2 +- background/chrome/native.js | 63 ++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/background/chrome/index.js b/background/chrome/index.js index 64a7d77..ca2e345 100644 --- a/background/chrome/index.js +++ b/background/chrome/index.js @@ -135,7 +135,7 @@ async function writeStyles(process, clear, replace) { loaded[key] = extract(loaded[key].replace(/\r\n?/g, '\n')); }); } } - (await native.write(files, replace)); + (await native.write(files, replace, true)); changed = Object.entries((await loaded)).some(([ key, loaded, ]) => written[key] !== loaded); fireWritten([ changed, ]); diff --git a/background/chrome/native.js b/background/chrome/native.js index 5721055..917c0e7 100644 --- a/background/chrome/native.js +++ b/background/chrome/native.js @@ -20,31 +20,70 @@ module.exports = { //// start implementation -const FS = require('fs'), { promisify, } = require('util'), { EOL, } = require('os'); +const FS = require('fs'), { promisify, } = require('util'), { EOL, } = require('os'), VM = require('vm'); const access = promisify(FS.access), mkdir = promisify(FS.mkdir), readFile = promisify(FS.readFile), writeFile = promisify(FS.writeFile); const { profileDir, } = require('browser'); -async function write(files, exp = '.*') { - try { (await access(profileDir)); } catch (_) { throw new Error(`Cant access profile directory in "${ profileDir }"`); } +async function write(files, exp = '.*', enable = false) { + try { (await access(profileDir)); } catch (_) { throw new Error(`Cant access profile directory in "${profileDir}"`); } try { (await mkdir(profileDir +'/chrome')); } catch (_) { } - (await Promise.all([ 'chrome', 'content', ].map(async type => { - const old = (await readSafe(type)); - const css = old.replace(new RegExp(exp +'|$'), () => files[type].replace(/\n/g, EOL)); - - (await writeFile(profileDir +`/chrome/userC${type.slice(1)}.css`, css, 'utf-8')); - }))); + (await Promise.all([ + replaceInFile(cssFilePath('chrome'), exp, files.chrome), + replaceInFile(cssFilePath('content'), exp, files.content), + enable && setUserConfig([ + { name: 'toolkit.legacyUserProfileCustomizations.stylesheets', value: true, comment: 'enforced by reStyle, required to apply browser styles', }, + ]).catch(console.error), + ])); } async function read() { const files = { chrome: '', content: '', }; (await Promise.all(Object.keys(files).map(async type => { - files[type] = (await readSafe(type)); + files[type] = (await readIfExists(cssFilePath(type))); }))); return files; } -async function readSafe(type) { - try { return (await readFile(profileDir +`/chrome/userC${type.slice(1)}.css`, 'utf-8')); } +function cssFilePath(type) { return profileDir +`/chrome/userC${ type.slice(1) }.css`; } + +async function readIfExists(path) { + try { return (await readFile(path, 'utf-8')); } catch (error) { error && error.code !== 'ENOENT' && console.error(error); return ''; } } + +async function replaceInFile(path, exp, content) { + content = (await readIfExists(path)) + .replace(new RegExp(exp +'|$'), () => content.replace(/\n/g, EOL)); + (await writeFile(path, content, 'utf-8')); +} + +/** + * Writes `about:config` values to the current profiles `user.js` file. + * If one or more entry with the same name exists, the first one will be replaced + * and the remaining ones removed. If none existed, the new entry will be appended. + * This function currently doesn't handle multi-line entries or comments properly. + * @param {[object]} prefs Array of objects representing the entries to write: + * @property {string} name Name of the config value. + * @property {string|number|boolean} value New value to write. + * @property {string} comment Single-line comment to append to the new line. + * @throws {TypeError} If the `user.js` file is not valid JavaScript before or after replacing the values. + */ +async function setUserConfig(prefs) { + const path = profileDir +'/user.js'; + let script = tryCompile((await readIfExists(path))); + + for (const { name, value, comment, } of prefs) { + let replaced = false; + script = tryCompile(script.replace(new RegExp( + `\\n\\s*?user_pref\\((["'])${ name.replace(/([.])/g, '[$1]') }\\1.*|$`, 'g' + ), match => { + if (replaced) { return match ? '\n' : ''; } replaced = true; + return `\nuser_pref(${ JSON.stringify(name) }, ${ JSON.stringify(value) });`+ (comment ? ' // '+ comment : ''); + })); + } + + function tryCompile(script) { new VM.Script(script, { filename: 'user.js', }); return script; } + + (await writeFile(path, script, 'utf-8')); +}