Przeglądaj źródła

feat: show injection failure in popup, allow retrying

tophf 5 lat temu
rodzic
commit
d8f37d1c1b

+ 6 - 0
src/_locales/en/messages.yml

@@ -455,6 +455,12 @@ menuDashboard:
 menuFindScripts:
   description: Menu item to find scripts for a site.
   message: Find scripts for this site
+menuInjectionFailed:
+  description: Injection error.
+  message: Could not inject some scripts.
+menuInjectionFailedFix:
+  description: Injection error fix, shown in case the default mode is "page".
+  message: Retry in "auto" mode
 menuMatchedFrameScripts:
   description: Label for menu listing matching scripts in sub-frames.
   message: Sub-frames only scripts

+ 1 - 0
src/injected/content/bridge.js

@@ -9,6 +9,7 @@ const bridge = {
   ids: [],
   // userscripts running in the content script context are messaged via invokeGuest
   invokableIds: [],
+  failedIds: [],
   // {CommandName: sendCmd} will relay the request via sendCmd as is
   addHandlers(obj) {
     assign(handlers, obj);

+ 6 - 2
src/injected/content/index.js

@@ -1,6 +1,6 @@
 import { getUniqId, isEmpty } from '#/common';
 import { INJECT_CONTENT } from '#/common/consts';
-import { objectKeys } from '#/common/object';
+import { objectKeys, objectPick } from '#/common/object';
 import { bindEvents, sendCmd } from '../utils';
 import {
   forEach, includes, append, createElementNS, setAttribute, NS_HTML,
@@ -37,6 +37,7 @@ const { split } = String.prototype;
   // 2) cloneInto is provided by Firefox in content scripts to expose data to the page
   bridge.post = bindEvents(contentId, webId, bridge.onHandle, global.cloneInto);
   bridge.isFirefox = data.isFirefox;
+  bridge.injectInto = data.injectInto;
   if (data.scripts) injectScripts(contentId, webId, data, isXml);
   if (data.expose) bridge.post('Expose');
   isPopupShown = data.isPopupShown;
@@ -95,7 +96,10 @@ bridge.addHandlers({
 
 function sendSetPopup() {
   if (isPopupShown) {
-    sendCmd('SetPopup', { ids: bridge.ids, menus });
+    sendCmd('SetPopup', {
+      menus,
+      ...objectPick(bridge, ['ids', 'failedIds', 'injectInto']),
+    });
   }
 }
 

+ 4 - 2
src/injected/content/inject.js

@@ -69,7 +69,7 @@ export function injectScripts(contentId, webId, data, isXml) {
     },
   };
   const triage = (script) => {
-    const { custom, dataKey, meta } = script;
+    const { custom, dataKey, meta, props: { id } } = script;
     const desiredRealm = custom.injectInto || meta.injectInto || data.injectInto;
     const internalRealm = INJECT_MAPPING[desiredRealm] || INJECT_MAPPING[INJECT_AUTO];
     const realm = internalRealm.find(key => realms[key]?.injectable());
@@ -82,8 +82,10 @@ export function injectScripts(contentId, webId, data, isXml) {
       const list = lists[runAt] || lists[runAt = 'end'];
       script.action = realm === INJECT_CONTENT && (runAt === 'start' ? runAt : 'wait');
       needsInjection = !!script.action;
-      ids::push(script.props.id);
+      ids::push(id);
       list::push(script);
+    } else {
+      bridge.failedIds.push(id);
     }
     return [dataKey, needsInjection];
   };

+ 13 - 2
src/popup/index.js

@@ -1,6 +1,6 @@
 import Vue from 'vue';
 import { getActiveTab, i18n, sendCmd } from '#/common';
-import { INJECTABLE_TAB_URL_RE } from '#/common/consts';
+import { INJECT_PAGE, INJECTABLE_TAB_URL_RE } from '#/common/consts';
 import handlers from '#/common/handlers';
 import { mapEntry } from '#/common/object';
 import * as tld from '#/common/tld';
@@ -41,7 +41,18 @@ Object.assign(handlers, {
     }
     if (ids.length) {
       // frameScripts may be appended multiple times if iframes have unique scripts
-      store[isTop ? 'scripts' : 'frameScripts'].push(...await sendCmd('GetMetas', ids));
+      const scope = store[isTop ? 'scripts' : 'frameScripts'];
+      scope.push(...await sendCmd('GetMetas', ids));
+      data.failedIds.forEach(id => {
+        scope.forEach((script) => {
+          if (script.props.id === id) {
+            script.failed = true;
+            if (!store.injectionFailure) {
+              store.injectionFailure = { fixable: data.injectInto === INJECT_PAGE };
+            }
+          }
+        });
+      });
     }
   },
 });

+ 3 - 0
src/popup/style.css

@@ -144,6 +144,9 @@ footer {
     > .flex-1 {
       padding-right: 2rem;
     }
+    .failed {
+      text-decoration: line-through red;
+    }
   }
   &-find {
     padding-left: 0;

+ 1 - 0
src/popup/utils/index.js

@@ -3,6 +3,7 @@ export const store = {
   frameScripts: [],
   commands: [],
   domain: '',
+  injectionFailure: null,
   injectable: true,
   blacklisted: false,
 };

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

@@ -70,6 +70,7 @@
             <img class="script-icon" :src="scriptIconUrl(item)" @error="scriptIconError">
             <icon :name="getSymbolCheck(item.data.config.enabled)"></icon>
             <div class="flex-auto ellipsis" v-text="item.name"
+                 :class="{failed: item.data.failed}"
                  @click.ctrl.exact.stop="onEditScript(item)"
                  @contextmenu.exact.stop="onEditScript(item)"
                  @mousedown.middle.exact.stop="onEditScript(item)" />
@@ -96,6 +97,12 @@
         </div>
       </div>
     </div>
+    <div class="failure-reason" v-if="store.injectionFailure">
+      <div v-text="i18n('menuInjectionFailed')"/>
+      <a v-text="i18n('menuInjectionFailedFix')" href="#"
+         v-if="store.injectionFailure.fixable"
+         @click.prevent="onInjectionFailureFix"/>
+    </div>
     <div class="incognito"
        v-if="store.currentTab && store.currentTab.incognito"
        v-text="i18n('msgIncognitoChanges')"/>
@@ -110,10 +117,9 @@
 
 <script>
 import Tooltip from 'vueleton/lib/tooltip/bundle';
+import { INJECT_AUTO } from '#/common/consts';
 import options from '#/common/options';
-import {
-  getLocaleString, i18n, sendCmd, sendTabCmd,
-} from '#/common';
+import { getLocaleString, i18n, makePause, sendCmd, sendTabCmd } from '#/common';
 import Icon from '#/common/ui/icon';
 import { store } from '../utils';
 
@@ -271,6 +277,13 @@ export default {
       });
       window.close();
     },
+    async onInjectionFailureFix() {
+      // TODO: promisify options.set, resolve on storage write, await it instead of makePause
+      options.set('defaultInjectInto', INJECT_AUTO);
+      await makePause(100);
+      await browser.tabs.reload();
+      window.close();
+    },
   },
 };
 </script>