From b132fecbcdd4fa7a3c6b61dc632e9a99d8684488 Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 16 Dec 2021 16:20:05 +0300 Subject: [PATCH] add a callstack to errors in `browser`, `API`, `msg` --- js/msg.js | 29 ++++++++++++++++++++--------- js/polyfill.js | 20 +++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/js/msg.js b/js/msg.js index 4b40dd0c..de2b00b3 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,15 @@ }; } - function unwrapResponse({data, error} = {error: {message: ERR_NO_RECEIVER}}) { + function unwrapResponseFactory(name) { + 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, { + stack: `${error.stack}\n${localErr.stack}`, + })) : data; } @@ -154,18 +160,23 @@ const bg = getExtBg() || chrome.tabs && await browser.runtime.getBackgroundPage().catch(() => {}); const message = {method: 'invokeAPI', path, args}; - let res; // content scripts, probably private tabs, and our extension tab during Chrome startup if (!bg) { - res = msg.send(message); - } else { - res = deepMerge(await bg.msg._execute(TARGETS.extension, message, { + return msg.send(message); + } + const err = new Error(`Callstack before invoking API.${path.join('.')}:`); + try { + return deepMerge(await bg.msg._execute(TARGETS.extension, message, { frameId: 0, // false in case of our Options frame but we really want to fetch styles early tab: NEEDS_TAB_IN_SENDER.includes(path.join('.')) && await getOwnTab(), url: location.href, })); + } catch (bgError) { + err.stack = `${bgError.stack}\n${err.stack}`; + err.message = bgError.message; + // Not using `throw` to avoid pausing debugger when it's configured to pause on exceptions + return Promise.reject(err); } - return res; }, }; /** @type {API} */ diff --git a/js/polyfill.js b/js/polyfill.js index 6d43aac7..b6f9d70a 100644 --- a/js/polyfill.js +++ b/js/polyfill.js @@ -23,14 +23,20 @@ }; const promisify = function (fn, ...args) { let res; + let resolve, reject; + 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) {