Jelajahi Sumber

fix #2185: check any `window` for vivaldi

+ reuse apiEvent::listenOnce()
tophf 1 tahun lalu
induk
melakukan
6db81de6fe

+ 2 - 17
src/background/utils/preinject.js

@@ -1,5 +1,5 @@
 import {
-  browserWindows, getActiveTab, getScriptName, getScriptPrettyUrl, getUniqId, sendTabCmd
+  getActiveTab, getScriptName, getScriptPrettyUrl, getUniqId, sendTabCmd
 } from '@/common';
 import {
   __CODE, TL_AWAIT, UNWRAP,
@@ -24,13 +24,12 @@ import {
 import { clearStorageCache, onStorageChanged } from './storage-cache';
 import { getFrameDocId, getFrameDocIdAsObj, tabsOnRemoved } from './tabs';
 import { addValueOpener, clearValueOpener, reifyValueOpener } from './values';
-import { setBrowserName, ua } from './ua';
+import { ua } from './ua';
 
 let isApplied;
 let injectInto;
 let ffInject;
 let xhrInject = false; // must be initialized for proper comparison when toggling
-let checkedVivaldi = IS_FIREFOX;
 
 const sessionId = getUniqId();
 const API_HEADERS_RECEIVED = browser.webRequest.onHeadersReceived;
@@ -156,7 +155,6 @@ addPublicCommands({
     const frameDoc = getFrameDocId(isTop, src[kDocumentId], frameId);
     const tabId = tab.id;
     if (!url) url = src.url || tab.url;
-    if (!checkedVivaldi) checkVivaldi(tab);
     clearFrameData(tabId, frameDoc);
     let skip = skippedTabs[tabId];
     if (skip > 0) { // first time loading the tab after skipScripts was invoked
@@ -399,7 +397,6 @@ async function prepareBag(cacheKey, url, isTop, env, inject, errors) {
   const bag = { [INJECT]: inject };
   const { allIds, [MORE]: envDelayed } = env;
   const moreKey = envDelayed[IDS].length && getUniqId('more');
-  if (isObject(checkedVivaldi)) await checkedVivaldi;
   Object.assign(inject, {
     [SCRIPTS]: prepareScripts(env),
     [INJECT_INTO]: injectInto,
@@ -670,18 +667,6 @@ function clearFrameData(tabId, frameId, tabRemoved) {
   clearNotifications(tabId, frameId, tabRemoved);
 }
 
-/** Checking on demand because a tab/window definitely exists now,
- * which is not guaranteed at browser start */
-function checkVivaldi(obj) {
-  if (obj.vivExtData/*new*/ || obj.extData/*old*/) {
-    setBrowserName('Vivaldi');
-  } else if (!checkedVivaldi) { // skipping when we're in the nested self-call
-    checkedVivaldi = browserWindows.getCurrent().then(checkVivaldi);
-    return;
-  }
-  checkedVivaldi = true;
-}
-
 function sendPopupShown(tabId, frameDoc) {
   setTimeout(sendTabCmd, 0, tabId, 'PopupShown', true, getFrameDocIdAsObj(frameDoc));
 }

+ 16 - 1
src/background/utils/ua.js

@@ -1,3 +1,5 @@
+import { browserWindows } from '@/common';
+import { listenOnce } from '@/common/browser';
 import { addOwnCommands, init } from './init';
 
 export const {
@@ -8,7 +10,6 @@ const uaVer = navUA.match(/\s(?:Chrom(?:e|ium)|Firefox)\/(\d+[.0-9]*)|$/i)[1];
 
 /** @type {VMScriptGMInfoPlatform} */
 export const ua = {};
-export const setBrowserName = name => { ua.browserName = name; };
 /** @type {number|void} This value can be trusted because the only way to spoof it in Chrome/ium
  * is to manually open devtools for the background page in device emulation mode.
  * Using `void` for numeric comparisons like CHROME < 100 to be false in Firefox */
@@ -27,10 +28,12 @@ init.deps.push(
     browser.runtime.getPlatformInfo(),
     browser.runtime.getBrowserInfo?.(),
     navUAD?.getHighEntropyValues(['fullVersionList']),
+    IS_FIREFOX ? [] : browserWindows.getAll(),
   ]).then(([
     { os, arch },
     { name, version } = {},
     uadValues,
+    [wnd],
   ]) => {
     if (!version && (uadValues = uadValues?.fullVersionList) && uadValues[0]) {
       [name, version] = uadValues.map(({ brand, version: v }) => (
@@ -44,5 +47,17 @@ init.deps.push(
     setBrowserName(name || 'chrome');
     ua.browserVersion = version || uaVer;
     if (FIREFOX) FIREFOX = parseFloat(version);
+    else if (wnd) checkVivaldi(wnd);
+    else browserWindows.onCreated::listenOnce(checkVivaldi);
   })
 );
+
+function checkVivaldi(wnd) {
+  if (wnd.vivExtData/*new*/ || wnd.extData/*old*/) {
+    setBrowserName('Vivaldi');
+  }
+}
+
+function setBrowserName(name) {
+  ua.browserName = name;
+}

+ 16 - 3
src/common/browser.js

@@ -1,4 +1,6 @@
 let { browser } = global;
+const kAddListener = 'addListener';
+const kRemoveListener = 'removeListener';
 
 // Since this also runs in a content script we'll guard against implicit global variables
 // for DOM elements with 'id' attribute which is a standard feature, more info:
@@ -9,8 +11,8 @@ if (!IS_FIREFOX && !browser?.runtime) {
   const { bind } = SafeProxy;
   const MESSAGE = 'message';
   const STACK = 'stack';
-  const isSyncMethodName = key => key === 'addListener'
-    || key === 'removeListener'
+  const isSyncMethodName = key => key === kAddListener
+    || key === kRemoveListener
     || key === 'hasListener'
     || key === 'hasListeners';
   /** API types or enums or literal constants */
@@ -149,7 +151,7 @@ if (!IS_FIREFOX && !browser?.runtime) {
       getManifest: 0,
       getURL: 0,
       onMessage: {
-        addListener: (onMessage, addListener) => (
+        [kAddListener]: (onMessage, addListener) => (
           listener => {
             if (process.env.DEV
             && !process.env.IS_INJECTED
@@ -210,3 +212,14 @@ if (!IS_FIREFOX && !browser?.runtime) {
  */
 
 export default browser;
+
+/** @this {object} browser api event
+ * @return {Promise<?>} listener's first parameter */
+export function listenOnce(cb) {
+  const event = this;
+  const onceFn = data => {
+    event[kRemoveListener](onceFn);
+    cb(data);
+  };
+  event[kAddListener](onceFn);
+}

+ 8 - 7
src/options/views/tab-settings/vm-import.vue

@@ -19,18 +19,22 @@
   </div>
 </template>
 
-<script setup>
-import { onMounted, reactive, ref } from 'vue';
+<script>
 import { ensureArray, getUniqId, i18n, sendCmdDirectly } from '@/common';
+import { listenOnce } from '@/common/browser';
 import { RUN_AT_RE } from '@/common/consts';
 import options from '@/common/options';
-import SettingCheck from '@/common/ui/setting-check';
 import loadZipLibrary from '@/common/zip';
 import { showConfirmation } from '@/common/ui';
 import {
   kDownloadURL, kExclude, kInclude, kMatch, kOrigExclude, kOrigInclude, kOrigMatch,
   runInBatch, store,
 } from '../../utils';
+</script>
+
+<script setup>
+import { onMounted, reactive, ref } from 'vue';
+import SettingCheck from '@/common/ui/setting-check';
 
 const reports = reactive([]);
 const buttonImport = ref();
@@ -222,10 +226,7 @@ async function undoImport() {
 }
 
 function resolveOnUndoMessage(resolve) {
-  undoPort.onMessage.addListener(function fn() {
-    undoPort.onMessage.removeListener(fn);
-    resolve();
-  });
+  undoPort.onMessage::listenOnce(resolve);
 }
 
 function initDragDrop(targetElement) {

+ 1 - 0
test/mock/polyfill.js

@@ -27,6 +27,7 @@ global.browser = {
     onUpdated: { addListener: () => {} },
   },
   windows: {
+    getAll: () => [{}],
     getCurrent: () => ({}),
   },
 };