diff --git a/background/background.js b/background/background.js index 434921e4..ed9aa30d 100644 --- a/background/background.js +++ b/background/background.js @@ -24,6 +24,11 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, { openEditor, updateIcon, + // exposed for stuff that requires followup sendMessage() like popup::openSettings + // that would fail otherwise if another extension forced the tab to open + // in the foreground thus auto-closing the popup (in Chrome) + openURL, + closeTab: (msg, sender, respond) => { chrome.tabs.remove(msg.tabId || sender.tab.id, () => { if (chrome.runtime.lastError && msg.tabId !== sender.tab.id) { diff --git a/js/messaging.js b/js/messaging.js index 6de837f0..fba3a154 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -282,18 +282,28 @@ function getTabRealURL(tab) { /** * Opens a tab or activates an existing one, * reuses the New Tab page or about:blank if it's focused now - * @param {Object} params - or just a string e.g. openURL('foo') - * @param {string} params.url - if relative, it's auto-expanded to the full extension URL - * @param {number} [params.index] - move the tab to this index in the tab strip, -1 = last - * @param {Boolean} [params.active=tue] - true to activate the tab, false to open in background - * @param {?Boolean} [params.currentWindow=true] - pass null to check all windows - * @returns {Promise} + * @param {Object} params + * or just a string e.g. openURL('foo') + * @param {string} params.url + * if relative, it's auto-expanded to the full extension URL + * @param {number} [params.index] + * move the tab to this index in the tab strip, -1 = last + * @param {Boolean} [params.active] + * true to activate the tab (this is the default value in the extensions API), + * false to open in background + * @param {?Boolean} [params.currentWindow] + * pass null to check all windows + * @param {any} [params.message] + * JSONifiable data to be sent to the tab via sendMessage() repeatedly for 200ms + * until the remote end returns a non-undefined response + * @returns {Promise} Promise that resolves to the opened/activated tab */ function openURL({ url = arguments[0], index, active, currentWindow = true, + message, }) { url = url.includes('://') ? url : chrome.runtime.getURL(url); // [some] chromium forks don't handle their fake branded protocols @@ -305,7 +315,21 @@ function openURL({ FIREFOX && url.includes('%2F') ? url.replace(/%2F.*/, '*').replace(/#.*/, '') : url.replace(/#.*/, ''); - return queryTabs({url: urlQuery, currentWindow}).then(maybeSwitch); + + let task = queryTabs({url: urlQuery, currentWindow}).then(maybeSwitch); + + if (message) { + task = task.then(function poll(tab, t0 = performance.now()) { + message.tabId = tab.id; + return sendMessage(message) + .then(ack => ack !== undefined ? tab : Promise.reject()) + .catch(() => { + ignoreChromeError(); + return performance.now() - t0 < 200 ? poll(tab) : tab; + }); + }); + } + return task; function maybeSwitch(tabs = []) { const urlFF = FIREFOX && url.replace(/%2F/g, '/'); diff --git a/popup/popup.js b/popup/popup.js index 821af770..801b00f7 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -427,22 +427,12 @@ Object.assign(handleEvent, { openURLandHide(event) { event.preventDefault(); - const el = this; - const msg = tryJSONparse(el.dataset.sendMessage); getActiveTab() - .then(activeTab => openURL({ + .then(activeTab => API.openURL({ url: this.href || this.dataset.href, index: activeTab.index + 1, - active: msg ? false : undefined, + message: tryJSONparse(this.dataset.sendMessage), })) - .then(msg && ( - function poll(tab, t0 = performance.now()) { - msg.tabId = tab.id; - return sendMessage(msg) - .catch(ignoreChromeError) - .then(handled => handled || performance.now() - t0 < 200 && poll(tab)) - .then(() => chrome.tabs.update(tab.id, {active: true})); - })) .then(window.close); },