Skip to content

Commit

Permalink
Merge branch 'master' into refactor/wrap-apis
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Jul 7, 2018
2 parents 7d7f110 + ea82fbf commit 39ac89c
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Table of contents

## Building

To build, assuming you're already installed [node >= 6](https://nodejs.org) and
To build, assuming you've already installed [node >= 6](https://nodejs.org) and
[npm](https://www.npmjs.com/), simply run:

```sh
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"homepage": "https://github.com/mozilla/webextension-polyfill",
"devDependencies": {
"async-wait-until": "^1.1.5",
"babel-core": "^6.26.3",
"babel-eslint": "^8.0.1",
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
"babel-preset-babili": "^0.0.10",
Expand All @@ -44,6 +45,7 @@
"shelljs": "^0.8.2",
"sinon": "^1.17.6",
"tap-nirvana": "^1.0.8",
"tape": "^4.9.1",
"tape-async": "^2.3.0",
"tmp": "0.0.33"
},
Expand Down
14 changes: 11 additions & 3 deletions src/browser-polyfill.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"use strict";

if (typeof browser === "undefined") {
const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
const SEND_RESPONSE_DEPRECATION_WARNING = `
Returning a Promise is the preferred way to send a reply from an
onMessage/onMessageExternal listener, as the sendResponse will be
Expand Down Expand Up @@ -96,7 +97,7 @@ if (typeof browser === "undefined") {
return (...callbackArgs) => {
if (extensionAPIs.runtime.lastError) {
promise.reject(extensionAPIs.runtime.lastError);
} else if (metadata.singleCallbackArg || callbackArgs.length === 1) {
} else if (metadata.singleCallbackArg || callbackArgs.length <= 1) {
promise.resolve(callbackArgs[0]);
} else {
promise.resolve(callbackArgs);
Expand Down Expand Up @@ -172,7 +173,7 @@ if (typeof browser === "undefined") {
* Wraps an existing method of the target object, so that calls to it are
* intercepted by the given wrapper function. The wrapper function receives,
* as its first argument, the original `target` object, followed by each of
* the arguments passed to the orginal method.
* the arguments passed to the original method.
*
* @param {object} target
* The original target object that the wrapped method belongs to.
Expand Down Expand Up @@ -446,7 +447,14 @@ if (typeof browser === "undefined") {

const wrappedSendMessageCallback = ({reject, resolve}, reply) => {
if (extensionAPIs.runtime.lastError) {
reject(extensionAPIs.runtime.lastError);
// Detect when none of the listeners replied to the sendMessage call and resolve
// the promise to undefined as in Firefox.
// See https://github.com/mozilla/webextension-polyfill/issues/130
if (extensionAPIs.runtime.lastError.message === CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE) {
resolve();
} else {
reject(extensionAPIs.runtime.lastError);
}
} else if (reply && reply.__mozWebExtensionPolyfillReject__) {
// Convert back the JSON representation of the error into
// an Error instance.
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/runtime-messaging-extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,20 @@ browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
case "test - sendMessage with listener callback throws":
throw new Error("listener throws");

case "test - sendMessage and no listener answers":
return undefined;

default:
return Promise.resolve(
`Unxpected message received by the background page: ${JSON.stringify(msg)}\n`);
}
});

browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === "test - sendMessage and no listener answers") {
return undefined;
}

setTimeout(() => {
sendResponse("second listener reply");
}, 100);
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/runtime-messaging-extension/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,8 @@ test("sendMessage with listener callback throws", async (t) => {
t.equal(err.message, "listener throws", "Got an error with the expected message");
}
});

test("sendMessage and no listener answers", async (t) => {
const reply = await browser.runtime.sendMessage("test - sendMessage and no listener answers");
t.equal(reply, undefined, "Got undefined reply as expected");
});
8 changes: 8 additions & 0 deletions test/test-async-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe("browser-polyfill", () => {
alarms: {clear: sinon.stub()},
runtime: {
lastError: null,
openOptionsPage: sinon.stub(),
requestUpdateCheck: sinon.stub(),
},
tabs: {
Expand All @@ -31,17 +32,24 @@ describe("browser-polyfill", () => {
fakeChrome.runtime.requestUpdateCheck
.onFirstCall().callsArgWith(0, "res1", "res2");

// Test for no callback arguments.
fakeChrome.runtime.openOptionsPage
.onFirstCall().callsArg(0);

return Promise.all([
window.browser.alarms.clear("test1"),
window.browser.tabs.query({active: true}),
window.browser.runtime.requestUpdateCheck(),
window.browser.runtime.openOptionsPage(),
]);
}).then(results => {
equal(results[0], "res1", "Fake alarms.clear call resolved to a single value");
deepEqual(results[1], ["res1", "res2"],
"Fake tabs.query resolved to an array of values");
deepEqual(results[2], ["res1", "res2"],
"Fake runtime.requestUpdateCheck resolved to an array of values");

equal(results[3], undefined, "Fake runtime.openOptionsPage resolved to a void value.");
});
});

Expand Down
25 changes: 25 additions & 0 deletions test/test-runtime-onMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,5 +238,30 @@ describe("browser-polyfill", () => {
});
});
});

it("resolves to undefined when no listeners reply", () => {
const fakeChrome = {
runtime: {
// This error message is defined as CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE
// in the polyfill sources and it is used to recognize when Chrome has detected that
// none of the listeners replied.
lastError: {
message: "The message port closed before a response was received.",
},
sendMessage: sinon.stub(),
},
};

fakeChrome.runtime.sendMessage.onFirstCall().callsArgWith(1, [undefined]);

return setupTestDOMWindow(fakeChrome).then(window => {
const promise = window.browser.runtime.sendMessage("some_message");
ok(fakeChrome.runtime.sendMessage.calledOnce, "sendMessage has been called once");

return promise.then(reply => {
deepEqual(reply, undefined, "sendMessage promise should be resolved to undefined");
});
});
});
});
});

0 comments on commit 39ac89c

Please sign in to comment.