Skip to content

Commit

Permalink
Merge branch 'async'
Browse files Browse the repository at this point in the history
  • Loading branch information
chatziko committed Mar 18, 2022
2 parents 5dd751d + c40064f commit 0c8d92d
Show file tree
Hide file tree
Showing 12 changed files with 603 additions and 611 deletions.
215 changes: 113 additions & 102 deletions src/js/common/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ Browser._main_script = function() {
// some operations cannot be done by other scripts, so we set
// handlers to do them in the main script
//
Browser.rpc.register('refreshIcon', function(tabId, callerTabId, replyHandler) {
Browser.rpc.register('refreshIcon', async function(tabId, callerTabId) {
// 'self' tabId in the content script means refresh its own tab
Browser.gui.refreshIcon(tabId == 'self' ? callerTabId : tabId, replyHandler);
return true; // will reply later
await Browser.gui.refreshIcon(tabId == 'self' ? callerTabId : tabId);
});

Browser.rpc.register('closeTab', function(tabId) {
Expand Down Expand Up @@ -98,19 +97,28 @@ Browser.rpc._listener = function(message, sender, replyHandler) {
// add tabId and replyHandler to the arguments
var args = message.args || [];
var tabId = sender.tab ? sender.tab.id : null;
args.push(tabId, replyHandler);

return handler.apply(null, args);
args.push(tabId);

const res = handler.apply(null, args);
if(res instanceof Promise) {
// handler returned a Promise. We return true here, to indicate that the response will come later.
res.then(replyHandler);
return true;
} else {
// We have a response right now
replyHandler(res);
return false;
}
};

Browser.rpc.call = function(tabId, name, args, cb) {
var message = { method: name, args: args };
if(!cb) cb = function() {}; // we get error of not cb is passed

if(tabId)
browser.tabs.sendMessage(tabId, message, cb);
else
browser.runtime.sendMessage(null, message, cb);
Browser.rpc.call = async function(tabId, name, args) {
return new Promise(resolve => {
var message = { method: name, args: args };
if(tabId)
browser.tabs.sendMessage(tabId, message, resolve);
else
browser.runtime.sendMessage(null, message, resolve);
});
}


Expand All @@ -124,52 +132,57 @@ Browser.rpc.call = function(tabId, name, args, cb) {
//
Browser.storage._key = "global"; // store everything under this key

Browser.storage.get = function(cb) {
browser.storage.local.get(Browser.storage._key, function(items) {
var st = items[Browser.storage._key];

// default values
if(!st) {
st = Browser.storage._default;
Browser.storage.set(st);
}
cb(st);
Browser.storage.get = async function() {
return new Promise(resolve => {
browser.storage.local.get(Browser.storage._key, function(items) {
var st = items[Browser.storage._key];

// default values
if(!st) {
st = Browser.storage._default;
Browser.storage.set(st);
}
resolve(st);
});
});
};

Browser.storage.set = function(st, handler) {
Browser.log('saving st', st);
var items = {};
items[Browser.storage._key] = st;
browser.storage.local.set(items, handler);
Browser.storage.set = async function(st) {
return new Promise(resolve => {
Browser.log('saving st', st);
var items = {};
items[Browser.storage._key] = st;
browser.storage.local.set(items, resolve);
});
};

Browser.storage.clear = function(handler) {
browser.storage.local.clear(handler);
Browser.storage.clear = async function() {
return new Promise(resolve => {
browser.storage.local.clear(resolve);
});
};


//////////////////// gui ///////////////////////////
//
//
Browser.gui.refreshIcon = function(tabId, cb) {
Browser.gui.refreshIcon = async function(tabId) {
// delegate the call to the 'main' script if:
// - we're in 'content': browser.pageAction/browserAction is not available there
// - we use the FF pageAction workaround: we need to update Browser.gui.iconShown in 'main'
//
if(Browser._script == 'content' ||
(Browser._script != 'main' && Browser.capabilities.needsPAManualHide())
) {
Browser.rpc.call(null, 'refreshIcon', [tabId], cb);
await Browser.rpc.call(null, 'refreshIcon', [tabId]);
return;
}

Util.getIconInfo(tabId, function(info) {
if(Browser.capabilities.permanentIcon())
Browser.gui._refreshBrowserAction(tabId, info, cb);
else
Browser.gui._refreshPageAction(tabId, info, cb);
});
const info = await Util.getIconInfo(tabId);
if(Browser.capabilities.permanentIcon())
await Browser.gui._refreshBrowserAction(tabId, info);
else
await Browser.gui._refreshPageAction(tabId, info);
};

Browser.gui._icons = function(private) {
Expand All @@ -180,97 +193,95 @@ Browser.gui._icons = function(private) {
return ret;
}

Browser.gui._refreshPageAction = function(tabId, info, cb) {
Browser.gui._refreshPageAction = function(tabId, info) {
if(info.hidden || info.apiCalls == 0) {
browser.pageAction.hide(tabId);
if(cb) cb();
return;
}

if(Browser.gui.iconShown)
Browser.gui.iconShown[tabId] = 1;
return new Promise(resolve => {
if(Browser.gui.iconShown)
Browser.gui.iconShown[tabId] = 1;

browser.pageAction.setPopup({
tabId: tabId,
popup: "popup.html?tabId=" + tabId // pass tabId in the url
});
browser.pageAction.show(tabId);
browser.pageAction.setPopup({
tabId: tabId,
popup: "popup.html?tabId=" + tabId // pass tabId in the url
});
browser.pageAction.show(tabId);

// Firefox on Android (version 56) doesn't support pageAction.setIcon/setTitle so we try/catch
try {
browser.pageAction.setTitle({
tabId: tabId,
title: info.title
});
browser.pageAction.setIcon({
tabId: tabId,
path: Browser.gui._icons(info.private)
}, cb); // setIcon is the only pageAction.set* method with a callback
} catch(e) {
if(cb) cb();
}
}, resolve); // setIcon is the only pageAction.set* method with a callback
});
}

Browser.gui._refreshBrowserAction = function(tabId, info, cb) {
browser.browserAction.setTitle({
tabId: tabId,
title: info.title
});
browser.browserAction.setBadgeText({
tabId: tabId,
text: (info.apiCalls || "").toString()
});
browser.browserAction.setBadgeBackgroundColor({
tabId: tabId,
color: "#b12222"
});
browser.browserAction.setPopup({
tabId: tabId,
popup: "popup.html" + (tabId ? "?tabId="+tabId : "") // pass tabId in the url
Browser.gui._refreshBrowserAction = function(tabId, info) {
return new Promise(resolve => {
browser.browserAction.setTitle({
tabId: tabId,
title: info.title
});
browser.browserAction.setBadgeText({
tabId: tabId,
text: (info.apiCalls || "").toString()
});
browser.browserAction.setBadgeBackgroundColor({
tabId: tabId,
color: "#b12222"
});
browser.browserAction.setPopup({
tabId: tabId,
popup: "popup.html" + (tabId ? "?tabId="+tabId : "") // pass tabId in the url
});
browser.browserAction.setIcon({
tabId: tabId,
path: Browser.gui._icons(info.private)
}, resolve); // setIcon is the only browserAction.set* method with a callback
});
browser.browserAction.setIcon({
tabId: tabId,
path: Browser.gui._icons(info.private)
}, cb); // setIcon is the only browserAction.set* method with a callback
}

Browser.gui.refreshAllIcons = function(cb) {
browser.tabs.query({}, function(tabs) {
// for browser action, also refresh default state (null tabId)
if(Browser.capabilities.permanentIcon())
tabs.push({ id: null });

var done = 0;
for(var i = 0; i < tabs.length; i++)
Browser.gui.refreshIcon(tabs[i].id, function() {
if(++done == tabs.length && cb)
cb();
});
Browser.gui.refreshAllIcons = async function() {
return new Promise(resolve => {
browser.tabs.query({}, async function(tabs) {
// for browser action, also refresh default state (null tabId)
if(Browser.capabilities.permanentIcon())
tabs.push({ id: null });

for(var i = 0; i < tabs.length; i++)
await Browser.gui.refreshIcon(tabs[i].id);

resolve();
});
});
};

Browser.gui.showPage = function(name) {
browser.tabs.create({ url: browser.extension.getURL(name) });
};

Browser.gui.getCallUrl = function(tabId, handler) {
function fetch(tabId) {
// we call getState from the content script
//
Browser.rpc.call(tabId, 'getState', [], function(state) {
handler(state && state.callUrl); // state might be null if no content script runs in the tab
});
Browser.gui.getCallUrl = async function(tabId) {
async function getActiveTabId() {
return new Promise(resolve => {
browser.tabs.query({
active: true, // Select active tabs
lastFocusedWindow: true // In the current window
}, async function(tabs) {
resolve(tabs[0].id);
});
})
}

if(tabId)
fetch(tabId);
else
browser.tabs.query({
active: true, // Select active tabs
lastFocusedWindow: true // In the current window
}, function(tabs) {
fetch(tabs[0].id)
});
if(!tabId)
tabId = await getActiveTabId();

// we call getState from the content script
const state = await Browser.rpc.call(tabId, 'getState', []);
return state && state.callUrl; // state might be null if no content script runs in the tab
};

Browser.gui.closePopup = function() {
Expand Down
43 changes: 19 additions & 24 deletions src/js/common/browser_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,24 @@ const Browser = {
//
// Registers a method to be callable from other scripts.
// handler should be a function
// function(...args..., tabId, replyHandler)
// async function(...args..., tabId)
//
// The function receives any arguments passed during the call (see Browser.rpc.call)
// Moreover, two extra arguments are automatically added:
// Moreover, one extra arguments are automatically added:
// tabId: the tabId of the caller, or null if the call is made from the main script
// replyHandler: function for asynchronously returning a result by calling replyHandler(result)
//
// IMPORTANT: If handler does not immediately return a result but stores replyHandler to do it asynchronously later,
// it should return a true value to keep replyHandler open.
//
register: function(name, handler) {},

// Browser.rpc.call(tabId, name, args, handler)
// Browser.rpc.call(tabId, name, args)
//
// Calls a remote method.
// tabId: tab id of the script to call, or null to call the main script
// name: method name
// args: array of arguments to pass
// handler: function(res), will be called when the result is received
//
// If the call cannot be made to the specific tabId, handler will be called with no arguments.
// If the call cannot be made to the specific tabId, null is returned.
//
call: function(tabId, name, args, handler) {}
call: async function(tabId, name, args) {}
},

// Browser.storage
Expand All @@ -62,24 +57,24 @@ const Browser = {
// else that needs to be stored. It is fetched and stored as a whole.
//
storage: {
// browser.storage.get(handler)
// browser.storage.get()
//
// fetches the storage object and passes it to the handler.
// fetches the storage object.
// The default object is returned if the storage is empty.
//
get: function(handler) {},
get: async function() {},

// browser.storage.set(st, handler)
// browser.storage.set(st)
//
// Stores the give storage object. Calls the handler when finished.
// Stores the give storage object.
//
set: function(st, handler) {},
set: async function(st) {},

// browser.storage.clear(handler)
// browser.storage.clear()
//
// Clears the storage. Calls the handler when finished.
// Clears the storage.
//
clear: function(handler) {},
clear: async function() {},

// default storage object
//
Expand Down Expand Up @@ -135,14 +130,14 @@ const Browser = {
// If called from a content script and tabId = 'self' it refreshes the icon of the content script's tab.
// getIconInfo should be called to get the icon's info
//
refreshIcon: function(tabId) {},
refreshIcon: async function(tabId) {},

// Browser.gui.refreshAllIcons()
//
// Refreshes the icons of all tabs.
// getIconInfo should be called to get the icon's info
//
refreshAllIcons: function() {},
refreshAllIcons: async function() {},

// Browser.gui.showPage(name)
//
Expand All @@ -151,11 +146,11 @@ const Browser = {
//
showPage: function(name) {},

// Browser.gui.getCallUrl(tabId, handler)
// Browser.gui.getCallUrl(tabId)
//
// Gets the callUrl of given tab and passes it to 'handler'
// Gets the callUrl of given tab.
//
getActiveCallUrl: function(tabId, handler) {},
getCallUrl: async function(tabId) {},

// Browser.gui.closePopup()
//
Expand Down
Loading

0 comments on commit 0c8d92d

Please sign in to comment.