mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Bug 1272774 - allow listTabs to return favicon data from PlacesUtils;r=ochameau
MozReview-Commit-ID: 8bkn3mG6YkL --HG-- extra : rebase_source : 9f514f1adbc4e79022c8c18c5195d3e1d1298062
This commit is contained in:
parent
f714f056d0
commit
1d94b51b97
@ -51,29 +51,22 @@ class TabsPanel extends Component {
|
||||
client.removeListener("tabListChanged", this.update);
|
||||
}
|
||||
|
||||
update() {
|
||||
this.props.client.mainRoot.listTabs().then(({ tabs }) => {
|
||||
// Filter out closed tabs (represented as `null`).
|
||||
tabs = tabs.filter(tab => !!tab);
|
||||
tabs.forEach(tab => {
|
||||
// FIXME Also try to fetch low-res favicon. But we should use actor
|
||||
// support for this to get the high-res one (bug 1061654).
|
||||
let url = new URL(tab.url);
|
||||
if (url.protocol.startsWith("http")) {
|
||||
let prePath = url.origin;
|
||||
let idx = url.pathname.lastIndexOf("/");
|
||||
if (idx === -1) {
|
||||
prePath += url.pathname;
|
||||
} else {
|
||||
prePath += url.pathname.substr(0, idx);
|
||||
}
|
||||
tab.icon = prePath + "/favicon.ico";
|
||||
} else {
|
||||
tab.icon = "chrome://devtools/skin/images/globe.svg";
|
||||
}
|
||||
});
|
||||
this.setState({ tabs });
|
||||
});
|
||||
async update() {
|
||||
let { tabs } = await this.props.client.mainRoot.listTabs({ favicons: true });
|
||||
|
||||
// Filter out closed tabs (represented as `null`).
|
||||
tabs = tabs.filter(tab => !!tab);
|
||||
|
||||
for (let tab of tabs) {
|
||||
if (tab.favicon) {
|
||||
let base64Favicon = btoa(String.fromCharCode.apply(String, tab.favicon));
|
||||
tab.icon = "data:image/png;base64," + base64Favicon;
|
||||
} else {
|
||||
tab.icon = "chrome://devtools/skin/images/globe.svg";
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ tabs });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -29,6 +29,23 @@ add_task(function* () {
|
||||
return container.querySelector(".target-name").title === TAB_URL;
|
||||
}, 100);
|
||||
|
||||
let icon = container.querySelector(".target-icon");
|
||||
ok(icon && icon.src, "Tab icon found and src attribute is not empty");
|
||||
|
||||
info("Check if the tab icon is a valid image");
|
||||
yield new Promise(r => {
|
||||
let image = new Image();
|
||||
image.onload = () => {
|
||||
ok(true, "Favicon is not a broken image");
|
||||
r();
|
||||
};
|
||||
image.onerror = () => {
|
||||
ok(false, "Favicon is a broken image");
|
||||
r();
|
||||
};
|
||||
image.src = icon.src;
|
||||
});
|
||||
|
||||
// Finally, close the tab
|
||||
yield removeTab(newTab);
|
||||
|
||||
|
@ -286,7 +286,7 @@ RootActor.prototype = {
|
||||
* would trigger any lazy tabs to be loaded, greatly increasing resource usage. Avoid
|
||||
* this method whenever possible.
|
||||
*/
|
||||
onListTabs: async function () {
|
||||
onListTabs: async function (request) {
|
||||
let tabList = this._parameters.tabList;
|
||||
if (!tabList) {
|
||||
return { from: this.actorID, error: "noTabs",
|
||||
@ -305,7 +305,8 @@ RootActor.prototype = {
|
||||
let tabActorList = [];
|
||||
let selected;
|
||||
|
||||
let tabActors = await tabList.getList();
|
||||
let options = request.options || {};
|
||||
let tabActors = await tabList.getList(options);
|
||||
for (let tabActor of tabActors) {
|
||||
if (tabActor.exited) {
|
||||
// Tab actor may have exited while we were gathering the list.
|
||||
|
@ -20,6 +20,7 @@ loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker
|
||||
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker-list", true);
|
||||
loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
loader.lazyImporter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
/**
|
||||
* Browser-specific actors.
|
||||
@ -256,7 +257,7 @@ BrowserTabList.prototype._getChildren = function (window) {
|
||||
});
|
||||
};
|
||||
|
||||
BrowserTabList.prototype.getList = function () {
|
||||
BrowserTabList.prototype.getList = function (browserActorOptions) {
|
||||
let topXULWindow = Services.wm.getMostRecentWindow(
|
||||
DebuggerServer.chromeWindowType);
|
||||
let selectedBrowser = null;
|
||||
@ -279,7 +280,7 @@ BrowserTabList.prototype.getList = function () {
|
||||
for (let browser of this._getBrowsers()) {
|
||||
let selected = browser === selectedBrowser;
|
||||
actorPromises.push(
|
||||
this._getActorForBrowser(browser)
|
||||
this._getActorForBrowser(browser, browserActorOptions)
|
||||
.then(actor => {
|
||||
// Set the 'selected' properties on all actors correctly.
|
||||
actor.selected = selected;
|
||||
@ -309,15 +310,18 @@ BrowserTabList.prototype.getList = function () {
|
||||
});
|
||||
};
|
||||
|
||||
BrowserTabList.prototype._getActorForBrowser = function (browser) {
|
||||
/**
|
||||
* @param browserActorOptions see options argument of BrowserTabActor constructor.
|
||||
*/
|
||||
BrowserTabList.prototype._getActorForBrowser = function (browser, browserActorOptions) {
|
||||
// Do we have an existing actor for this browser? If not, create one.
|
||||
let actor = this._actorByBrowser.get(browser);
|
||||
if (actor) {
|
||||
this._foundCount++;
|
||||
return actor.update();
|
||||
return actor.update(browserActorOptions);
|
||||
}
|
||||
|
||||
actor = new BrowserTabActor(this._connection, browser);
|
||||
actor = new BrowserTabActor(this._connection, browser, browserActorOptions);
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
this._checkListening();
|
||||
return actor.connect();
|
||||
@ -695,16 +699,19 @@ exports.BrowserTabList = BrowserTabList;
|
||||
*
|
||||
* @param connection The main RDP connection.
|
||||
* @param browser <xul:browser> or <iframe mozbrowser> element to connect to.
|
||||
* @param options
|
||||
* - {Boolean} favicons: true if the form should include the favicon for the tab.
|
||||
*/
|
||||
function BrowserTabActor(connection, browser) {
|
||||
function BrowserTabActor(connection, browser, options = {}) {
|
||||
this._conn = connection;
|
||||
this._browser = browser;
|
||||
this._form = null;
|
||||
this.exited = false;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
BrowserTabActor.prototype = {
|
||||
connect() {
|
||||
async connect() {
|
||||
let onDestroy = () => {
|
||||
if (this._deferredUpdate) {
|
||||
// Reject the update promise if the tab was destroyed while requesting an update
|
||||
@ -716,10 +723,14 @@ BrowserTabActor.prototype = {
|
||||
this.exit();
|
||||
};
|
||||
let connect = DebuggerServer.connectToChild(this._conn, this._browser, onDestroy);
|
||||
return connect.then(form => {
|
||||
this._form = form;
|
||||
return this;
|
||||
});
|
||||
let form = await connect;
|
||||
|
||||
this._form = form;
|
||||
if (this.options.favicons) {
|
||||
this._form.favicon = await this.getFaviconData();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
get _tabbrowser() {
|
||||
@ -736,27 +747,52 @@ BrowserTabActor.prototype = {
|
||||
this._browser.frameLoader.messageManager;
|
||||
},
|
||||
|
||||
update() {
|
||||
async getFaviconData() {
|
||||
try {
|
||||
let { data } = await PlacesUtils.promiseFaviconData(this._form.url);
|
||||
return data;
|
||||
} catch (e) {
|
||||
// Favicon unavailable for this url.
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* See BrowserTabActor constructor.
|
||||
*/
|
||||
async update(options = {}) {
|
||||
// Update the BrowserTabActor options.
|
||||
this.options = options;
|
||||
|
||||
// If the child happens to be crashed/close/detach, it won't have _form set,
|
||||
// so only request form update if some code is still listening on the other
|
||||
// side.
|
||||
if (!this.exited) {
|
||||
this._deferredUpdate = defer();
|
||||
if (this.exited) {
|
||||
return this.connect();
|
||||
}
|
||||
|
||||
let form = await new Promise(resolve => {
|
||||
let onFormUpdate = msg => {
|
||||
// There may be more than just one childtab.js up and running
|
||||
if (this._form.actor != msg.json.actor) {
|
||||
return;
|
||||
}
|
||||
this._mm.removeMessageListener("debug:form", onFormUpdate);
|
||||
this._form = msg.json;
|
||||
this._deferredUpdate.resolve(this);
|
||||
|
||||
resolve(msg.json);
|
||||
};
|
||||
|
||||
this._mm.addMessageListener("debug:form", onFormUpdate);
|
||||
this._mm.sendAsyncMessage("debug:form");
|
||||
return this._deferredUpdate.promise;
|
||||
});
|
||||
|
||||
this._form = form;
|
||||
if (this.options.favicons) {
|
||||
this._form.favicon = await this.getFaviconData();
|
||||
}
|
||||
|
||||
return this.connect();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -809,6 +845,7 @@ BrowserTabActor.prototype = {
|
||||
if (!form.url) {
|
||||
form.url = this.url;
|
||||
}
|
||||
|
||||
return form;
|
||||
},
|
||||
|
||||
|
@ -316,8 +316,8 @@ DebuggerClient.prototype = {
|
||||
* This function exists only to preserve DebuggerClient's interface;
|
||||
* new code should say 'client.mainRoot.listTabs()'.
|
||||
*/
|
||||
listTabs: function (onResponse) {
|
||||
return this.mainRoot.listTabs(onResponse);
|
||||
listTabs: function (options, onResponse) {
|
||||
return this.mainRoot.listTabs(options, onResponse);
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -5,7 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
const { Ci } = require("chrome");
|
||||
const {DebuggerClient} = require("devtools/shared/client/debugger-client");
|
||||
const { arg, DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
/**
|
||||
* A RootClient object represents a root actor on the server. Each
|
||||
@ -48,10 +48,13 @@ RootClient.prototype = {
|
||||
/**
|
||||
* List the open tabs.
|
||||
*
|
||||
* @param object options
|
||||
* Optional flags for listTabs:
|
||||
* - boolean favicons: return favicon data
|
||||
* @param function onResponse
|
||||
* Called with the response packet.
|
||||
*/
|
||||
listTabs: DebuggerClient.requester({ type: "listTabs" }),
|
||||
listTabs: DebuggerClient.requester({ type: "listTabs", options: arg(0) }),
|
||||
|
||||
/**
|
||||
* List the installed addons.
|
||||
|
Loading…
Reference in New Issue
Block a user