From 7d39649dab1285704ce1631c5348dc72a7d2c169 Mon Sep 17 00:00:00 2001 From: Niklas Gollenstede Date: Tue, 5 Jun 2018 14:31:23 -0400 Subject: [PATCH] use native-ext v0.3.0 (2h) --- background/chrome/index.js | 52 ++++++++++++-------------- background/index.js | 11 +----- background/local/index.js | 75 +++++++++++++++++++++----------------- background/style.js | 2 +- build-config.js | 11 +++--- common/options.js | 4 +- package.json | 8 ++-- views/index.js | 12 +++--- views/panel.js | 18 ++++++++- views/setup.js | 64 ++++++++++++++++++++------------ 10 files changed, 143 insertions(+), 114 deletions(-) diff --git a/background/chrome/index.js b/background/chrome/index.js index 285b5c2..0de9bfc 100644 --- a/background/chrome/index.js +++ b/background/chrome/index.js @@ -1,10 +1,8 @@ (function(global) { 'use strict'; define(({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 'node_modules/web-ext-utils/browser/': { manifest, rootUrl, }, - 'node_modules/web-ext-utils/loader/views': { openView, }, 'node_modules/web-ext-utils/utils/': { reportError, }, 'node_modules/web-ext-utils/utils/event': { setEvent, }, 'node_modules/native-ext/': Native, - 'node_modules/es6lib/functional': { debounce, }, 'node_modules/regexpx/': RegExpX, 'common/options': options, '../util': { debounceIdle, }, @@ -24,7 +22,7 @@ class ChromeStyle { // that means they are pretty useless unless they are !important ==> add that to all rules this.chrome = (chrome || '').toString({ minify: false, important: true, namespace: false, }).trim().replace(/\r\n?/g, '\n'); this.content = (content || '').toString({ minify: false, important: true, namespace: false, }).trim().replace(/\r\n?/g, '\n'); - writeStyles(); + applyStyles(); } static get changed() { return changed; } @@ -33,13 +31,13 @@ class ChromeStyle { if (!styles.has(this)) { return; } styles.delete(this); this.code = null; - writeStyles(); + applyStyles(); } toJSON() { return { path: this.path, chrome: this.chrome, content: this.content, }; } static fromJSON({ path, chrome, content, }) { - // writeStyles(); // this is only used to load styles after a restart. This style should not have changed since it was last written. + // applyStyles(); // this is only used to load styles after a restart. This style should not have changed since it was last written. return new ChromeStyle(path, chrome, content); } @@ -78,24 +76,23 @@ function extract(file) { return (rExtract.exec(file) || [ '', ])[0].replace(/^\n?.*\n/, '').replace(/\n.*\n?$/, ''); } -// `load` tries to require the ./native module in node.js, returns `false` if that fails -// `unload` releases the module after a while to allow node.js to quit -let native = null; const load = async () => { - if (native) { return native; } - if (!(await Native.test())) { - reportError(`NativeExt unaviable`, `${manifest.name} could not connect to it's native counterpart. To use chrome styles, please follow the setup instructions.`); - active = options.chrome.value = false; openView('setup'); return null; - } - return (native = (await Native.require(require.resolve('./native')))); -}, unload = debounce(() => { - Native.unref(native); native = null; -}, 60e3); -// `writeStyles` actually writes the styles, but not to frequently and only if `options.chrome` enabled -let current = null, active = options.chrome.value; options.chrome.onChange(([ value, ]) => { active = value; writeStyles(!value); }); -const writeStyles = debounceIdle(async (clear) => { try { +// `applyStyles` actually writes the styles, but not to frequently and only if `options.chrome` enabled +let active = options.chrome.value; options.chrome.onChange(([ value, ]) => { active = value; applyStyles(!value); }); +const applyStyles = debounceIdle(async (clear) => { + if (!(active || clear)) { return; } // this shouldn't be possible + if (clear && !current) { return; } // witched off but didn't work before + + Native.getApplicationName({ stale: true, }).then(name => !name + && reportError('Set up NativeExt', `Applying chrome styles requires NativeExt, but it is not installed or not set up correctly.`) + ); + + Native.do(writeStyles); // deduplicates calls (until started) +}, 1e3); + +let current = null; async function writeStyles(process) { try { + let clear; if (!active) { if (current) { clear = true; } { return; } } - if (!active && (!clear || !current)) { return; } if (!(await load())) { return; } const sorted = clear ? null : Array.from(styles).sort((a, b) => a.path < b.path ? -1 : 1); // TODO: this throws all @namespace declarations into a single file. Is that even supposed to work? Do later (default) declarations overwrite earlier ones? @@ -108,21 +105,18 @@ const writeStyles = debounceIdle(async (clear) => { try { ).join('\n')) + suffix )); - if (!current) { - current = (await native.read()); + const native = (await process.require(require.resolve('./native'))); + if (!current) { current = (await native.read()); { Object.keys(current).forEach(key => { current[key] = extract(current[key].replace(/\r\n?/g, '\n')); }); - } - + } } (await native.write(files, rExtractSource)); changed = Object.entries((await current)).some(([ key, current, ]) => data[key] !== current); - fireWritten([ changed, ]); -} catch (error) { - reportError(`Failed to write chrome styles`, error); -} finally { unload(); } }, 1e3); + +} catch (error) { reportError(`Failed to write chrome styles`, error); } } return ChromeStyle; diff --git a/background/index.js b/background/index.js index 62c3dac..0eb2f0c 100644 --- a/background/index.js +++ b/background/index.js @@ -1,7 +1,6 @@ (function(global) { 'use strict'; define(async ({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 'node_modules/web-ext-utils/browser/': Browser, 'node_modules/web-ext-utils/update/': updated, - 'node_modules/web-ext-utils/utils/': { reportError, }, 'node_modules/native-ext/': Native, 'common/options': options, 'views/': _, // put views in TabView (but doesn't load them yet) @@ -14,17 +13,11 @@ }) => { /** - * This file mostly just requires other files in order to load them. + * This file just requires other files in order to load them. * LocalStyle and RemoteStyle automatically load their styles from their sources * and apply them as ChromeStyles or ContentStyles. */ - - -// handle uncaught exceptions/rejections in the native modules to prevent the process from exiting -Native.onUncaughtException(error => { reportError('Unhandled error in native code', error); /*Native.nuke();*/ }); -Native.onUnhandledRejection(error => { reportError('Unhandled rejection in native code', error); /*Native.nuke();*/ }); -// TODO: don't just log the errors, they could be critical - +void (updated, Badge, LocalStyle, RemoteStyle); // must load these // debug stuff Object.assign(global, module.exports = { diff --git a/background/local/index.js b/background/local/index.js index c2cb4bc..79c087a 100644 --- a/background/local/index.js +++ b/background/local/index.js @@ -1,6 +1,4 @@ (function(global) { 'use strict'; define(({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - 'node_modules/web-ext-utils/browser/': { manifest, }, - 'node_modules/web-ext-utils/loader/views': { openView, }, 'node_modules/web-ext-utils/utils/': { reportError, }, 'node_modules/native-ext/': Native, 'common/options': options, @@ -27,14 +25,14 @@ class LocalStyle extends Style { /// creates a new `.css` file in the local folder static async createStyle(name, file) { - if (!native) { throw new Error(`Can only write files if Development mode is enabled`); } + if (!native) { throw new Error(`Can only write files if NativeExt is set up and Development mode is enabled`); } const path = options.local.children.folder.value.replace(/([\\/])?$/, _=>_ || '/') + name; (await native.createStyle(path, file)); return path; } /// opens a `.css` file in the local folder with the systems default editor static async openStyle(name) { - if (!native) { throw new Error(`Can only write files if Development mode is enabled`); } + if (!native) { throw new Error(`Can only open files if NativeExt is set up and Development mode is enabled`); } const path = options.local.children.folder.value.replace(/([\\/])?$/, _=>_ || '/') + name; (await native.openStyle(path)); return path; } @@ -52,36 +50,42 @@ class LocalStyle extends Style { //// start implementation -let native = null/*Port*/; const styles = new Map/**/; let exclude = null/*RegExp*/; -let active = options.local.value; options.local.onChange(async ([ value, ]) => { try { (await (value ? enable() : disable())); } catch (error) { reportError(error); } }); -let unloading = false; global.addEventListener('unload', () => (unloading = true)); - -if (active) { enable(true).catch(reportError); } else { if (global.__startupSyncPoint__) { global.__startupSyncPoint__(); } else { global.__startupSyncPoint__ = () => null; } } +let native = null/*exports*/, onSpawn, waiting; +const styles = new Map/**/; let exclude = null/*RegExp*/; +options.local.children.folder.onChange(async () => { + if (active) { try { disable(); (await enable()); } catch (error) { reportError(error); } } +}); +let active = options.local.value; options.local.onChange(async ([ value, ]) => { + try { (await (value ? enable() : disable())); } catch (error) { reportError(error); } +}); +if (active) { enable(true).catch(reportError); } else { letRemoteProceed(); } // reads the local dir and starts listening for changes of it or the chrome/ dir async function enable(init) { if (active && !init) { return; } active = options.local.value = true; - if (!(await Native.test())) { - reportError(`NativeExt unaviable`, `${manifest.name} could not connect to it's native counterpart. To use local styles, please follow the setup instructions.`); - disable(); openView('setup'); return; - } + if (!(await Native.getApplicationName({ stale: true, }))) { { + reportError('Set up NativeExt', `Loading local styles requires NativeExt, but it is not installed or not set up correctly.`); + !waiting && (waiting = Native.getApplicationName({ blocking: true, }).then(() => enable())); + active = false; letRemoteProceed(); + } return; } waiting = null; // console.log('enable local styles'); exclude = new RegExp(options.local.children.exclude.value || '^.^'); - native = (await Native.require( - require.resolve('./native'), - { onDisconnect: () => global.setTimeout(() => { - !unloading && active && reportError('Connection to native extension lost'); - // TODO: show permanent notification with option to restart - }, 20), }, - )); + const files = (await Native.on((onSpawn = async process => { + native = (await process.require(require.resolve('./native'))); - const files = (await native.readStyles(options.local.children.folder.value, onCange)); + if (options.local.children.chrome.value) { debounceIdle(() => { + files && active && native && native.watchChrome(onChromeChange); + }, 2500)(); } + + return native.readStyles(options.local.children.folder.value, onCange); + }))); - if (files === null) { return void reportError(`Can't read local dir`, - `The folder "${options.local.children.folder.value}" does not exist of can not be read. To use local styles, create the folder or change it in the options.` - ); } + if (files === null) { { + Native.on.removeListener(onSpawn); active = false; letRemoteProceed(); + reportError(`Can't read local dir`, `The folder "${options.local.children.folder.value}" does not exist or can not be read. To use local styles, create the folder or change it in the options.`); + } return; } // console.log('got local styles', files); (await Promise.all( @@ -102,11 +106,16 @@ async function enable(init) { } styles.forEach(style => { try { style.disabled = false; } catch (error) { reportError(`Failed to add local style`, error); } }); +} - if (!options.local.children.chrome.value) { return; } +function disable() { + options.local.value = false; if (!active) { return; } active = false; + // console.log('disable local styles'); + Array.from(styles.values(), _=>_.destroy()); styles.clear(); - (await new Promise(done => debounceIdle(done, 2500)())); - native.watchChrome(onChromeChange); + native && native.release(onCange); + native && native.release(onChromeChange); + Native.on.removeListener(onSpawn); native = null; } // called when a .css file in the local dir was actually changed @@ -180,13 +189,11 @@ async function onChromeChange(path, css) { try { }))); } catch (error) { console.error('Error in fs.watch handler', error); } } -function disable() { - options.local.value = false; if (!active) { return; } active = false; - // console.log('disable local styles'); - Array.from(styles.values(), _=>_.destroy()); styles.clear(); - native && native.release(onCange); - Native.unref(native); native = null; -} +function letRemoteProceed() { if (global.__startupSyncPoint__) { + global.__startupSyncPoint__(); +} else { + global.__startupSyncPoint__ = () => null; +} } return LocalStyle; diff --git a/background/style.js b/background/style.js index d688b23..565588a 100644 --- a/background/style.js +++ b/background/style.js @@ -105,7 +105,7 @@ const chromeUrlPrefixes = [ 'chrome://', // only this (?) ]; -const contentDomains = options.advanced.children.restrictedDomains.value.split(','); +const contentDomains = options.internal.children.restrictedDomains.value.split(','); const contentUrlPrefixes = [ 'about:', 'blob:', 'data:', // pretty sure data: doesn't work in a WebStyle, and even if blob:https?:// does, others shouldn't diff --git a/build-config.js b/build-config.js index 8f463eb..efcde53 100644 --- a/build-config.js +++ b/build-config.js @@ -6,6 +6,7 @@ module.exports = function({ options, /*packageJson,*/ manifestJson, files, }) { manifestJson.permissions.push( 'nativeMessaging', 'notifications', + // 'sessions', // remove closed popups // TODO: add this as a minor update at some point 'tabs', 'webNavigation', '', @@ -22,16 +23,16 @@ module.exports = function({ options, /*packageJson,*/ manifestJson, files, }) { files.node_modules = [ 'es6lib/dom.js', 'es6lib/functional.js', + 'multiport/index.js', 'native-ext/index.js', - 'native-ext/install.js', - 'native-ext/tar.js', - 'native-ext/version-native.js', + 'native-ext/init.node.js', + 'native-ext/manager.js', + 'native-ext/process.js', + 'pbq/require.js', 'regexpx/index.js', 'web-ext-utils/browser/index.js', 'web-ext-utils/browser/storage.js', 'web-ext-utils/browser/version.js', - 'web-ext-utils/lib/multiport/index.js', - 'web-ext-utils/lib/pbq/require.js', 'web-ext-utils/loader/_background.html', 'web-ext-utils/loader/_background.js', 'web-ext-utils/loader/_view.html', diff --git a/common/options.js b/common/options.js index 37bbde0..da595d9 100644 --- a/common/options.js +++ b/common/options.js @@ -108,8 +108,8 @@ const model = { }, }, }, - advanced: { - title: 'Advanced', + internal: { + title: 'Internal', expanded: false, hidden: !isBeta, description: `Proceed at your own risk`, diff --git a/package.json b/package.json index e5ae09d..02ccd3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "re-style", - "version": "0.3.2", + "version": "0.4.0", "title": "reStyle", "description": "A user style manager for Firefox 57+ which can load local files and apply UI styles", "author": "Niklas Gollenstede", @@ -18,10 +18,12 @@ ], "dependencies": { "es6lib": "0.0.2", - "native-ext": "0.2.3", + "multiport": "0.2.3", + "native-ext": "0.3.0", + "pbq": "0.3.3", "regexpx": "0.1.4", "web-ext-build": "0.0.9", - "web-ext-utils": "0.0.20" + "web-ext-utils": "0.1.1" }, "devDependencies": { "babel-eslint": "8.2.2", diff --git a/views/index.js b/views/index.js index 7817168..dc38f5e 100644 --- a/views/index.js +++ b/views/index.js @@ -13,18 +13,18 @@ return new Home({ backgroundSize: '80%', height: '100%', backgroundImage: `url(${ getURL('icon.svg') })`, }, }), - }, { - id: 'setup', - title: 'Setup', - icon: createElement('div', { style: { - position: 'relative', fontSize: '130%', top: '-6px', - }, }, [ '❔', ]), }, { id: 'options', title: 'Options', icon: createElement('div', { style: { position: 'relative', fontSize: '180%', top: '-5px', }, }, [ '⚙', ]), + }, { + id: 'setup', + title: 'Setup', + icon: createElement('div', { style: { + position: 'relative', fontSize: '130%', top: '-6px', + }, }, [ '❔', ]), }, { id: 'about', title: 'About', diff --git a/views/panel.js b/views/panel.js index 6823cff..819ef9e 100644 --- a/views/panel.js +++ b/views/panel.js @@ -1,5 +1,5 @@ (function(global) { 'use strict'; define(({ // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. - 'node_modules/web-ext-utils/browser/': { Tabs, }, + 'node_modules/web-ext-utils/browser/': { Tabs, WebNavigation, }, 'node_modules/web-ext-utils/loader/views': { openView, }, 'node_modules/web-ext-utils/utils/': { reportError, reportSuccess, }, 'node_modules/es6lib/dom': { createElement, }, @@ -17,7 +17,7 @@ document.body.innerHTML = `
-

Setup (optional)

- To load local styles and apply styles to the browser UI of Firefox, you have to install an additional application and allow reStyle to connect to it.
- To do so, please follow these steps: -
    -
  1. Download and - execute NativeExt. - After the installation, you should get a success message.
  2. -
  3. Save, extract - and run this script. - After dismissing some security warnings, you should see another a success message.
  4. -
  5. Done! you can now enable UI Styles - and the Development Mode in the options.
  6. -
-`; // TODO: on mac, open with 'Archive Utility', ctrl+click `add ${manifest.name}.command`, 'Open' -> 'Open' +

NativeExt Setup

+
+

To load local styles and apply styles to the browser UI of Firefox, reStyle needs access to the NativeExt extension.

+

Please install the NativeExt extension and follow its setup instructions, + then click this button.

+ +
+

It seems you are all set!

+

You can now use UI Styles + and enable the Development Mode in the options.

+

Show instructions

+
+`; -document.querySelector('#download-bin').href = download.direct; -const show = document.querySelector('#show-script'), save = document.querySelector('#save-script'); -save.onclick = e => { if (!e.button) { saveAs.call(window, script.url, script.name); e.preventDefault(); } }; -show.href = window.URL.createObjectURL(new window.Blob([ '
', script.text, '
', ], { type: 'text/html', })); -show.title = script.text; -!(/windows/i).test(window.navigator.oscpu) && document.body.classList.add('unix'); +const todo = document.querySelector('#todo'), done = document.querySelector('#done'); +function show(section) { document.querySelectorAll('.active').forEach(_=>_.classList.remove('active')); section.classList.add('active'); } +if ((await Native.getApplicationName({ stale: null, }))) { show(done); } +document.querySelector('#instructions').onclick = e => { if(!e.button) { show(todo); e.preventDefault(); } }; + +document.querySelector('#extension').href = Native.extensionInstallPage(browser); + +document.querySelector('#request').onclick = async e => { if(!e.button) { try { + const reply = (await Native.requestPermission({ + message: `reStyle needs access to the NativeExt to load local styles and apply styles to the browser UI of Firefox.`, + })); if (reply.failed) { throw new Error(reply.message); } + show(done); + document.querySelector('#error').style.display = 'none'; + document.querySelector('#message').textContent = ''; +} catch (error) { + document.querySelector('#error').style.display = 'block'; + document.querySelector('#message').textContent = error.message; +} } }; }); })(this);