From 0ac01d2e22cdf4d16633dcb7b2d892f2c7ce6712 Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 16 Dec 2021 20:21:02 +0300 Subject: [PATCH] add a callstack to errors in `browser` and `msg` (#1369) --- js/msg.js | 15 +++++++++++---- js/polyfill.js | 21 ++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/js/msg.js b/js/msg.js index 4b40dd0c..87bcc074 100644 --- a/js/msg.js +++ b/js/msg.js @@ -78,12 +78,12 @@ send(data, target = 'extension') { return browser.runtime.sendMessage({data, target}) - .then(unwrapResponse); + .then(unwrapResponseFactory('send')); }, sendTab(tabId, data, options, target = 'tab') { return browser.tabs.sendMessage(tabId, {data, target}, options) - .then(unwrapResponse); + .then(unwrapResponseFactory('sendTab')); }, _execute(types, ...args) { @@ -138,9 +138,16 @@ }; } - function unwrapResponse({data, error} = {error: {message: ERR_NO_RECEIVER}}) { + function unwrapResponseFactory(name) { + // Saving the local callstack before making an async call + return unwrapResponse.bind(null, new Error(`Callstack before invoking msg.${name}:`)); + } + + function unwrapResponse(localErr, {data, error} = {error: {message: ERR_NO_RECEIVER}}) { return error - ? Promise.reject(Object.assign(new Error(error.message), error)) + ? Promise.reject(Object.assign(localErr, error, error.stack && { + stack: `${error.stack}\n${localErr.stack}`, + })) : data; } diff --git a/js/polyfill.js b/js/polyfill.js index 6d43aac7..d227ae21 100644 --- a/js/polyfill.js +++ b/js/polyfill.js @@ -23,14 +23,21 @@ }; const promisify = function (fn, ...args) { let res; + let resolve, reject; + // Saving the local callstack before making an async call + const err = new Error(); try { - let resolve, reject; - /* Some callbacks have 2 parameters so we're resolving as an array in that case. - For example, chrome.runtime.requestUpdateCheck and chrome.webRequest.onAuthRequired */ - args.push((...results) => - chrome.runtime.lastError ? - reject(new Error(chrome.runtime.lastError.message)) : - resolve(results.length <= 1 ? results[0] : results)); + args.push((...results) => { + const {lastError} = chrome.runtime; + if (lastError) { + err.message = lastError.message; + reject(err); + } else { + /* Some callbacks have 2 parameters so we're resolving as an array in that case. + For example, chrome.runtime.requestUpdateCheck and chrome.webRequest.onAuthRequired */ + resolve(results.length <= 1 ? results[0] : results); + } + }); fn.apply(this, args); res = new Promise((...rr) => ([resolve, reject] = rr)); } catch (err) {