Kaynağa Gözat

speed up setting icon data in Chrome (#628)

* fix: updateState on the default about:newtab in FF

* speedup: cache setIcon data in Chrome

* fixup: handle iconCache only inside setIcon
tophf 6 yıl önce
ebeveyn
işleme
1e5983bd8d
1 değiştirilmiş dosya ile 36 ekleme ve 5 silme
  1. 36 5
      src/background/utils/icon.js

+ 36 - 5
src/background/utils/icon.js

@@ -23,6 +23,10 @@ let showBadge;
 let titleBlacklisted;
 let titleNoninjectable;
 
+// We'll cache the icon data in Chrome as it doesn't cache the data and takes up to 40ms
+// in our background page context to set the 4 icon sizes for each new tab opened
+const iconCache = (global.chrome || {}).app && {};
+
 hookOptions((changes) => {
   if ('isApplied' in changes) {
     isApplied = changes.isApplied;
@@ -51,7 +55,9 @@ browser.tabs.onRemoved.addListener((id) => {
 });
 
 browser.tabs.onUpdated.addListener((tabId, info, tab) => {
-  if (info.status === 'loading') {
+  if (info.status === 'loading'
+      // at least about:newtab in Firefox may open without 'loading' status
+      || info.favIconUrl && tab.url.startsWith('about:')) {
     updateState(tab, info.url);
   }
 });
@@ -111,13 +117,38 @@ function updateState(tab, url = tab.url) {
   }
 }
 
-function setIcon(tab = {}, data = {}) {
+async function setIcon(tab = {}, data = {}) {
   // modern Chrome and Firefox use 16/32, other browsers may still use 19/38 (e.g. Vivaldi)
   const mod = data.blocked && 'b' || !isApplied && 'w' || '';
+  const iconData = {};
+  for (const n of [16, 19, 32, 38]) {
+    const path = `/public/images/icon${n}${mod}.png`;
+    let icon = iconCache ? iconCache[path] : path;
+    if (!icon) {
+      icon = await loadImageData(path);
+      iconCache[path] = icon;
+    }
+    iconData[n] = icon;
+  }
   browserAction.setIcon({
-    path: Object.assign({}, ...[16, 19, 32, 38].map(n => ({
-      [n]: `/public/images/icon${n}${mod}.png`,
-    }))),
     tabId: tab.id,
+    [iconCache ? 'imageData' : 'path']: iconData,
+  });
+}
+
+function loadImageData(path) {
+  return new Promise(resolve => {
+    const img = new Image();
+    img.src = path;
+    img.onload = () => {
+      const { width, height } = img;
+      const canvas = document.createElement('canvas');
+      const ctx = canvas.getContext('2d');
+      canvas.width = width;
+      canvas.height = height;
+      ctx.clearRect(0, 0, width, height);
+      ctx.drawImage(img, 0, 0, width, height);
+      resolve(ctx.getImageData(0, 0, width, height));
+    };
   });
 }