ソースを参照

fix #2035: support `icon` in GM_registerMenuCommand

tophf 2 ヶ月 前
コミット
2fb5dd0db0
3 ファイル変更31 行追加5 行削除
  1. 20 1
      src/common/load-script-icon.js
  2. 8 2
      src/popup/index.js
  3. 3 2
      src/popup/views/app.vue

+ 20 - 1
src/common/load-script-icon.js

@@ -1,4 +1,4 @@
-import { isDataUri, isValidHttpUrl, noop, sendCmdDirectly } from '@/common/index';
+import { isDataUri, isValidHttpUrl, noop, sendCmdDirectly } from '@/common';
 
 // TODO: convert this into a component tag e.g. <safe-icon>
 const KEY = 'safeIcon';
@@ -36,3 +36,22 @@ export async function loadScriptIcon(script, store, showDefault) {
   }
   return script[KEY];
 }
+
+/**
+ * Sets script's safeIcon property after the image is successfully loaded
+ * @param {{}} cmdOpts
+ * @param {{cache?:{}}} store
+ */
+export async function loadCommandIcon(cmdOpts, store) {
+  const { icon } = cmdOpts;
+  const cache = store.cache ??= {};
+  if (icon && !(KEY in cmdOpts)) {
+    cmdOpts[KEY] = null; // creating an observable property
+    let url = cache[icon] || isDataUri(icon) && icon;
+    if (!url && isValidHttpUrl(icon)) {
+      url = cache[icon] = sendCmdDirectly('GetImageData', icon).catch(noop);
+    }
+    if (isObject(url)) url = await url;
+    cmdOpts[KEY] = cache[icon] = url || null;
+  }
+}

+ 8 - 2
src/popup/index.js

@@ -1,7 +1,7 @@
 import '@/common/browser';
 import { sendCmdDirectly } from '@/common';
 import handlers from '@/common/handlers';
-import { loadScriptIcon } from '@/common/load-script-icon';
+import { loadCommandIcon, loadScriptIcon } from '@/common/load-script-icon';
 import { mapEntry } from '@/common/object';
 import { isTouch, render } from '@/common/ui';
 import '@/common/ui/style';
@@ -46,7 +46,7 @@ async function setPopup(data, { [kFrameId]: frameId, url }) {
     store[IS_APPLIED] = data[INJECT_INTO] !== 'off'; // isApplied at the time of GetInjected
   }
   // Ensuring top script's menu wins over a per-frame menu with different commands
-  store.commands = Object.assign(data.menus, !isTop && store.commands);
+  const commands = store.commands = Object.assign(data.menus, !isTop && store.commands);
   const idMapAllFrames = store.idMap;
   const idMapMain = idMapAllFrames[0] || (idMapAllFrames[0] = {});
   const idMapOld = idMapAllFrames[frameId] || (idMapAllFrames[frameId] = {});
@@ -84,6 +84,12 @@ async function setPopup(data, { [kFrameId]: frameId, url }) {
       }
     });
   }
+  for (const scriptId in commands) {
+    const scriptCommands = commands[scriptId];
+    for (const id in scriptCommands) {
+      loadCommandIcon(scriptCommands[id], store);
+    }
+  }
   if (isTop) mutexResolve(); // resolving at the end after all `await` above are settled
   if (!hPrev) {
     hPrev = Math.max(innerHeight, 100); // ignore the not-yet-resized popup e.g. in Firefox

+ 3 - 2
src/popup/views/app.vue

@@ -154,7 +154,7 @@
           <div class="submenu-commands">
             <div
               class="menu-item menu-area"
-              v-for="({ autoClose = true, text, title }, key) in store.commands[item.id]"
+              v-for="({ autoClose = true, safeIcon, text, title }, key) in store.commands[item.id]"
               :key
               :tabIndex
               :cmd.prop="[item.id, key, autoClose]"
@@ -163,7 +163,8 @@
               @mouseup="onCommand"
               @keydown.enter="onCommand"
               @keydown.space="onCommand">
-              <icon name="command" />
+              <img v-if="safeIcon" class="icon" :src="safeIcon">
+              <icon v-else name="command" />
               <div class="flex-auto ellipsis" v-text="text" />
             </div>
           </div>