Bug 1301862 - Call tabs.create sooner r=kmag

Call tabs.create immediately after the tab is created without delay to
reduce the chance of losing tabs.onUpdated events.

If needed, the tab waiting is done by `executeScript`, etc.

MozReview-Commit-ID: 7A1zH99zafK

--HG--
extra : rebase_source : 56fb363f45040e76a120e6a3de8feaad1575c337
This commit is contained in:
Rob Wu 2016-09-11 01:32:24 -07:00
parent bdf0719848
commit 743d610ca6

View File

@ -8,7 +8,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
@ -242,6 +243,7 @@ let tabListener = {
tabReadyInitialized: false,
tabReadyPromises: new WeakMap(),
initializingTabs: new WeakSet(),
initTabReady() {
if (!this.tabReadyInitialized) {
@ -256,6 +258,10 @@ let tabListener = {
let gBrowser = browser.ownerGlobal.gBrowser;
let tab = gBrowser.getTabForBrowser(browser);
// Now we are certain that the first page in the tab was loaded.
this.initializingTabs.delete(tab);
// browser.innerWindowID is now set, resolve the promises if any.
let deferred = this.tabReadyPromises.get(tab);
if (deferred) {
deferred.resolve(tab);
@ -264,10 +270,25 @@ let tabListener = {
}
},
/**
* Returns a promise that resolves when the tab is ready.
* Tabs created via the `tabs.create` method are "ready" once the location
* changed to the requested URL. Other tabs are always assumed to be ready.
*
* @param {XULElement} tab The <tab> element.
* @returns {Promise} Resolves with the given tab once ready.
*/
awaitTabReady(tab) {
return new Promise((resolve, reject) => {
this.tabReadyPromises.set(tab, {resolve, reject});
});
let deferred = this.tabReadyPromises.get(tab);
if (!deferred) {
deferred = PromiseUtils.defer();
if (!this.initializingTabs.has(tab) && tab.linkedBrowser.innerWindowID) {
deferred.resolve(tab);
} else {
this.tabReadyPromises.set(tab, deferred);
}
}
return deferred.promise;
},
};
@ -538,18 +559,18 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
window.gBrowser.pinTab(tab);
}
if (!createProperties.url || createProperties.url.startsWith("about:")) {
if (createProperties.url && !createProperties.url.startsWith("about:")) {
// We can't wait for a location change event for about:newtab,
// since it may be pre-rendered, in which case its initial
// location change event has already fired.
return tab;
// Mark the tab as initializing, so that operations like
// `executeScript` wait until the requested URL is loaded in
// the tab before dispatching messages to the inner window
// that contains the URL we're attempting to load.
tabListener.initializingTabs.add(tab);
}
// Wait for the first location change event, so that operations
// like `executeScript` are dispatched to the inner window that
// contains the URL we're attempting to load.
return tabListener.awaitTabReady(tab);
}).then(tab => {
return TabManager.convert(extension, tab);
});
},
@ -717,45 +738,49 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
WindowManager.topWindow :
WindowManager.getWindow(windowId, context);
let browser = window.gBrowser.selectedBrowser;
let recipient = {
innerWindowID: browser.innerWindowID,
};
let tab = window.gBrowser.selectedTab;
return tabListener.awaitTabReady(tab).then(() => {
let browser = tab.linkedBrowser;
let recipient = {
innerWindowID: browser.innerWindowID,
};
if (!options) {
options = {};
}
if (options.format == null) {
options.format = "png";
}
if (options.quality == null) {
options.quality = 92;
}
if (!options) {
options = {};
}
if (options.format == null) {
options.format = "png";
}
if (options.quality == null) {
options.quality = 92;
}
let message = {
options,
width: browser.clientWidth,
height: browser.clientHeight,
};
let message = {
options,
width: browser.clientWidth,
height: browser.clientHeight,
};
return context.sendMessage(browser.messageManager, "Extension:Capture",
message, {recipient});
return context.sendMessage(browser.messageManager, "Extension:Capture",
message, {recipient});
});
},
detectLanguage: function(tabId) {
let tab = tabId !== null ? TabManager.getTab(tabId, context) : TabManager.activeTab;
let browser = tab.linkedBrowser;
let recipient = {innerWindowID: browser.innerWindowID};
return tabListener.awaitTabReady(tab).then(() => {
let browser = tab.linkedBrowser;
let recipient = {innerWindowID: browser.innerWindowID};
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
{}, {recipient});
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
{}, {recipient});
});
},
// Used to executeScript, insertCSS and removeCSS.
_execute: function(tabId, details, kind, method) {
let tab = tabId !== null ? TabManager.getTab(tabId, context) : TabManager.activeTab;
let mm = tab.linkedBrowser.messageManager;
let options = {
js: [],
@ -772,10 +797,6 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
return Promise.reject({message: `'frameId' and 'allFrames' are mutually exclusive`});
}
let recipient = {
innerWindowID: tab.linkedBrowser.innerWindowID,
};
if (TabManager.for(extension).hasActiveTabPermission(tab)) {
// If we have the "activeTab" permission for this tab, ignore
// the host whitelist.
@ -809,7 +830,14 @@ extensions.registerSchemaAPI("tabs", "addon_parent", context => {
options.run_at = "document_idle";
}
return context.sendMessage(mm, "Extension:Execute", {options}, {recipient});
return tabListener.awaitTabReady(tab).then(() => {
let browser = tab.linkedBrowser;
let recipient = {
innerWindowID: browser.innerWindowID,
};
return context.sendMessage(browser.messageManager, "Extension:Execute", {options}, {recipient});
});
},
executeScript: function(tabId, details) {