Change: simplify msg.js (#544)

* Fix: make API work in private windows

* Change: simplify msg.js
This commit is contained in:
eight 2018-11-11 21:07:30 +08:00 committed by Rob Garrison
parent b622ebc172
commit b2657e3ebd

151
js/msg.js
View File

@ -4,30 +4,20 @@
'use strict'; 'use strict';
const msg = (() => { const msg = (() => {
let isBg = false; const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime));
if (chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) { const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
isBg = true; const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs));
const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
if (isBg) {
window._msg = { window._msg = {
id: 1,
storage: new Map(),
handler: null, handler: null,
clone: deepCopy clone: deepCopy
}; };
} }
const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime)); const bgReady = getBg();
const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs));
let bg;
const preparing = !isBg && chrome.runtime.getBackgroundPage &&
promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))()
.catch(() => null)
.then(_bg => {
bg = _bg;
});
bg = isBg ? window : !preparing ? null : undefined;
const EXTENSION_URL = chrome.runtime.getURL(''); const EXTENSION_URL = chrome.runtime.getURL('');
let handler; let handler;
const from_ = location.href.startsWith(EXTENSION_URL) ? 'extension' : 'content';
const RX_NO_RECEIVER = /Receiving end does not exist/; const RX_NO_RECEIVER = /Receiving end does not exist/;
const RX_PORT_CLOSED = /The message port closed before a response was received/; const RX_PORT_CLOSED = /The message port closed before a response was received/;
return { return {
@ -46,33 +36,29 @@ const msg = (() => {
RX_PORT_CLOSED RX_PORT_CLOSED
}; };
function getBg() {
if (isBg) {
return Promise.resolve(window);
}
if (!chrome.runtime.getBackgroundPage) {
return Promise.resolve();
}
return promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))()
.catch(() => null);
}
function send(data, target = 'extension') { function send(data, target = 'extension') {
if (bg === undefined) { const message = {data, target};
return preparing.then(() => send(data, target)); return runtimeSend(message).then(unwrapData);
}
const message = {type: 'direct', data, target, from: from_};
if (bg) {
exchangeSet(message);
}
const request = runtimeSend(message).then(unwrapData);
if (message.id) {
return withCleanup(request, () => bg._msg.storage.delete(message.id));
}
return request;
} }
function sendTab(tabId, data, options, target = 'tab') { function sendTab(tabId, data, options, target = 'tab') {
return tabSend(tabId, {type: 'direct', data, target, from: from_}, options) return tabSend(tabId, {data, target}, options)
.then(unwrapData); .then(unwrapData);
} }
function sendBg(data) { function sendBg(data) {
if (bg === undefined) { return bgReady.then(bg => {
return preparing.then(doSend);
}
return withPromiseError(doSend);
function doSend() {
if (bg) { if (bg) {
if (!bg._msg.handler) { if (!bg._msg.handler) {
throw new Error('there is no bg handler'); throw new Error('there is no bg handler');
@ -84,7 +70,7 @@ const msg = (() => {
.then(deepCopy); .then(deepCopy);
} }
return send(data); return send(data);
} });
} }
function ignoreError(err) { function ignoreError(err) {
@ -126,15 +112,12 @@ const msg = (() => {
if (!dataObj) { if (!dataObj) {
continue; continue;
} }
const message = {type: 'direct', data: dataObj, target, from: from_}; const message = {data: dataObj, target};
if (isExtension) { requests.push(
exchangeSet(message); tabSend(tab.id, message, options)
} .then(unwrapData)
let request = tabSend(tab.id, message, options).then(unwrapData); .catch(ignoreError)
if (message.id) { );
request = withCleanup(request, () => bg._msg.storage.delete(message.id));
}
requests.push(request.catch(ignoreError));
} }
return Promise.all(requests); return Promise.all(requests);
}); });
@ -178,7 +161,7 @@ const msg = (() => {
extension: [] extension: []
}; };
if (isBg) { if (isBg) {
bg._msg.handler = handler; window._msg.handler = handler;
} }
chrome.runtime.onMessage.addListener(handleMessage); chrome.runtime.onMessage.addListener(handleMessage);
} }
@ -202,16 +185,6 @@ const msg = (() => {
if (!handlers.length) { if (!handlers.length) {
return; return;
} }
if (message.type === 'exchange') {
const pending = exchangeGet(message, true);
if (pending) {
pending.then(response);
return true;
}
}
return response();
function response() {
const result = executeCallbacks(handlers, message.data, sender); const result = executeCallbacks(handlers, message.data, sender);
if (result === undefined) { if (result === undefined) {
return; return;
@ -231,42 +204,9 @@ const msg = (() => {
}, err) // this allows us to pass custom properties e.g. `err.index` }, err) // this allows us to pass custom properties e.g. `err.index`
}) })
) )
.then(function doResponse(responseMessage) {
if (message.from === 'extension' && bg === undefined) {
return preparing.then(() => doResponse(responseMessage));
}
if (message.from === 'extension' && bg) {
exchangeSet(responseMessage);
} else {
responseMessage.type = 'direct';
}
return responseMessage;
})
.then(sendResponse); .then(sendResponse);
return true; return true;
} }
}
function exchangeGet(message, keepStorage = false) {
if (bg === undefined) {
return preparing.then(() => exchangeGet(message, keepStorage));
}
message.data = bg._msg.storage.get(message.id);
if (keepStorage) {
message.data = deepCopy(message.data);
} else {
bg._msg.storage.delete(message.id);
}
}
function exchangeSet(message) {
const id = bg._msg.id;
bg._msg.storage.set(id, message.data);
bg._msg.id++;
message.type = 'exchange';
message.id = id;
delete message.data;
}
function withPromiseError(fn, ...args) { function withPromiseError(fn, ...args) {
try { try {
@ -276,47 +216,16 @@ const msg = (() => {
} }
} }
function withCleanup(p, fn) {
return p.then(
result => {
cleanup();
return result;
},
err => {
cleanup();
throw err;
}
);
function cleanup() {
try {
fn();
} catch (err) {
// pass
}
}
}
// {type, error, data, id} // {type, error, data, id}
function unwrapData(result) { function unwrapData(result) {
if (result === undefined) { if (result === undefined) {
throw new Error('Receiving end does not exist'); throw new Error('Receiving end does not exist');
} }
if (result.type === 'exchange') {
const pending = exchangeGet(result);
if (pending) {
return pending.then(unwrap);
}
}
return unwrap();
function unwrap() {
if (result.error) { if (result.error) {
throw Object.assign(new Error(result.data.message), result.data); throw Object.assign(new Error(result.data.message), result.data);
} }
return result.data; return result.data;
} }
}
})(); })();
const API = new Proxy({}, { const API = new Proxy({}, {