Bug 1255270, r=mak a=ritu

ba=IDon'tThinkThisHookIsSupposedToBeEnforcedOnBeta47

MozReview-Commit-ID: 5fInAZiZMhl
This commit is contained in:
Gijs Kruitbosch 2016-05-23 10:09:13 +01:00
parent 97b377ca48
commit 32954f4653
5 changed files with 182 additions and 71 deletions

View File

@ -63,10 +63,6 @@
Components.classes["@mozilla.org/docshell/urifixup;1"]
.getService(Components.interfaces.nsIURIFixup);
</field>
<field name="mFaviconService" readonly="true">
Components.classes["@mozilla.org/browser/favicon-service;1"]
.getService(Components.interfaces.nsIFaviconService);
</field>
<field name="_placesAutocomplete" readonly="true">
Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
.getService(Components.interfaces.mozIPlacesAutoComplete);
@ -75,9 +71,6 @@
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
.getService(Components.interfaces.mozIPlacesAutoComplete);
</field>
<field name="PlacesUtils" readonly="true">
(Components.utils.import("resource://gre/modules/PlacesUtils.jsm", {})).PlacesUtils;
</field>
<field name="AppConstants" readonly="true">
(Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
</field>
@ -866,7 +859,7 @@
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
if (aURI && this.mFaviconService) {
if (aURI) {
if (!(aURI instanceof Ci.nsIURI)) {
aURI = makeURI(aURI);
}
@ -876,12 +869,7 @@
let loadingPrincipal = aLoadingPrincipal
? aLoadingPrincipal
: Services.scriptSecurityManager.getSystemPrincipal();
let loadType = PrivateBrowsingUtils.isWindowPrivate(window)
? this.mFaviconService.FAVICON_LOAD_PRIVATE
: this.mFaviconService.FAVICON_LOAD_NON_PRIVATE;
this.mFaviconService.setAndFetchFaviconForPage(
browser.currentURI, aURI, false, loadType, null, loadingPrincipal);
PlacesUIUtils.loadFavicon(browser, loadingPrincipal, aURI);
}
let sizedIconUrl = browser.mIconURL || "";
@ -954,12 +942,9 @@
<parameter name="aURI"/>
<body>
<![CDATA[
if (this.mFaviconService) {
if (!(aURI instanceof Ci.nsIURI))
aURI = makeURI(aURI);
return this.mFaviconService.isFailedFavicon(aURI);
}
return null;
if (!(aURI instanceof Ci.nsIURI))
aURI = makeURI(aURI);
return PlacesUtils.favicons.isFailedFavicon(aURI);
]]>
</body>
</method>

View File

@ -214,15 +214,6 @@ FeedWriter.prototype = {
element.setAttribute(attribute, uri);
},
__faviconService: null,
get _faviconService() {
if (!this.__faviconService)
this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
getService(Ci.nsIFaviconService);
return this.__faviconService;
},
__bundle: null,
get _bundle() {
if (!this.__bundle) {
@ -1023,7 +1014,6 @@ FeedWriter.prototype = {
prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
this._removeFeedFromCache();
this.__faviconService = null;
this.__bundle = null;
this._feedURI = null;
@ -1164,46 +1154,6 @@ FeedWriter.prototype = {
}
},
/**
* Sets the icon for the given web-reader item in the readers menu.
* The icon is fetched and stored through the favicon service.
*
* @param aReaderUrl
* the reader url.
* @param aMenuItem
* the reader item in the readers menulist.
*
* @note For privacy reasons we cannot set the image attribute directly
* to the icon url. See Bug 358878 for details.
*/
_setFaviconForWebReader:
function FW__setFaviconForWebReader(aReaderUrl, aMenuItem) {
let readerURI = makeURI(aReaderUrl);
if (!/^https?$/.test(readerURI.scheme)) {
// Don't try to get a favicon for non http(s) URIs.
return;
}
let faviconURI = makeURI(readerURI.prePath + "/favicon.ico");
let self = this;
let usePrivateBrowsing = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsILoadContext)
.usePrivateBrowsing;
let nullPrincipal = Cc["@mozilla.org/nullprincipal;1"]
.createInstance(Ci.nsIPrincipal);
this._faviconService.setAndFetchFaviconForPage(readerURI, faviconURI, false,
usePrivateBrowsing ? this._faviconService.FAVICON_LOAD_PRIVATE
: this._faviconService.FAVICON_LOAD_NON_PRIVATE,
function (aURI, aDataLen, aData, aMimeType) {
if (aDataLen > 0) {
let dataURL = "data:" + aMimeType + ";base64," +
btoa(String.fromCharCode.apply(null, aData));
aMenuItem.setAttribute('image', dataURL);
}
}, nullPrincipal);
},
get _mm() {
let mm = this._window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDocShell).

View File

@ -12,6 +12,7 @@ var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
@ -37,6 +38,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
const FAVICON_REQUEST_TIMEOUT = 60 * 1000;
// Map from windows to arrays of data about pending favicon loads.
let gFaviconLoadDataMap = new Map();
// copied from utilityOverlay.js
const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
@ -77,6 +83,149 @@ function IsLivemark(aItemId) {
return self.ids.has(aItemId);
}
let InternalFaviconLoader = {
/**
* This gets called for every inner window that is destroyed.
* In the parent process, we process the destruction ourselves. In the child process,
* we notify the parent which will then process it based on that message.
*/
observe(subject, topic, data) {
let innerWindowID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
this.onInnerDestroyed(innerWindowID);
},
/**
* Actually cancel the request, and clear the timeout for cancelling it.
*/
_cancelRequest({uri, innerWindowID, timerID, callback}, reason) {
// Break cycle
let request = callback.request;
delete callback.request;
// Ensure we don't time out.
clearTimeout(timerID);
try {
request.cancel();
} catch (ex) {
Cu.reportError("When cancelling a request for " + uri.spec + " because " + reason + ", it was already canceled!");
}
},
/**
* Called for every inner that gets destroyed, only in the parent process.
*/
onInnerDestroyed(innerID) {
for (let [window, loadDataForWindow] of gFaviconLoadDataMap) {
let newLoadDataForWindow = loadDataForWindow.filter(loadData => {
let innerWasDestroyed = loadData.innerWindowID == innerID;
if (innerWasDestroyed) {
this._cancelRequest(loadData, "the inner window was destroyed");
}
// Keep the items whose inner is still alive.
return !innerWasDestroyed;
});
// Map iteration with for...of is safe against modification, so
// now just replace the old value:
gFaviconLoadDataMap.set(window, newLoadDataForWindow);
}
},
/**
* Called when a toplevel chrome window unloads. We use this to tidy up after ourselves,
* avoid leaks, and cancel any remaining requests. The last part should in theory be
* handled by the inner-window-destroyed handlers. We clean up just to be on the safe side.
*/
onUnload(win) {
let loadDataForWindow = gFaviconLoadDataMap.get(win);
if (loadDataForWindow) {
for (let loadData of loadDataForWindow) {
this._cancelRequest(loadData, "the chrome window went away");
}
}
gFaviconLoadDataMap.delete(win);
},
/**
* Create a function to use as a nsIFaviconDataCallback, so we can remove cancelling
* information when the request succeeds. Note that right now there are some edge-cases,
* such as about: URIs with chrome:// favicons where the success callback is not invoked.
* This is OK: we will 'cancel' the request after the timeout (or when the window goes
* away) but that will be a no-op in such cases.
*/
_makeCompletionCallback(win, id) {
return {
onComplete(uri) {
let loadDataForWindow = gFaviconLoadDataMap.get(win);
if (loadDataForWindow) {
let itemIndex = loadDataForWindow.findIndex(loadData => {
return loadData.innerWindowID == id &&
loadData.uri.equals(uri) &&
loadData.callback.request == this.request;
});
if (itemIndex != -1) {
let loadData = loadDataForWindow[itemIndex];
clearTimeout(loadData.timerID);
loadDataForWindow.splice(itemIndex, 1);
}
}
delete this.request;
},
};
},
ensureInitialized() {
if (this._initialized) {
return;
}
this._initialized = true;
Services.obs.addObserver(this, "inner-window-destroyed", false);
Services.ppmm.addMessageListener("Toolkit:inner-window-destroyed", msg => {
this.onInnerDestroyed(msg.data);
});
},
loadFavicon(browser, principal, uri) {
this.ensureInitialized();
let win = browser.ownerDocument.defaultView;
if (!gFaviconLoadDataMap.has(win)) {
gFaviconLoadDataMap.set(win, []);
let unloadHandler = event => {
let doc = event.target;
let eventWin = doc.defaultview;
if (win == win.top && doc.documentURI != "about:blank") {
win.removeEventListener("unload", unloadHandler);
this.onUnload(win);
}
};
win.addEventListener("unload", unloadHandler, true);
}
// First we do the actual setAndFetch call:
let {innerWindowID, currentURI} = browser;
let loadType = PrivateBrowsingUtils.isWindowPrivate(win)
? PlacesUtils.favicons.FAVICON_LOAD_PRIVATE
: PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE;
let callback = this._makeCompletionCallback(win, innerWindowID);
let request = PlacesUtils.favicons.setAndFetchFaviconForPage(currentURI, uri, false,
loadType, callback, principal);
// Now register the result so we can cancel it if/when necessary.
if (!request) {
// The favicon service can return with success but no-op (and leave request
// as null) if the icon is the same as the page (e.g. for images) or if it is
// the favicon for an error page. In this case, we do not need to do anything else.
return;
}
callback.request = request;
let loadData = {innerWindowID, uri, callback};
loadData.timerID = setTimeout(() => {
this._cancelRequest(loadData, "it timed out");
}, FAVICON_REQUEST_TIMEOUT);
let loadDataForWindow = gFaviconLoadDataMap.get(win);
loadDataForWindow.push(loadData);
},
};
this.PlacesUIUtils = {
ORGANIZER_LEFTPANE_VERSION: 7,
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
@ -481,6 +630,19 @@ this.PlacesUIUtils = {
return RecentWindow.getMostRecentBrowserWindow();
},
/**
* set and fetch a favicon. Can only be used from the parent process.
* @param browser {Browser} The XUL browser element for which we're fetching a favicon.
* @param principal {Principal} The loading principal to use for the fetch.
* @param uri {URI} The URI to fetch.
*/
loadFavicon(browser, principal, uri) {
if (gInContentProcess) {
throw new Error("Can't track loads from within the child process!");
}
InternalFaviconLoader.loadFavicon(browser, principal, uri);
},
/**
* Returns the closet ancestor places view for the given DOM node
* @param aNode

View File

@ -605,7 +605,9 @@ AsyncFetchAndSetIconForPage::AsyncOnChannelRedirect(
, nsIAsyncVerifyRedirectCallback *cb
)
{
(void)cb->OnRedirectVerifyCallback(NS_OK);
// If we've been canceled, stop the redirect with NS_BINDING_ABORTED, and
// handle the cancel on the original channel.
(void)cb->OnRedirectVerifyCallback(mCanceled ? NS_BINDING_ABORTED : NS_OK);
return NS_OK;
}

View File

@ -12,9 +12,21 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/RemotePageManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
Services.cpmm.addMessageListener("gmp-plugin-crash", msg => {
let gmpservice = Cc["@mozilla.org/gecko-media-plugin-service;1"]
.getService(Ci.mozIGeckoMediaPluginService);
gmpservice.RunPluginCrashCallbacks(msg.data.pluginID, msg.data.pluginName);
});
// Forward inner-window-destroyed notifications with the inner window ID,
// so that code in the parent that should do something when content
// windows go away can do it
if (gInContentProcess) {
Services.obs.addObserver((subject, topic, data) => {
let innerWindowID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
Services.cpmm.sendAsyncMessage("Toolkit:inner-window-destroyed", innerWindowID);
}, "inner-window-destroyed", false);
}