faster popup load to avoid resize flicker
This commit is contained in:
parent
51f125113d
commit
4d198f56e2
38
popup.html
38
popup.html
|
@ -16,12 +16,6 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<!-- Notes:
|
||||
* Chrome doesn't garbage-collect (or even leaks) SVG <symbol> referenced via <use> so we'll embed the code directly
|
||||
* inter-tag whitespace in templates is automatically removed in localization.js
|
||||
* i18n-anything attribute automatically creates "anything" attribute
|
||||
-->
|
||||
|
||||
<template data-id="style">
|
||||
<div class="entry">
|
||||
<div class="entry-content">
|
||||
|
@ -178,33 +172,35 @@
|
|||
<div class="search-result-empty"></div>
|
||||
</template>
|
||||
|
||||
<link rel="stylesheet" href="vendor-overwrites/colorpicker/colorpicker.css">
|
||||
<script src="vendor-overwrites/colorpicker/colorconverter.js"></script>
|
||||
<script src="vendor-overwrites/colorpicker/colorpicker.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="msgbox/msgbox.css">
|
||||
<script src="msgbox/msgbox.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="options/onoffswitch.css">
|
||||
<link rel="stylesheet" href="manage/config-dialog.css">
|
||||
<script src="manage/config-dialog.js"></script>
|
||||
|
||||
<script src="js/polyfill.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/messaging.js"></script>
|
||||
<script src="js/localization.js"></script>
|
||||
<script src="js/msg.js"></script>
|
||||
<script src="js/messaging.js"></script>
|
||||
<script src="popup/popup-preinit.js"></script>
|
||||
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/localization.js"></script>
|
||||
<script src="js/prefs.js"></script>
|
||||
<script src="content/style-injector.js"></script>
|
||||
<script src="content/apply.js"></script>
|
||||
|
||||
<!-- async hack to load auxiliary stylesheets later so they don't delay the UI -->
|
||||
<link rel="stylesheet" href="vendor-overwrites/colorpicker/colorpicker.css" media="print">
|
||||
<link rel="stylesheet" href="msgbox/msgbox.css" media="print">
|
||||
<link rel="stylesheet" href="manage/config-dialog.css" media="print">
|
||||
<link rel="stylesheet" href="popup/search-results.css" media="print">
|
||||
<link rel="stylesheet" href="options/onoffswitch.css">
|
||||
<link rel="stylesheet" href="popup/popup.css">
|
||||
<link rel="stylesheet" href="popup/search-results.css">
|
||||
|
||||
<script src="popup/popup.js"></script>
|
||||
<script src="popup/search-results.js"></script>
|
||||
<script src="popup/hotkeys.js"></script>
|
||||
|
||||
<script src="js/script-loader.js" async></script>
|
||||
<script src="js/storage-util.js" async></script>
|
||||
<script src="vendor-overwrites/colorpicker/colorconverter.js" async></script>
|
||||
<script src="vendor-overwrites/colorpicker/colorpicker.js" async></script>
|
||||
<script src="msgbox/msgbox.js" async></script>
|
||||
<script src="manage/config-dialog.js" async></script>
|
||||
</head>
|
||||
|
||||
<body id="stylus-popup">
|
||||
|
|
87
popup/popup-preinit.js
Normal file
87
popup/popup-preinit.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* global
|
||||
API
|
||||
URLS
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const ABOUT_BLANK = 'about:blank';
|
||||
/* exported tabURL */
|
||||
/** @type string */
|
||||
let tabURL;
|
||||
|
||||
/* exported initializing */
|
||||
const initializing = (async () => {
|
||||
let [tab] = await browser.tabs.query({currentWindow: true, active: true});
|
||||
if (!chrome.app && tab.status === 'loading' && tab.url === 'about:blank') {
|
||||
tab = await waitForTabUrlFF(tab);
|
||||
}
|
||||
const frames = sortTabFrames(await browser.webNavigation.getAllFrames({tabId: tab.id}));
|
||||
let url = tab.pendingUrl || tab.url || ''; // new Chrome uses pendingUrl while connecting
|
||||
if (url === 'chrome://newtab/' && !URLS.chromeProtectsNTP) {
|
||||
url = frames[0].url || '';
|
||||
}
|
||||
if (!URLS.supported(url)) {
|
||||
url = '';
|
||||
frames.length = 1;
|
||||
}
|
||||
tabURL = frames[0].url = url;
|
||||
const uniqFrames = frames.filter(f => f.url && !f.isDupe);
|
||||
const styles = await Promise.all(uniqFrames.map(getFrameStyles));
|
||||
return {frames, styles};
|
||||
|
||||
async function getFrameStyles({url}) {
|
||||
return {
|
||||
url,
|
||||
styles: await getStyleDataMerged(url),
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {chrome.webNavigation.GetAllFrameResultDetails[]} frames */
|
||||
function sortTabFrames(frames) {
|
||||
const unknown = new Map(frames.map(f => [f.frameId, f]));
|
||||
const known = new Map([[0, unknown.get(0) || {frameId: 0, url: ''}]]);
|
||||
unknown.delete(0);
|
||||
let lastSize = 0;
|
||||
while (unknown.size !== lastSize) {
|
||||
for (const [frameId, f] of unknown) {
|
||||
if (known.has(f.parentFrameId)) {
|
||||
unknown.delete(frameId);
|
||||
if (!f.errorOccurred) known.set(frameId, f);
|
||||
if (f.url === ABOUT_BLANK) f.url = known.get(f.parentFrameId).url;
|
||||
}
|
||||
}
|
||||
lastSize = unknown.size; // guard against an infinite loop due to a weird frame structure
|
||||
}
|
||||
const sortedFrames = [...known.values(), ...unknown.values()];
|
||||
const urls = new Set([ABOUT_BLANK]);
|
||||
for (const f of sortedFrames) {
|
||||
if (!f.url) f.url = '';
|
||||
f.isDupe = urls.has(f.url);
|
||||
urls.add(f.url);
|
||||
}
|
||||
return sortedFrames;
|
||||
}
|
||||
|
||||
function waitForTabUrlFF(tab) {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener(...[
|
||||
function onUpdated(tabId, info, updatedTab) {
|
||||
if (info.url && tabId === tab.id) {
|
||||
browser.tabs.onUpdated.removeListener(onUpdated);
|
||||
resolve(updatedTab);
|
||||
}
|
||||
},
|
||||
...'UpdateFilter' in browser.tabs ? [{tabId: tab.id}] : [],
|
||||
// TODO: remove both spreads and tabId check when strict_min_version >= 61
|
||||
]);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
/* Merges the extra props from API into style data.
|
||||
* When `id` is specified returns a single object otherwise an array */
|
||||
async function getStyleDataMerged(url, id) {
|
||||
const styles = (await API.getStylesByUrl(url, id))
|
||||
.map(r => Object.assign(r.data, r));
|
||||
return id ? styles[0] : styles;
|
||||
}
|
125
popup/popup.js
125
popup/popup.js
|
@ -3,18 +3,22 @@
|
|||
$$
|
||||
$create
|
||||
animateElement
|
||||
ABOUT_BLANK
|
||||
API
|
||||
CHROME
|
||||
CHROME_HAS_BORDER_BUG
|
||||
configDialog
|
||||
FIREFOX
|
||||
getActiveTab
|
||||
getStyleDataMerged
|
||||
hotkeys
|
||||
initializing
|
||||
msg
|
||||
onDOMready
|
||||
prefs
|
||||
setupLivePrefs
|
||||
t
|
||||
tabURL
|
||||
tryJSONparse
|
||||
URLS
|
||||
*/
|
||||
|
@ -23,11 +27,8 @@
|
|||
|
||||
/** @type Element */
|
||||
let installed;
|
||||
/** @type string */
|
||||
let tabURL;
|
||||
const handleEvent = {};
|
||||
|
||||
const ABOUT_BLANK = 'about:blank';
|
||||
const ENTRY_ID_PREFIX_RAW = 'style-';
|
||||
|
||||
$.entry = styleOrId => $(`#${ENTRY_ID_PREFIX_RAW}${styleOrId.id || styleOrId}`);
|
||||
|
@ -38,28 +39,21 @@ if (CHROME >= 66 && CHROME <= 69) { // Chrome 66-69 adds a gap, https://crbug.co
|
|||
|
||||
toggleSideBorders();
|
||||
|
||||
initTabUrls()
|
||||
.then(frames =>
|
||||
Promise.all([
|
||||
onDOMready().then(() => initPopup(frames)),
|
||||
...frames
|
||||
.filter(f => f.url && !f.isDupe)
|
||||
.map(({url}) => getStyleDataMerged(url).then(styles => ({styles, url}))),
|
||||
]))
|
||||
.then(([, ...results]) => {
|
||||
const sliders = prefs.get('ui.sliders');
|
||||
const slot = $('toggle', t.template.style);
|
||||
const toggle = t.template[sliders ? 'toggleSlider' : 'toggleChecker'];
|
||||
slot.parentElement.replaceChild(toggle.cloneNode(true), slot);
|
||||
document.body.classList.toggle('has-sliders', sliders);
|
||||
if (results[0]) {
|
||||
showStyles(results);
|
||||
} else {
|
||||
// unsupported URL;
|
||||
$('#popup-manage-button').removeAttribute('title');
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
Promise.all([
|
||||
initializing,
|
||||
onDOMready(),
|
||||
]).then(([
|
||||
{frames, styles},
|
||||
]) => {
|
||||
toggleUiSliders();
|
||||
initPopup(frames);
|
||||
if (styles[0]) {
|
||||
showStyles(styles);
|
||||
} else {
|
||||
// unsupported URL;
|
||||
$('#popup-manage-button').removeAttribute('title');
|
||||
}
|
||||
});
|
||||
|
||||
msg.onExtension(onRuntimeMessage);
|
||||
|
||||
|
@ -109,23 +103,12 @@ function toggleSideBorders(state = prefs.get('popup.borders')) {
|
|||
}
|
||||
}
|
||||
|
||||
async function initTabUrls() {
|
||||
let tab = await getActiveTab();
|
||||
if (FIREFOX && tab.status === 'loading' && tab.url === ABOUT_BLANK) {
|
||||
tab = await waitForTabUrlFF(tab);
|
||||
}
|
||||
let frames = await browser.webNavigation.getAllFrames({tabId: tab.id});
|
||||
let url = tab.pendingUrl || tab.url || ''; // new Chrome uses pendingUrl while connecting
|
||||
frames = sortTabFrames(frames);
|
||||
if (url === 'chrome://newtab/' && !URLS.chromeProtectsNTP) {
|
||||
url = frames[0].url || '';
|
||||
}
|
||||
if (!URLS.supported(url)) {
|
||||
url = '';
|
||||
frames.length = 1;
|
||||
}
|
||||
tabURL = frames[0].url = url;
|
||||
return frames;
|
||||
function toggleUiSliders() {
|
||||
const sliders = prefs.get('ui.sliders');
|
||||
const slot = $('toggle', t.template.style);
|
||||
const toggle = t.template[sliders ? 'toggleSlider' : 'toggleChecker'];
|
||||
slot.parentElement.replaceChild(toggle.cloneNode(true), slot);
|
||||
document.body.classList.toggle('has-sliders', sliders);
|
||||
}
|
||||
|
||||
/** @param {chrome.webNavigation.GetAllFrameResultDetails[]} frames */
|
||||
|
@ -158,6 +141,10 @@ async function initPopup(frames) {
|
|||
installed);
|
||||
}
|
||||
|
||||
for (const el of $$('link[media=print]')) {
|
||||
el.removeAttribute('media');
|
||||
}
|
||||
|
||||
if (!tabURL) {
|
||||
blockPopup();
|
||||
return;
|
||||
|
@ -188,6 +175,11 @@ async function initPopup(frames) {
|
|||
// so we'll wait a bit to handle popup being invoked right after switching
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
|
||||
initUnreachable(isStore);
|
||||
}
|
||||
|
||||
function initUnreachable(isStore) {
|
||||
const info = t.template.unreachableInfo;
|
||||
if (!FIREFOX) {
|
||||
// Chrome "Allow access to file URLs" in chrome://extensions message
|
||||
|
@ -294,32 +286,6 @@ function getDomains(url) {
|
|||
return domains;
|
||||
}
|
||||
|
||||
/** @param {chrome.webNavigation.GetAllFrameResultDetails[]} frames */
|
||||
function sortTabFrames(frames) {
|
||||
const unknown = new Map(frames.map(f => [f.frameId, f]));
|
||||
const known = new Map([[0, unknown.get(0) || {frameId: 0, url: ''}]]);
|
||||
unknown.delete(0);
|
||||
let lastSize = 0;
|
||||
while (unknown.size !== lastSize) {
|
||||
for (const [frameId, f] of unknown) {
|
||||
if (known.has(f.parentFrameId)) {
|
||||
unknown.delete(frameId);
|
||||
if (!f.errorOccurred) known.set(frameId, f);
|
||||
if (f.url === ABOUT_BLANK) f.url = known.get(f.parentFrameId).url;
|
||||
}
|
||||
}
|
||||
lastSize = unknown.size; // guard against an infinite loop due to a weird frame structure
|
||||
}
|
||||
const sortedFrames = [...known.values(), ...unknown.values()];
|
||||
const urls = new Set([ABOUT_BLANK]);
|
||||
for (const f of sortedFrames) {
|
||||
if (!f.url) f.url = '';
|
||||
f.isDupe = urls.has(f.url);
|
||||
urls.add(f.url);
|
||||
}
|
||||
return sortedFrames;
|
||||
}
|
||||
|
||||
function sortStyles(entries) {
|
||||
const enabledFirst = prefs.get('popup.enabledFirst');
|
||||
return entries.sort(({styleMeta: a}, {styleMeta: b}) =>
|
||||
|
@ -697,29 +663,6 @@ function handleDelete(id) {
|
|||
}
|
||||
}
|
||||
|
||||
function waitForTabUrlFF(tab) {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener(...[
|
||||
function onUpdated(tabId, info, updatedTab) {
|
||||
if (info.url && tabId === tab.id) {
|
||||
chrome.tabs.onUpdated.removeListener(onUpdated);
|
||||
resolve(updatedTab);
|
||||
}
|
||||
},
|
||||
...'UpdateFilter' in browser.tabs ? [{tabId: tab.id}] : [],
|
||||
// TODO: remove both spreads and tabId check when strict_min_version >= 61
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/* Merges the extra props from API into style data.
|
||||
* When `id` is specified returns a single object otherwise an array */
|
||||
async function getStyleDataMerged(url, id) {
|
||||
const styles = (await API.getStylesByUrl(url, id))
|
||||
.map(r => Object.assign(r.data, r));
|
||||
return id ? styles[0] : styles;
|
||||
}
|
||||
|
||||
function blockPopup(isBlocked = true) {
|
||||
document.body.classList.toggle('blocked', isBlocked);
|
||||
if (isBlocked) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user