mozilla / webextension-polyfill

A lightweight polyfill library for Promise-based WebExtension APIs in Chrome
Mozilla Public License 2.0
2.73k stars 214 forks source link

Can't send runtime messages in Firefox; Error: "TypeError: promise.then is not a function" #105

Open kottkrig opened 6 years ago

kottkrig commented 6 years ago

I'm getting the following error in Firefox when I try to send a runtime message from a content script to a background script:

Unhandled promise rejection 
TypeError​columnNumber: 9​
fileName: "resource://gre/modules/ExtensionCommon.jsm"​
lineNumber: 490​
message: "promise.then is not a function"
​stack: "wrapPromise/<@resource://gre/modules/ExtensionCommon.jsm:490:9_@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:69770
wrapPromise@resource://gre/modules/ExtensionCommon.jsm:489:14
callAsyncFunction@resource://gre/modules/ExtensionCommon.jsm:697:12
stub@resource://gre/modules/Schemas.jsm:2297:22
t/<@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96454
b@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:50347
A/o._invoke@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:50137
E/</t[n]@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:50523
e@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96739
i/<@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96722
_@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:69770
i@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96680
u<@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96912
@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:96983
r@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:105
@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:97021
r@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:105
@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:517
@moz-extension://63f25ebd-93c8-554e-bc69-b002724886b7/content_script.js:1:2
inject@resource://gre/modules/ExtensionContent.jsm:483:18
injectInto@resource://gre/modules/ExtensionContent.jsm:388:14
"​__proto__: Object { stack: "", … } content_script.js:1:69061
// content_script.js

import browser from "webextension-polyfill";

async function sendMessage() {
  const { response } = await browser.runtime.sendMessage({
    kind: "LOREM",
    data: "ipsum"
  });

  console.log("content_script: response:", response);
}

sendMessage();
// background_page.js

import browser from "webextension-polyfill";

browser.runtime.onMessage.addListener(async ({ kind, data }) => {
  if (kind === "LOREM") {
    return Promise.resolve({ response: data });
  }

  return false;
});

I'm using webpack with babel-polyfill to include the generator runtime:


module.exports = {
  entry: {
    background_page: ["babel-polyfill", "./src/background_page.js"],
    content_script: ["babel-polyfill", "./src/content_script.js"]
  },

  output: {
    path: path.resolve(__dirname + "/dist"),
    filename: "[name].js"
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["babel-preset-env"]
          }
        }
      }
    ],
  }
}

It works fine in Chrome. What am I missing?

james-cnz commented 6 years ago

Perhaps try changing this line: return Promise.resolve({ response: data }); I don't really understand promises, but if you're using async functions, I think they're dealt with automatically, and you can just say: return { response: data };

james-cnz commented 6 years ago

On second thoughts, that probably won't make any difference.

It could be the way you're importing it? This might bypass the check intended to stop the polyfill being applied to Firefox? The main page has suggestions about how to import it.

james-cnz commented 6 years ago

A couple of other suggestions: There might an issue with Webpack? (see #86) And if you don't need to support old browsers, you can drop Babel. (see #50) (Just going by others' comments. I don't know anything about either of these myself.)

rikedia commented 6 years ago

Has anyone found a solution to this problem, running into this as well.

Rob--W commented 6 years ago

Can you share your generated bundle?

Side note, the following is incorrect if your intent is to only respond to LOREM because the onMessage handler is an async function, which returns a promise by default.

browser.runtime.onMessage.addListener(async ({ kind, data }) => {
  if (kind === "LOREM") {
    return Promise.resolve({ response: data });
  }

  return false;
});

should be without async, or simply:


browser.runtime.onMessage.addListener(async ({ kind, data }) => {
  if (kind === "LOREM") {
    return { response: data };
  }
});
rikedia commented 6 years ago

@Rob--W I am trying to send a message from a content script, if I pause the debugger just before it is about to send the message, and I send a message that is not handled: browser.runtime.sendMessage("whatever").then(console.log.bind(console))

I get the same error.

TypeError: promise.then is not a function Stack trace: wrapPromise@resource://gre/modules/ExtensionCommon.jsm:492:7 callAsyncFunction@resource://gre/modules/ExtensionCommon.jsm:737:12 stub@resource://gre/modules/Schemas.jsm:2300:22 _callee5$@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67708:29 tryCatch@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:22601:37 invoke@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:22839:22 defineIteratorMethods/</prototype[method]@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:22653:16 step@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67526:183 _asyncToGenerator/</<@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67526:437 Promise@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:20706:7 _asyncToGenerator/<@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67526:99 init/<@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67765:20 Promise@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:20706:7 init@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67684:12 @moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67770:1 __webpack_require__@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:20:12 @moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:67280:18 __webpack_require__@moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:20:12 @moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:63:18 @moz-extension://b03cc2f6-d49c-454e-a4aa-1f4b8b3d9614/plugin.js:1:11 inject@resource://gre/modules/ExtensionContent.jsm:489:18

Rob--W commented 6 years ago

@rikedia Please share your generated code...

rikedia commented 6 years ago

@Rob--W newsreader-extension-nl-tst.zip

Rob--W commented 6 years ago

@rikedia In your extension, the native Promise constructor has been replaced with something else. Don't do that (e.g. by removing babel-polyfill; both Chrome and Firefox support async / await these days).

The fact that replacing the global Promise constructor breaks sendMessage is probably an unintended bug. I have reported it upstream at https://bugzilla.mozilla.org/show_bug.cgi?id=1456531 .

rikedia commented 6 years ago

@Rob--W Thanks for pointing that out.

I fixed my problems by removing babel-polyfill and adding babel-plugin-transform-runtime with the polyfill option set to false.

.babelrc

{
    ...
    "plugins": [
        ...
        [
            "transform-runtime",
            {
                "polyfill": false,
                "regenerator": true
            }
        ]
    ]
}
kottkrig commented 6 years ago

Thanks @Rob--W and @rikedia. That helped me get rid of the "promise.then is not a function" error but I'm now getting another error in Firefox:

Unhandled promise rejection TypeError: "_ref2 is undefined"

(It works in Chrome)

The generated files are available in this gist. And here is an excerpt with my function from content_script.js:

var sendMessage = function () {
  var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() {
    var _ref2, response;

    return _regenerator2.default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return _webextensionPolyfill2.default.runtime.sendMessage({
              kind: "LOREM",
              data: "ipsum"
            });

          case 2:
            _ref2 = _context.sent;
            response = _ref2.response;

            console.log("content_script: response:", response);

          case 5:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function sendMessage() {
    return _ref.apply(this, arguments);
  };
}(); // content_script.js

I've added the transform-runtime to my webpack config and removed the babel-polyfill:

const path = require("path");

module.exports = {
  entry: {
    background_page: ["./src/background_page.js"],
    content_script: ["./src/content_script.js"]
  },

  output: {
    path: path.resolve(__dirname + "/dist"),
    filename: "[name].js"
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["babel-preset-env"],
            plugins: [
              [
                "babel-plugin-transform-runtime",
                { polyfill: false, regenerator: true }
              ]
            ]
          }
        }
      }
    ]
  }
};
Rob--W commented 6 years ago

Have you tried following the suggestion from https://github.com/mozilla/webextension-polyfill/issues/105#issuecomment-383876541 ? The auto-generated code is difficult to read due to the replaced async/await. Can you share a zip file or repo with the minimal project to reproduce the bug?

kottkrig commented 6 years ago

I got it working by changing my webpack setup to use "babel-preset-env" with my current node as the target so that it uses native async/await.

Thanks @Rob--W for the help!

My updated webpack config for completeness:

const path = require("path");
module.exports = {
  entry: {
    background_page: ["./src/background_page.js"],
    content_script: ["./src/content_script.js"]
  },

  output: {
    path: path.resolve(__dirname + "/dist"),
    filename: "[name].js"
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["babel-preset-env", { targets: { node: "current" } }]]
          }
        }
      }
    ]
  }
};
Rob--W commented 6 years ago

Closing this since this is not an issue with the polyfill, but with Firefox * as explained in https://github.com/mozilla/webextension-polyfill/issues/105#issuecomment-383990462 . The solution is also described in the previous comments: change the webpack/babel configuration to not replace the Promise global.

* The polyfill is a no-op in Firefox. We will document this more clearly and close #55 in the future.

abhijithvijayan commented 5 years ago

@Rob--W I was also facing similar issue in Firefox. browser.runtime.onMessage.addListener should be waiting for the Promise to resolve and the return to the browser.runtime.sendMessage. It worked in Chrome but in Firefox it returned and didn't wait. I was using babel-polyfill. Following your solution helped and now the extension works as expected in all the browsers. Thank you. 👍

https://github.com/abhijithvijayan/kuttUrl-browser-extension/commit/7564e7ecfd2bd203270544519e993151c891527f

ashclarke commented 5 years ago

@abhijithvijayan - Just had the exact same issue. Took a few hours to get to this thread and this point. I was getting an unhandled promise rejection and my sendMessage call was being immediately fulfilled. This is a really nasty side effect of the usage of the Promise polyfill.

My .babelrc now looks like this:

module.exports = {
    presets: [
        ["@babel/preset-env", {
            corejs: 3,
            debug: false,
            exclude: [
                "es.promise",
                "es.promise.finally"
            ],
            loose: true,
            spec: true,
            targets: {
                browsers: [
                    "last 2 Chrome versions",
                    "last 2 Firefox versions"
                ]
            },
            useBuiltIns: "usage"
        }]
    ]
}
rpl commented 5 years ago

@Rob--W I think that it may be reasonable to explicitly document the "TypeError: promise.then is not a function" error in one of the following sections of the README.md file e.g.:

even if this isn't technically a issue of the webextension-polyfill, so that an addon developer that has this issue doesn't need to find this closed issue to know how to fix it.

Rob--W commented 5 years ago

Let's update the docs.

Preferably this should be fixed in Firefox. The validation in the extension framework is stricter than the JS engine's (i.e. in situations where promises are used, thenables are perfectly fine, whereas in the extension framework we don't accept them currently).