diff --git a/background/background.js b/background/background.js index 77d5c705..a9a6cf21 100644 --- a/background/background.js +++ b/background/background.js @@ -326,6 +326,14 @@ function onRuntimeMessage(request, sender, sendResponse) { case 'injectContent': injectContent(sender.tab.id, request).then(sendResponse); return KEEP_CHANNEL_OPEN; + + case 'openUsercssInstallPage': + usercssHelper.openInstallPage(sender.tab.id, request).then(sendResponse); + return KEEP_CHANNEL_OPEN; + + case 'initUsercssInstallPage': + usercssHelper.initInstallPage(sender.tab.id, request).then(sendResponse); + return KEEP_CHANNEL_OPEN; } } diff --git a/background/usercss-helper.js b/background/usercss-helper.js index b2877978..acdb663b 100644 --- a/background/usercss-helper.js +++ b/background/usercss-helper.js @@ -96,5 +96,30 @@ var usercssHelper = (function () { ); } - return {build, save, findDup}; + function openInstallPage(tabId, request) { + // FIXME: openURL doesn't reuse old page? + const url = '/install-usercss/install-usercss.html' + + '?updateUrl=' + encodeURIComponent(request.updateUrl) + + '&tabId=' + tabId; + const pending = openURL({url}) + .then(tab => { + // FIXME: need a reliable way to check if a new tab is created + if (tab.url) { + chrome.runtime.onMessage.addListener(function _({method}, sender, sendResponse) { + if (method !== 'usercssInstallPageReady') { + return; + } + if (sender.tab.id !== tab.id) { + return; + } + chrome.runtime.onMessage.removeListener(_); + wrapReject(Promise.resolve(request)).then(sendResponse); + return true; + }); + } + }); + return wrapReject(pending); + } + + return {build, save, findDup, openInstallPage}; })(); diff --git a/content/install-user-css.js b/content/install-user-css.js index ee20f79a..29d7bc47 100644 --- a/content/install-user-css.js +++ b/content/install-user-css.js @@ -1,31 +1,5 @@ -/* global semverCompare makeLink */ - 'use strict'; -let pendingResource; - -function install(style) { - const request = Object.assign(style, { - method: 'saveUsercss', - reason: 'update' - }); - return runtimeSend(request) - .then(result => { - $$('.warning') - .forEach(el => el.remove()); - $('button.install').textContent = 'Installed'; - $('button.install').disabled = true; - $('button.install').classList.add('installed'); - $('.set-update-url').disabled = true; - $('.set-update-url-label').title = result.updateUrl ? - t('installUpdateFrom', result.updateUrl) : ''; - window.dispatchEvent(new CustomEvent('installed', {detail: result})); - }) - .catch(err => { - alert(chrome.i18n.getMessage('styleInstallFailed', String(err))); - }); -} - function runtimeSend(request) { return new Promise((resolve, reject) => { chrome.runtime.sendMessage( @@ -35,204 +9,6 @@ function runtimeSend(request) { }); } -function getAppliesTo(style) { - function *_gen() { - for (const section of style.sections) { - for (const type of ['urls', 'urlPrefixes', 'domains', 'regexps']) { - if (section[type]) { - yield *section[type]; - } - } - } - } - const result = [..._gen()]; - if (!result.length) { - result.push(chrome.i18n.getMessage('appliesToEverything')); - } - return result; -} - -function initInstallPage({style, dup}, sourceLoader) { - return pendingResource.then(() => { - const data = style.usercssData; - const dupData = dup && dup.usercssData; - const versionTest = dup && semverCompare(data.version, dupData.version); - document.body.textContent = ''; - document.body.appendChild(buildPage()); - - if (versionTest < 0) { - $('.actions').parentNode.insertBefore( - $element({className: 'warning', textContent: t('versionInvalidOlder')}), - $('.actions') - ); - } - $('.code').textContent = style.sourceCode; - $('button.install').onclick = () => { - if (dup) { - if (confirm(chrome.i18n.getMessage('styleInstallOverwrite', [ - data.name, dupData.version, data.version - ]))) { - install(style); - } - } else if (confirm(chrome.i18n.getMessage('styleInstall', [data.name]))) { - install(style); - } - }; - if (dup && dup.updateUrl === location.href) { - $('.set-update-url').checked = true; - // there is no way to "unset" updateUrl, you can only overwrite it. - $('.set-update-url').disabled = true; - } else if (!dup && location.protocol !== 'file:') { - $('.set-update-url').checked = true; - style.updateUrl = location.href; - } - $('.set-update-url').onchange = e => { - if (e.target.checked) { - style.updateUrl = location.href; - } else { - delete style.updateUrl; - } - }; - - if (location.protocol === 'file:') { - initLiveReload(sourceLoader); - } - - function buildPage() { - return $element({className: 'container', appendChild: [ - $element({className: 'header', appendChild: [ - $element({className: 'actions', appendChild: [ - $element({tag: 'button', className: 'install', textContent: installButtonLabel()}), - $element({ - tag: 'label', - title: dup && dup.updateUrl && t('installUpdateFrom', dup.updateUrl) || '', - className: 'set-update-url-label', - appendChild: [ - $element({ - tag: 'input', - type: 'checkbox', - className: 'set-update-url' - }), - $element({tag: 'span', textContent: t('installUpdateFromLabel')}) - ] - }) - ]}), - $element({tag: 'h1', appendChild: [ - data.name, - $element({tag: 'small', className: 'meta-version', textContent: data.version}) - ]}), - $element({tag: 'p', textContent: data.description}), - data.author && $element({tag: 'h3', textContent: t('author')}), - data.author, - data.license && $element({tag: 'h3', textContent: t('license')}), - data.license, - $element({tag: 'h3', textContent: t('appliesLabel')}), - $element({tag: 'ul', appendChild: getAppliesTo(style).map( - pattern => $element({tag: 'li', textContent: pattern}) - )}), - externalLink(), - ]}), - $element({className: 'main', appendChild: [ - $element({className: 'code'}) - ]}) - ]}); - } - - function externalLink() { - const urls = []; - if (data.homepageURL) { - urls.push([data.homepageURL, t('externalHomepage')]); - } - if (data.supportURL) { - urls.push([data.supportURL, t('externalSupport')]); - } - if (urls.length) { - return $element({appendChild: [ - $element({tag: 'h3', textContent: t('externalLink')}), - $element({tag: 'ul', appendChild: urls.map(args => - $element({tag: 'li', appendChild: makeLink(...args)}) - )}) - ]}); - } - } - - function installButtonLabel() { - return t(!dup ? 'installButton' : - versionTest > 0 ? 'installButtonUpdate' : 'installButtonReinstall'); - } - }); -} - -function initLiveReload(sourceLoader) { - let installed; - const watcher = sourceLoader.watch(source => { - $('.code').textContent = source; - return runtimeSend({ - method: 'saveUsercss', - id: installed.id, - sourceCode: source - }).then(() => { - $$('.main .warning').forEach(e => e.remove()); - }).catch(err => { - const oldWarning = $('.main .warning'); - const warning = buildWarning(err); - if (oldWarning) { - oldWarning.replaceWith(warning); - } else { - $('.main').insertBefore(warning, $('.main').childNodes[0]); - } - }); - }); - window.addEventListener('installed', ({detail: style}) => { - installed = style; - if ($('.live-reload-checkbox').checked) { - watcher.start(); - } - }); - chrome.runtime.onMessage.addListener(request => { - if (request.method === 'styleDeleted') { - if (installed && installed.id === request.id) { - installed = null; - watcher.stop(); - $('.live-reload-checkbox').checked = false; - location.reload(); - } - } - }); - $('.actions').appendChild($element({tag: 'label', className: 'live-reload', appendChild: [ - $element({tag: 'input', type: 'checkbox', className: 'live-reload-checkbox'}), - $element({tag: 'span', textContent: t('liveReloadLabel')}) - ]})); - $('.live-reload-checkbox').onchange = e => { - if (!installed) { - return; - } - if (e.target.checked) { - watcher.start(); - } else { - watcher.stop(); - } - }; -} - -function buildWarning(err) { - return $element({className: 'warning', appendChild: [ - t('parseUsercssError'), - $element({tag: 'pre', textContent: String(err)}) - ]}); -} - -function initErrorPage(err, source) { - return pendingResource.then(() => { - document.body.textContent = ''; - [ - buildWarning(err), - $element({className: 'code'}) - ].forEach(e => document.body.appendChild(e)); - $('.code').textContent = source; - }); -} - function createSourceLoader() { let source; @@ -292,28 +68,28 @@ function createSourceLoader() { } function initUsercssInstall() { - pendingResource = runtimeSend({ - method: 'injectContent', - files: [ - '/js/dom.js', - '/js/localization.js', - '/js/usercss.js', - '/vendor/node-semver/semver.js', - '/content/install-user-css.css' - ] - }); + const pendingSource = createSourceLoader().load(); + chrome.runtime.onConnect.addListener(port => { + // FIXME: is this the correct way to reject a connection? + // https://developer.chrome.com/extensions/messaging#connect + console.assert(port.name === 'usercss-install'); - const sourceLoader = createSourceLoader(); - sourceLoader.load() - .then(() => - runtimeSend({ - method: 'buildUsercss', - sourceCode: sourceLoader.source(), - checkDup: true - }) - ) - .then(result => initInstallPage(result, sourceLoader)) - .catch(err => initErrorPage(err, sourceLoader.source())); + port.onMessage.addListener(msg => { + switch (msg.method) { + case 'getSourceCode': + pendingSource.then(sourceCode => + port.postMessage({method: msg.method + 'Response', sourceCode}) + ).catch(err => + port.postMessage({method: msg.method + 'Response', error: err.message || String(err)}) + ); + break; + } + }); + }); + return runtimeSend({ + method: 'openUsercssInstallPage', + updateUrl: location.href + }).catch(alert); } function isUsercss() { diff --git a/content/install-user-css.css b/install-usercss/install-usercss.css similarity index 92% rename from content/install-user-css.css rename to install-usercss/install-usercss.css index e6f41a3f..1922f0ec 100644 --- a/content/install-user-css.css +++ b/install-usercss/install-usercss.css @@ -1,6 +1,7 @@ body { margin: 0; font: 12px arial, sans-serif; + background: white; } * { @@ -21,7 +22,7 @@ body { overflow-wrap: break-word; } -.header :first-child { +.header > :first-child { margin-top: 0; } @@ -71,8 +72,6 @@ h1 small { .code { padding: 2em; - font-family: monospace; - white-space: pre-wrap; } .main { diff --git a/install-usercss/install-usercss.html b/install-usercss/install-usercss.html new file mode 100644 index 00000000..72490773 --- /dev/null +++ b/install-usercss/install-usercss.html @@ -0,0 +1,53 @@ + + +
+ + + +