make manager load real fast

This commit is contained in:
tophf 2020-10-15 13:55:27 +03:00
parent bc6c9c826a
commit d1b9338707
9 changed files with 73 additions and 152 deletions

View File

@ -23,11 +23,11 @@
<script src="js/polyfill.js"></script>
<script src="js/dom.js"></script>
<script src="js/messaging.js"></script>
<script src="js/msg.js"></script>
<script src="js/prefs.js"></script>
<script src="js/localization.js"></script>
<script src="js/script-loader.js"></script>
<script src="js/storage-util.js"></script>
<script src="js/msg.js"></script>
<script src="content/style-injector.js"></script>
<script src="content/apply.js"></script>

View File

@ -33,7 +33,8 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
onExtension,
off,
RX_NO_RECEIVER,
RX_PORT_CLOSED
RX_PORT_CLOSED,
isBg,
};
function getBg() {

View File

@ -1,6 +1,8 @@
/* global promisifyChrome */
/* global promisifyChrome msg API */
'use strict';
// Needs msg.js loaded first
self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
const defaults = {
'openEditInWindow': false, // new editor opens in a own browser window
@ -112,12 +114,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
'storage.sync': ['get', 'set'],
});
const initializing = browser.storage.sync.get('settings')
.then(result => {
if (result.settings) {
setAll(result.settings, true);
}
});
const initializing = (
msg.isBg
? browser.storage.sync.get('settings').then(res => res.settings)
: API.getPrefs()
).then(res => res && setAll(res, true));
chrome.storage.onChanged.addListener((changes, area) => {
if (area !== 'sync' || !changes.settings || !changes.settings.newValue) {

View File

@ -48,73 +48,3 @@ const loadScript = (() => {
));
};
})();
(() => {
let subscribers, observer;
// natively declared <script> elements in html can't have onload= attribute
// due to the default extension CSP that forbids inline code (and we don't want to relax it),
// so we're using MutationObserver to add onload event listener to the script element to be loaded
window.onDOMscriptReady = (srcSuffix, timeout = 1000) => {
if (!subscribers) {
subscribers = new Map();
observer = new MutationObserver(observe);
observer.observe(document.head, {childList: true});
}
return new Promise((resolve, reject) => {
const listeners = subscribers.get(srcSuffix);
if (listeners) {
listeners.push(resolve);
} else {
subscribers.set(srcSuffix, [resolve]);
}
// a resolved Promise won't reject anymore
setTimeout(() => {
emptyAfterCleanup(srcSuffix);
reject(new Error('Timeout'));
}, timeout);
});
};
return;
function observe(mutations) {
for (const {addedNodes} of mutations) {
for (const n of addedNodes) {
if (n.src && getSubscribersForSrc(n.src)) {
n.addEventListener('load', notifySubscribers, {once: true});
}
}
}
}
function getSubscribersForSrc(src) {
for (const [suffix, listeners] of subscribers.entries()) {
if (src.endsWith(suffix)) {
return {suffix, listeners};
}
}
}
function notifySubscribers(event) {
for (let data; (data = getSubscribersForSrc(this.src));) {
data.listeners.forEach(fn => fn(event));
if (emptyAfterCleanup(data.suffix)) {
return;
}
}
}
function emptyAfterCleanup(suffix) {
if (!subscribers) {
return true;
}
subscribers.delete(suffix);
if (!subscribers.size) {
observer.disconnect();
observer = null;
subscribers = null;
return true;
}
}
})();

View File

@ -149,8 +149,8 @@
<script src="js/polyfill.js"></script>
<script src="js/dom.js"></script>
<script src="js/messaging.js"></script>
<script src="js/prefs.js"></script>
<script src="js/msg.js"></script>
<script src="js/prefs.js"></script>
<script src="js/router.js"></script>
<script src="content/style-injector.js"></script>
<script src="content/apply.js"></script>

View File

@ -14,7 +14,7 @@ let initialized = false;
router.watch({search: ['search']}, ([search]) => {
$('#search').value = search || '';
if (!initialized) {
init();
initFilters();
initialized = true;
} else {
searchStyles();
@ -36,7 +36,7 @@ HTMLSelectElement.prototype.adjustWidth = function () {
parent.replaceChild(this, singleSelect);
};
function init() {
function initFilters() {
$('#search').oninput = e => {
router.updateSearch('search', e.target.value);
};

View File

@ -1,15 +1,11 @@
/* global messageBox styleSectionsEqual API onDOMready
tryJSONparse scrollElementIntoView $ $$ API $create t animateElement
styleJSONseemsValid */
/* exported bulkChangeQueue bulkChangeTime */
styleJSONseemsValid bulkChangeQueue */
'use strict';
const STYLISH_DUMP_FILE_EXT = '.txt';
const STYLUS_BACKUP_FILE_EXT = '.json';
let bulkChangeQueue = [];
let bulkChangeTime = 0;
onDOMready().then(() => {
$('#file-all-styles').onclick = () => exportToFile();
$('#unfile-all-styles').onclick = () => importFromFile({fileTypeFilter: STYLUS_BACKUP_FILE_EXT});
@ -135,7 +131,7 @@ function importFromString(jsonString) {
}
});
bulkChangeQueue.length = 0;
bulkChangeTime = performance.now();
bulkChangeQueue.time = performance.now();
return API.importManyStyles(items.map(i => i.item))
.then(styles => {
for (let i = 0; i < styles.length; i++) {

View File

@ -4,11 +4,10 @@ global messageBox getStyleWithNoCode
checkUpdate handleUpdateInstalled
objectDiff
configDialog
sorter msg prefs API onDOMready $ $$ $create template setupLivePrefs
sorter msg prefs API $ $$ $create template setupLivePrefs
t tWordBreak formatDate
getOwnTab getActiveTab openURL animateElement sessionStorageHash debounce
scrollElementIntoView CHROME VIVALDI router
bulkChangeTime:true bulkChangeQueue
*/
'use strict';
@ -18,6 +17,8 @@ const ENTRY_ID_PREFIX_RAW = 'style-';
const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW;
const BULK_THROTTLE_MS = 100;
const bulkChangeQueue = [];
bulkChangeQueue.time = 0;
// define pref-mapped ids separately
const newUI = {
@ -49,46 +50,10 @@ Promise.all([
API.getAllStyles(true),
// FIXME: integrate this into filter.js
router.getSearch('search') && API.searchDB({query: router.getSearch('search')}),
Promise.all([
onDOMready(),
prefs.initializing,
])
.then(() => {
initGlobalEvents();
if (!VIVALDI) {
$$('#header select').forEach(el => el.adjustWidth());
}
}),
]).then(args => {
showStyles(...args);
});
msg.onExtension(onRuntimeMessage);
function onRuntimeMessage(msg) {
switch (msg.method) {
case 'styleUpdated':
case 'styleAdded':
case 'styleDeleted':
bulkChangeQueue.push(msg);
if (performance.now() - bulkChangeTime < BULK_THROTTLE_MS) {
debounce(handleBulkChange, BULK_THROTTLE_MS);
} else {
handleBulkChange();
}
break;
case 'styleApply':
case 'styleReplaceAll':
break;
default:
return;
}
setTimeout(sorter.updateStripes, 0, {onlyWhenColumnsChanged: true});
}
function initGlobalEvents() {
installed = $('#installed');
waitForSelector('#installed'), // needed to avoid flicker due to an extra frame and layout shift
prefs.initializing
]).then(([styles, ids, el]) => {
installed = el;
installed.onclick = handleEvent.entryClicked;
$('#manage-options-button').onclick = () => router.updateHash('#stylus-options');
$('#sync-styles').onclick = () => router.updateHash('#stylus-options');
@ -96,31 +61,12 @@ function initGlobalEvents() {
// show date installed & last update on hover
installed.addEventListener('mouseover', handleEvent.lazyAddEntryTitle);
installed.addEventListener('mouseout', handleEvent.lazyAddEntryTitle);
document.addEventListener('visibilitychange', onVisibilityChange);
$$('[data-toggle-on-click]').forEach(el => {
// dataset on SVG doesn't work in Chrome 49-??, works in 57+
const target = $(el.getAttribute('data-toggle-on-click'));
el.onclick = event => {
event.preventDefault();
target.classList.toggle('hidden');
if (target.classList.contains('hidden')) {
el.removeAttribute('open');
} else {
el.setAttribute('open', '');
}
};
});
// N.B. triggers existing onchange listeners
setupLivePrefs();
sorter.init();
prefs.subscribe(newUI.ids.map(newUI.prefKeyForId), () => switchUI());
switchUI({styleOnly: true});
// translate CSS manually
document.head.appendChild($create('style', `
.disabled h2::after {
@ -133,6 +79,39 @@ function initGlobalEvents() {
content: "${t('filteredStylesAllHidden')}";
}
`));
if (!VIVALDI) {
$$('#header select').forEach(el => el.adjustWidth());
}
if (CHROME >= 80 && CHROME <= 88) {
// Wrong checkboxes are randomly checked after going back in history, https://crbug.com/1138598
addEventListener('pagehide', () => {
$$('input[type=checkbox]').forEach((el, i) => (el.name = `bug${i}`));
});
}
showStyles(styles, ids);
});
msg.onExtension(onRuntimeMessage);
function onRuntimeMessage(msg) {
switch (msg.method) {
case 'styleUpdated':
case 'styleAdded':
case 'styleDeleted':
bulkChangeQueue.push(msg);
if (performance.now() - bulkChangeQueue.time < BULK_THROTTLE_MS) {
debounce(handleBulkChange, BULK_THROTTLE_MS);
} else {
handleBulkChange();
}
break;
case 'styleApply':
case 'styleReplaceAll':
break;
default:
return;
}
setTimeout(sorter.updateStripes, 0, {onlyWhenColumnsChanged: true});
}
function showStyles(styles = [], matchUrlIds) {
@ -538,7 +517,7 @@ function handleBulkChange() {
const {id} = msg.style;
if (msg.method === 'styleDeleted') {
handleDelete(id);
bulkChangeTime = performance.now();
bulkChangeQueue.time = performance.now();
} else {
handleUpdateForId(id, msg);
}
@ -549,7 +528,7 @@ function handleBulkChange() {
function handleUpdateForId(id, opts) {
return API.getStyle(id, true).then(style => {
handleUpdate(style, opts);
bulkChangeTime = performance.now();
bulkChangeQueue.time = performance.now();
});
}
@ -723,6 +702,20 @@ function highlightEditedStyle() {
}
}
function waitForSelector(selector) {
// TODO: if used in other places, move to dom.js
// TODO: if used concurrently, rework to use just one observer internally
return new Promise(resolve => {
const mo = new MutationObserver(() => {
const el = $(selector);
if (el) {
mo.disconnect();
resolve(el);
}
});
mo.observe(document, {childList: true, subtree: true});
});
}
function embedOptions() {
let options = $('#stylus-embedded-options');

View File

@ -180,8 +180,8 @@
<script src="js/dom.js"></script>
<script src="js/messaging.js"></script>
<script src="js/localization.js"></script>
<script src="js/prefs.js"></script>
<script src="js/msg.js"></script>
<script src="js/prefs.js"></script>
<script src="content/style-injector.js"></script>
<script src="content/apply.js"></script>