Browse Source

fix: internal FF detection + faster FormData in FF

* starting with FF 100+ the version string can't be compared to a number like 56
* FormData can't be sent directly across worlds in FF, same as in Chrome
tophf 3 years ago
parent
commit
7a22ebfffb

+ 1 - 0
.eslintrc.js

@@ -163,6 +163,7 @@ function getGlobals(filename) {
     }) {
       if (name) {
         // const NAME = whatever
+        // We consider `let` immutable too to avoid unintentional reassignment
         res[name] = false;
       } else if (properties) {
         // const { NAME1, prototype: { NAME2: ALIAS2 } } = whatever

+ 8 - 1
src/background/utils/requests.js

@@ -237,10 +237,17 @@ export function clearRequestsByTabId(tabId, frameId) {
   });
 }
 
-/** Polyfill for Chrome's inability to send complex types over extension messaging */
+/** Polyfill for browser's inability to send complex types over extension messaging */
 function decodeBody([body, type, wasBlob]) {
   if (type === 'query') {
     type = 'application/x-www-form-urlencoded';
+  } else if (type === 'fd') {
+    // FF supports FormData over messaging
+    // Chrome doesn't - we use this code only with an empty FormData just to create the object
+    const res = new FormData();
+    body.forEach(entry => res.set(...entry));
+    body = res;
+    type = '';
   } else if (type != null) {
     // 5x times faster than fetch() which wastes time on inter-process communication
     const res = string2uint8array(atob(body.slice(body.indexOf(',') + 1)));

+ 3 - 0
src/injected/content/inject.js

@@ -128,6 +128,9 @@ export async function injectScripts(contentId, webId, data, isXml) {
   if (errors) {
     logging.warn(errors);
   }
+  if (IS_FIREFOX) {
+    IS_FIREFOX = parseFloat(info.ua.browserVersion); // eslint-disable-line no-global-assign
+  }
   realms = {
     __proto__: null,
     [INJECT_CONTENT]: {

+ 4 - 1
src/injected/content/requests.js

@@ -34,7 +34,7 @@ bridge.addHandlers({
     ]);
     msg.url = getFullUrl(msg.url);
     let { data } = msg;
-    if (data[1]) {
+    if (data[1] && !IS_FIREFOX /* in FF FormData is recreated in bg::decodeBody */) {
       // TODO: support huge data by splitting it to multiple messages
       data = await encodeBody(data[0], data[1]);
       msg.data = cloneInto ? cloneInto(data, msg) : data;
@@ -183,6 +183,9 @@ function finishChunks(req) {
 /** Doing it here because vault's SafeResponse+blob() doesn't work in injected-web */
 async function encodeBody(body, mode) {
   if (mode === 'fd') {
+    if (!body.length) { // see decodeBody comments about FormData in Chrome
+      return [body, mode];
+    }
     const fd = new SafeFormData();
     body::forEach(entry => fd::fdAppend(entry[0], entry[1]));
     body = fd;

+ 2 - 2
src/injected/content/safe-globals-content.js

@@ -1,4 +1,4 @@
-/* eslint-disable no-unused-vars */
+/* eslint-disable no-unused-vars, import/no-mutable-exports, prefer-const */
 
 /**
  * `safeCall` is used by our modified babel-plugin-safe-bind.js.
@@ -49,5 +49,5 @@ export const getReadyState = describeProperty(Document[PROTO], 'readyState').get
 export const isDocumentLoading = () => !/^(inter|compl)/::regexpTest(document::getReadyState());
 export const logging = assign(createNullObj(), console);
 export const { chrome } = global;
-export const IS_FIREFOX = !chrome.app;
 export const VM_UUID = chrome.runtime.getURL('');
+export let IS_FIREFOX = !chrome.app;

+ 4 - 3
src/injected/web/requests.js

@@ -147,9 +147,10 @@ function start(req, context, fileName) {
     data: data == null && []
       // `binary` is for TM/GM-compatibility + non-objects = must use a string `data`
       || (opts.binary || !isObject(data)) && [`${data}`]
-      // FF56+ can send any cloneable data directly, FF52-55 can't due to https://bugzil.la/1371246
-      || IS_FIREFOX && bridge.ua.browserVersion >= 56 && [data]
+      // No browser can send FormData directly across worlds
       || getFormData(data)
+      // FF56+ can send any cloneable data directly, FF52-55 can't due to https://bugzil.la/1371246
+      || IS_FIREFOX >= 56 && [data]
       || [data, 'bin'],
     eventsToNotify: [
       'abort',
@@ -173,7 +174,7 @@ function start(req, context, fileName) {
   ]), context);
 }
 
-/** Chrome can't directly transfer FormData to isolated world so we explode it,
+/** Chrome/FF can't directly transfer FormData to isolated world so we explode it,
  * trusting its iterator is usable because the only reason for a site to break it
  * is to fight a userscript, which it can do by breaking FormData constructor anyway */
 function getFormData(data) {