mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1218996 - Remove now redundant messaging code for remote newtab r=marcosc
--HG-- extra : rebase_source : 667b19c153820198977d423703c08c0274a05c8f
This commit is contained in:
parent
e9b515f263
commit
25a0061e43
@ -1,23 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#remotedoc {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
position: absolute;
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/*globals XPCOMUtils, Components, sendAsyncMessage, addMessageListener, removeMessageListener,
|
||||
Services, PrivateBrowsingUtils*/
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu, interfaces: Ci} = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
(function() {
|
||||
let remoteNewTabLocation;
|
||||
let remoteIFrame;
|
||||
|
||||
/**
|
||||
* Attempts to handle commands sent from the remote IFrame within this content frame.
|
||||
* Expected commands below, with data types explained.
|
||||
*
|
||||
* @returns {Boolean} whether or not the command was handled
|
||||
* @param {String} command
|
||||
* The command passed from the remote IFrame
|
||||
* @param {Object} data
|
||||
* Parameters to the command
|
||||
*/
|
||||
function handleCommand(command, data) {
|
||||
let commandHandled = true;
|
||||
switch (command) {
|
||||
case "NewTab:UpdateTelemetryProbe":
|
||||
/**
|
||||
* Update a given Telemetry histogram
|
||||
*
|
||||
* @param {String} data.probe
|
||||
* Probe name to update
|
||||
* @param {Number} data.value
|
||||
* Value to update histogram by
|
||||
*/
|
||||
Services.telemetry.getHistogramById(data.probe).add(data.value);
|
||||
break;
|
||||
case "NewTab:Register":
|
||||
registerEvent(data.type);
|
||||
break;
|
||||
case "NewTab:GetInitialState":
|
||||
getInitialState();
|
||||
break;
|
||||
default:
|
||||
commandHandled = false;
|
||||
}
|
||||
return commandHandled;
|
||||
}
|
||||
|
||||
function initRemotePage(initData) {
|
||||
// Messages that the iframe sends the browser will be passed onto
|
||||
// the privileged parent process
|
||||
remoteNewTabLocation = initData;
|
||||
remoteIFrame = document.querySelector("#remotedoc");
|
||||
|
||||
let loadHandler = () => {
|
||||
if (remoteIFrame.src !== remoteNewTabLocation.href) {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteIFrame.removeEventListener("load", loadHandler);
|
||||
|
||||
remoteIFrame.contentDocument.addEventListener("NewTabCommand", (e) => {
|
||||
// If the commands are not handled within this content frame, the command will be
|
||||
// passed on to main process, in RemoteAboutNewTab.jsm
|
||||
let handled = handleCommand(e.detail.command, e.detail.data);
|
||||
if (!handled) {
|
||||
sendAsyncMessage(e.detail.command, e.detail.data);
|
||||
}
|
||||
});
|
||||
registerEvent("NewTab:Observe");
|
||||
let ev = new CustomEvent("NewTabCommandReady");
|
||||
remoteIFrame.contentDocument.dispatchEvent(ev);
|
||||
};
|
||||
|
||||
remoteIFrame.src = remoteNewTabLocation.href;
|
||||
remoteIFrame.addEventListener("load", loadHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the content IFrame to register a listener to an event sent by
|
||||
* the privileged parent process, in RemoteAboutNewTab.jsm
|
||||
*
|
||||
* @param {String} eventName
|
||||
* Event name to listen to
|
||||
*/
|
||||
function registerEvent(eventName) {
|
||||
addMessageListener(eventName, (message) => {
|
||||
remoteIFrame.contentWindow.postMessage(message, remoteNewTabLocation.origin);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the initial data payload to a content IFrame so it can bootstrap
|
||||
*/
|
||||
function getInitialState() {
|
||||
let prefs = Services.prefs;
|
||||
let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
|
||||
let state = {
|
||||
enabled: prefs.getBoolPref("browser.newtabpage.enabled"),
|
||||
enhanced: prefs.getBoolPref("browser.newtabpage.enhanced"),
|
||||
rows: prefs.getIntPref("browser.newtabpage.rows"),
|
||||
columns: prefs.getIntPref("browser.newtabpage.columns"),
|
||||
introShown: prefs.getBoolPref("browser.newtabpage.introShown"),
|
||||
windowID: window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID,
|
||||
privateBrowsingMode: isPrivate
|
||||
};
|
||||
remoteIFrame.contentWindow.postMessage({
|
||||
name: "NewTab:State",
|
||||
data: state
|
||||
}, remoteNewTabLocation.origin);
|
||||
}
|
||||
|
||||
addMessageListener("NewTabFrame:Init", function loadHandler(message) {
|
||||
// Everything is loaded. Initialize the New Tab Page.
|
||||
removeMessageListener("NewTabFrame:Init", loadHandler);
|
||||
initRemotePage(message.data);
|
||||
});
|
||||
sendAsyncMessage("NewTabFrame:GetInit");
|
||||
}());
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
|
||||
%newTabDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&newtab.pageTitle;</title>
|
||||
<link rel="stylesheet" href="chrome://browser/content/remote-newtab/newTab.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="remotedoc"/>
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/remote-newtab/newTab.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -136,9 +136,6 @@ browser.jar:
|
||||
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
|
||||
content/browser/newtab/newTab.css (content/newtab/newTab.css)
|
||||
content/browser/newtab/newTab.inadjacent.json (content/newtab/newTab.inadjacent.json)
|
||||
content/browser/remote-newtab/newTab.xhtml (content/remote-newtab/newTab.xhtml)
|
||||
content/browser/remote-newtab/newTab.js (content/remote-newtab/newTab.js)
|
||||
content/browser/remote-newtab/newTab.css (content/remote-newtab/newTab.css)
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
|
@ -89,11 +89,6 @@ static RedirEntry kRedirMap[] = {
|
||||
// the newtab's actual URL will be determined when the channel is created
|
||||
{ "newtab", "about:blank",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
#ifndef RELEASE_BUILD
|
||||
{ "remote-newtab", "chrome://browser/content/remote-newtab/newTab.xhtml",
|
||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
#endif
|
||||
{ "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
|
||||
nsIAboutModule::ALLOW_SCRIPT },
|
||||
{ "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
|
||||
|
@ -1,302 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/* globals Services, XPCOMUtils, RemotePages, RemoteNewTabUtils, Task, aboutNewTabService */
|
||||
/* globals BackgroundPageThumbs, PageThumbs, DirectoryLinksProvider, PlacesProvider, NewTabPrefsProvider */
|
||||
/* exported RemoteAboutNewTab */
|
||||
|
||||
"use strict";
|
||||
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteAboutNewTab"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
|
||||
"resource://gre/modules/RemotePageManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BackgroundPageThumbs",
|
||||
"resource://gre/modules/BackgroundPageThumbs.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider",
|
||||
"resource:///modules/DirectoryLinksProvider.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesProvider",
|
||||
"resource:///modules/PlacesProvider.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider",
|
||||
"resource:///modules/NewTabPrefsProvider.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
||||
"@mozilla.org/browser/aboutnewtab-service;1",
|
||||
"nsIAboutNewTabService");
|
||||
|
||||
let RemoteAboutNewTab = {
|
||||
|
||||
pageListener: null,
|
||||
remoteURL: null,
|
||||
|
||||
/**
|
||||
* Initialize the RemotePageManager and add all message listeners for this page
|
||||
*/
|
||||
init: function() {
|
||||
this.remoteURL = new URL(aboutNewTabService.generateRemoteURL());
|
||||
this.pageListener = new RemotePages(this.remoteURL.href);
|
||||
this.pageListener.addMessageListener("NewTab:InitializeGrid", this.initializeGrid.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:UpdateGrid", this.updateGrid.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:Customize", this.customize.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:CaptureBackgroundPageThumbs",
|
||||
this.captureBackgroundPageThumb.bind(this));
|
||||
this.pageListener.addMessageListener("NewTab:PageThumbs", this.createPageThumb.bind(this));
|
||||
this.pageListener.addMessageListener("NewTabFrame:GetInit", this.initContentFrame.bind(this));
|
||||
|
||||
this._addObservers();
|
||||
},
|
||||
|
||||
customize: function(message) {
|
||||
if (message.data.enabled !== undefined) {
|
||||
Services.prefs.setBoolPref("browser.newtabpage.enabled", message.data.enabled);
|
||||
}
|
||||
if (message.data.enhanced !== undefined) {
|
||||
Services.prefs.setBoolPref("browser.newtabpage.enhanced", message.data.enhanced);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies when history is cleared
|
||||
*/
|
||||
placesClearHistory: function() {
|
||||
this.pageListener.sendAsyncMessage("NewTab:PlacesClearHistory");
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies when a link has changed
|
||||
*/
|
||||
placesLinkChanged: function(name, data) { // jshint ignore:line
|
||||
this.pageListener.sendAsyncMessage("NewTab:PlacesLinkChanged", data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies when many links have changed
|
||||
*/
|
||||
placesManyLinksChanged: function() {
|
||||
this.pageListener.sendAsyncMessage("NewTab:PlacesManyLinksChanged");
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies when one URL has been deleted
|
||||
*/
|
||||
placesDeleteURI: function(name, data) { // jshint ignore:line
|
||||
this.pageListener.sendAsyncMessage("NewTab:PlacesDeleteURI", data.url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the grid for the first time when the page loads.
|
||||
* Fetch all the links and send them down to the child to populate
|
||||
* the grid with.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message.
|
||||
*/
|
||||
initializeGrid: function(message) {
|
||||
RemoteNewTabUtils.links.populateCache(() => {
|
||||
message.target.sendAsyncMessage("NewTab:InitializeLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Inits the content iframe with the newtab location
|
||||
*/
|
||||
initContentFrame: function(message) {
|
||||
message.target.sendAsyncMessage("NewTabFrame:Init", {
|
||||
href: this.remoteURL.href,
|
||||
origin: this.remoteURL.origin
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the grid by getting a new set of links.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message.
|
||||
*/
|
||||
updateGrid: function(message) {
|
||||
message.target.sendAsyncMessage("NewTab:UpdateLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Captures the site's thumbnail in the background, then attemps to show the thumbnail.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message with the following data:
|
||||
*
|
||||
* link (Object):
|
||||
* A link object that contains:
|
||||
*
|
||||
* baseDomain (String)
|
||||
* blockState (Boolean)
|
||||
* frecency (Integer)
|
||||
* lastVisiteDate (Integer)
|
||||
* pinState (Boolean)
|
||||
* title (String)
|
||||
* type (String)
|
||||
* url (String)
|
||||
*/
|
||||
captureBackgroundPageThumb: Task.async(function* (message) {
|
||||
try {
|
||||
yield BackgroundPageThumbs.captureIfMissing(message.data.link.url);
|
||||
this.createPageThumb(message);
|
||||
} catch (err) {
|
||||
Cu.reportError("error: " + err);
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates the thumbnail to display for each site based on the unique URL
|
||||
* of the site and it's type (regular or enhanced). If the thumbnail is of
|
||||
* type "regular", we create a blob and send that down to the child. If the
|
||||
* thumbnail is of type "enhanced", get the file path for the URL and create
|
||||
* and enhanced URI that will be sent down to the child.
|
||||
*
|
||||
* @param {Object} message
|
||||
* A RemotePageManager message with the following data:
|
||||
*
|
||||
* link (Object):
|
||||
* A link object that contains:
|
||||
*
|
||||
* baseDomain (String)
|
||||
* blockState (Boolean)
|
||||
* frecency (Integer)
|
||||
* lastVisiteDate (Integer)
|
||||
* pinState (Boolean)
|
||||
* title (String)
|
||||
* type (String)
|
||||
* url (String)
|
||||
*/
|
||||
createPageThumb: function(message) {
|
||||
let imgSrc = PageThumbs.getThumbnailURL(message.data.link.url);
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
let img = doc.createElementNS(XHTML_NAMESPACE, "img");
|
||||
let canvas = doc.createElementNS(XHTML_NAMESPACE, "canvas");
|
||||
let enhanced = Services.prefs.getBoolPref("browser.newtabpage.enhanced");
|
||||
|
||||
img.onload = function(e) { // jshint ignore:line
|
||||
canvas.width = img.naturalWidth;
|
||||
canvas.height = img.naturalHeight;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(this, 0, 0, this.naturalWidth, this.naturalHeight);
|
||||
canvas.toBlob(function(blob) {
|
||||
let host = new URL(message.data.link.url).host;
|
||||
RemoteAboutNewTab.pageListener.sendAsyncMessage("NewTab:RegularThumbnailURI", {
|
||||
thumbPath: "/pagethumbs/" + host,
|
||||
enhanced,
|
||||
url: message.data.link.url,
|
||||
blob,
|
||||
});
|
||||
});
|
||||
};
|
||||
img.src = imgSrc;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the set of enhanced links (if any) from the Directory Links Provider.
|
||||
*/
|
||||
getEnhancedLinks: function() {
|
||||
let enhancedLinks = [];
|
||||
for (let link of RemoteNewTabUtils.links.getLinks()) {
|
||||
if (link) {
|
||||
enhancedLinks.push(DirectoryLinksProvider.getEnhancedLink(link));
|
||||
}
|
||||
}
|
||||
return enhancedLinks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for a preference change or session purge for all pages and sends
|
||||
* a message to update the pages that are open. If a session purge occured,
|
||||
* also clear the links cache and update the set of links to display, as they
|
||||
* may have changed, then proceed with the page update.
|
||||
*/
|
||||
observe: function(aSubject, aTopic, aData) { // jshint ignore:line
|
||||
let extraData;
|
||||
if (aTopic === "browser:purge-session-history") {
|
||||
RemoteNewTabUtils.links.resetCache();
|
||||
RemoteNewTabUtils.links.populateCache(() => {
|
||||
this.pageListener.sendAsyncMessage("NewTab:UpdateLinks", {
|
||||
links: RemoteNewTabUtils.links.getLinks(),
|
||||
enhancedLinks: this.getEnhancedLinks(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (extraData !== undefined || aTopic === "page-thumbnail:create") {
|
||||
if (aTopic !== "page-thumbnail:create") {
|
||||
// Change the topic for enhanced and enabled observers.
|
||||
aTopic = aData;
|
||||
}
|
||||
this.pageListener.sendAsyncMessage("NewTab:Observe", {topic: aTopic, data: extraData});
|
||||
}
|
||||
},
|
||||
|
||||
setEnabled: function(name, data) { // jshint ignore:line
|
||||
this.pageListener.sendAsyncMessage("NewTab:setEnabled", data);
|
||||
},
|
||||
|
||||
setEnhanced: function(name, data) { // jshint ignore:line
|
||||
this.pageListener.sendAsyncMessage("NewTab:setEnhanced", data);
|
||||
},
|
||||
|
||||
setPinned: function(name, data) { // jshint ignore:line
|
||||
this.pageListener.sendAsyncMessage("NewTab:setPinnedLinks", data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add all observers that about:newtab page must listen for.
|
||||
*/
|
||||
_addObservers: function() {
|
||||
Services.obs.addObserver(this, "page-thumbnail:create", true);
|
||||
Services.obs.addObserver(this, "browser:purge-session-history", true);
|
||||
PlacesProvider.links.on("deleteURI", this.placesDeleteURI.bind(this));
|
||||
PlacesProvider.links.on("clearHistory", this.placesClearHistory.bind(this));
|
||||
PlacesProvider.links.on("linkChanged", this.placesLinkChanged.bind(this));
|
||||
PlacesProvider.links.on("manyLinksChanged", this.placesManyLinksChanged.bind(this));
|
||||
NewTabPrefsProvider.prefs.on("browser.newtabpage.enabled", this.setEnabled.bind(this));
|
||||
NewTabPrefsProvider.prefs.on("browser.newtabpage.enhanced", this.setEnhanced.bind(this));
|
||||
NewTabPrefsProvider.prefs.on("browser.newtabpage.pinned", this.setPinned.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all observers on the page.
|
||||
*/
|
||||
_removeObservers: function() {
|
||||
Services.obs.removeObserver(this, "page-thumbnail:create");
|
||||
Services.obs.removeObserver(this, "browser:purge-session-history");
|
||||
PlacesProvider.links.off("deleteURI", this.placesDeleteURI);
|
||||
PlacesProvider.links.off("clearHistory", this.placesClearHistory);
|
||||
PlacesProvider.links.off("linkChanged", this.placesLinkChanged);
|
||||
PlacesProvider.links.off("manyLinksChanged", this.placesManyLinksChanged);
|
||||
NewTabPrefsProvider.prefs.off("browser.newtabpage.enabled", this.setEnabled.bind(this));
|
||||
NewTabPrefsProvider.prefs.off("browser.newtabpage.enhanced", this.setEnhanced.bind(this));
|
||||
NewTabPrefsProvider.prefs.off("browser.newtabpage.pinned", this.setPinned.bind(this));
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
uninit: function() {
|
||||
this._removeObservers();
|
||||
this.pageListener.destroy();
|
||||
this.pageListener = null;
|
||||
},
|
||||
};
|
@ -1,766 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteNewTabUtils"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
|
||||
"resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BinarySearch",
|
||||
"resource://gre/modules/BinarySearch.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gPrincipal", function () {
|
||||
let uri = Services.io.newURI("about:newtab", null, null);
|
||||
return Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
||||
});
|
||||
|
||||
// The maximum number of results PlacesProvider retrieves from history.
|
||||
const HISTORY_RESULTS_LIMIT = 100;
|
||||
|
||||
// The maximum number of links Links.getLinks will return.
|
||||
const LINKS_GET_LINKS_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Singleton that serves as the default link provider for the grid. It queries
|
||||
* the history to retrieve the most frequently visited sites.
|
||||
*/
|
||||
let PlacesProvider = {
|
||||
/**
|
||||
* A count of how many batch updates are under way (batches may be nested, so
|
||||
* we keep a counter instead of a simple bool).
|
||||
**/
|
||||
_batchProcessingDepth: 0,
|
||||
|
||||
/**
|
||||
* A flag that tracks whether onFrecencyChanged was notified while a batch
|
||||
* operation was in progress, to tell us whether to take special action after
|
||||
* the batch operation completes.
|
||||
**/
|
||||
_batchCalledFrecencyChanged: false,
|
||||
|
||||
/**
|
||||
* Set this to change the maximum number of links the provider will provide.
|
||||
*/
|
||||
maxNumLinks: HISTORY_RESULTS_LIMIT,
|
||||
|
||||
/**
|
||||
* Must be called before the provider is used.
|
||||
*/
|
||||
init: function PlacesProvider_init() {
|
||||
PlacesUtils.history.addObserver(this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current set of links delivered by this provider.
|
||||
* @param aCallback The function that the array of links is passed to.
|
||||
*/
|
||||
getLinks: function PlacesProvider_getLinks(aCallback) {
|
||||
let options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.maxResults = this.maxNumLinks;
|
||||
|
||||
// Sort by frecency, descending.
|
||||
options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_DESCENDING
|
||||
|
||||
let links = [];
|
||||
|
||||
let callback = {
|
||||
handleResult: function (aResultSet) {
|
||||
let row;
|
||||
|
||||
while ((row = aResultSet.getNextRow())) {
|
||||
let url = row.getResultByIndex(1);
|
||||
if (LinkChecker.checkLoadURI(url)) {
|
||||
let title = row.getResultByIndex(2);
|
||||
let frecency = row.getResultByIndex(12);
|
||||
let lastVisitDate = row.getResultByIndex(5);
|
||||
links.push({
|
||||
url: url,
|
||||
title: title,
|
||||
frecency: frecency,
|
||||
lastVisitDate: lastVisitDate,
|
||||
type: "history",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleError: function (aError) {
|
||||
// Should we somehow handle this error?
|
||||
aCallback([]);
|
||||
},
|
||||
|
||||
handleCompletion: function (aReason) {
|
||||
// The Places query breaks ties in frecency by place ID descending, but
|
||||
// that's different from how Links.compareLinks breaks ties, because
|
||||
// compareLinks doesn't have access to place IDs. It's very important
|
||||
// that the initial list of links is sorted in the same order imposed by
|
||||
// compareLinks, because Links uses compareLinks to perform binary
|
||||
// searches on the list. So, ensure the list is so ordered.
|
||||
let i = 1;
|
||||
let outOfOrder = [];
|
||||
while (i < links.length) {
|
||||
if (Links.compareLinks(links[i - 1], links[i]) > 0)
|
||||
outOfOrder.push(links.splice(i, 1)[0]);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
for (let link of outOfOrder) {
|
||||
i = BinarySearch.insertionIndexOf(Links.compareLinks, links, link);
|
||||
links.splice(i, 0, link);
|
||||
}
|
||||
|
||||
aCallback(links);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the query.
|
||||
let query = PlacesUtils.history.getNewQuery();
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase);
|
||||
db.asyncExecuteLegacyQueries([query], 1, options, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers an object that will be notified when the provider's links change.
|
||||
* @param aObserver An object with the following optional properties:
|
||||
* * onLinkChanged: A function that's called when a single link
|
||||
* changes. It's passed the provider and the link object. Only the
|
||||
* link's `url` property is guaranteed to be present. If its `title`
|
||||
* property is present, then its title has changed, and the
|
||||
* property's value is the new title. If any sort properties are
|
||||
* present, then its position within the provider's list of links may
|
||||
* have changed, and the properties' values are the new sort-related
|
||||
* values. Note that this link may not necessarily have been present
|
||||
* in the lists returned from any previous calls to getLinks.
|
||||
* * onManyLinksChanged: A function that's called when many links
|
||||
* change at once. It's passed the provider. You should call
|
||||
* getLinks to get the provider's new list of links.
|
||||
*/
|
||||
addObserver: function PlacesProvider_addObserver(aObserver) {
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onBeginUpdateBatch: function() {
|
||||
this._batchProcessingDepth += 1;
|
||||
},
|
||||
|
||||
onEndUpdateBatch: function() {
|
||||
this._batchProcessingDepth -= 1;
|
||||
if (this._batchProcessingDepth == 0 && this._batchCalledFrecencyChanged) {
|
||||
this.onManyFrecenciesChanged();
|
||||
this._batchCalledFrecencyChanged = false;
|
||||
}
|
||||
},
|
||||
|
||||
onDeleteURI: function PlacesProvider_onDeleteURI(aURI, aGUID, aReason) {
|
||||
// let observers remove sensetive data associated with deleted visit
|
||||
this._callObservers("onDeleteURI", {
|
||||
url: aURI.spec,
|
||||
});
|
||||
},
|
||||
|
||||
onClearHistory: function() {
|
||||
this._callObservers("onClearHistory")
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onFrecencyChanged: function PlacesProvider_onFrecencyChanged(aURI, aNewFrecency, aGUID, aHidden, aLastVisitDate) {
|
||||
// If something is doing a batch update of history entries we don't want
|
||||
// to do lots of work for each record. So we just track the fact we need
|
||||
// to call onManyFrecenciesChanged() once the batch is complete.
|
||||
if (this._batchProcessingDepth > 0) {
|
||||
this._batchCalledFrecencyChanged = true;
|
||||
return;
|
||||
}
|
||||
// The implementation of the query in getLinks excludes hidden and
|
||||
// unvisited pages, so it's important to exclude them here, too.
|
||||
if (!aHidden && aLastVisitDate) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: aURI.spec,
|
||||
frecency: aNewFrecency,
|
||||
lastVisitDate: aLastVisitDate,
|
||||
type: "history",
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onManyFrecenciesChanged: function PlacesProvider_onManyFrecenciesChanged() {
|
||||
this._callObservers("onManyLinksChanged");
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the history service.
|
||||
*/
|
||||
onTitleChanged: function PlacesProvider_onTitleChanged(aURI, aNewTitle, aGUID) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: aURI.spec,
|
||||
title: aNewTitle
|
||||
});
|
||||
},
|
||||
|
||||
_callObservers: function PlacesProvider__callObservers(aMethodName, aArg) {
|
||||
for (let obs of this._observers) {
|
||||
if (obs[aMethodName]) {
|
||||
try {
|
||||
obs[aMethodName](this, aArg);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that provides access to all links contained in the grid (including
|
||||
* the ones that don't fit on the grid). A link is a plain object that looks
|
||||
* like this:
|
||||
*
|
||||
* {
|
||||
* url: "http://www.mozilla.org/",
|
||||
* title: "Mozilla",
|
||||
* frecency: 1337,
|
||||
* lastVisitDate: 1394678824766431,
|
||||
* }
|
||||
*/
|
||||
let Links = {
|
||||
/**
|
||||
* The maximum number of links returned by getLinks.
|
||||
*/
|
||||
maxNumLinks: LINKS_GET_LINKS_LIMIT,
|
||||
|
||||
/**
|
||||
* A mapping from each provider to an object { sortedLinks, siteMap, linkMap }.
|
||||
* sortedLinks is the cached, sorted array of links for the provider.
|
||||
* siteMap is a mapping from base domains to URL count associated with the domain.
|
||||
* siteMap is used to look up a user's top sites that can be targeted
|
||||
* with a suggested tile.
|
||||
* linkMap is a Map from link URLs to link objects.
|
||||
*/
|
||||
_providers: new Map(),
|
||||
|
||||
/**
|
||||
* The properties of link objects used to sort them.
|
||||
*/
|
||||
_sortProperties: [
|
||||
"frecency",
|
||||
"lastVisitDate",
|
||||
"url",
|
||||
],
|
||||
|
||||
/**
|
||||
* List of callbacks waiting for the cache to be populated.
|
||||
*/
|
||||
_populateCallbacks: [],
|
||||
|
||||
/**
|
||||
* A list of objects that are observing links updates.
|
||||
*/
|
||||
_observers: [],
|
||||
|
||||
/**
|
||||
* Registers an object that will be notified when links updates.
|
||||
*/
|
||||
addObserver: function (aObserver) {
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a link provider.
|
||||
* @param aProvider The link provider.
|
||||
*/
|
||||
addProvider: function Links_addProvider(aProvider) {
|
||||
this._providers.set(aProvider, null);
|
||||
aProvider.addObserver(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a link provider.
|
||||
* @param aProvider The link provider.
|
||||
*/
|
||||
removeProvider: function Links_removeProvider(aProvider) {
|
||||
if (!this._providers.delete(aProvider))
|
||||
throw new Error("Unknown provider");
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates the cache with fresh links from the providers.
|
||||
* @param aCallback The callback to call when finished (optional).
|
||||
* @param aForce When true, populates the cache even when it's already filled.
|
||||
*/
|
||||
populateCache: function Links_populateCache(aCallback, aForce) {
|
||||
let callbacks = this._populateCallbacks;
|
||||
|
||||
// Enqueue the current callback.
|
||||
callbacks.push(aCallback);
|
||||
|
||||
// There was a callback waiting already, thus the cache has not yet been
|
||||
// populated.
|
||||
if (callbacks.length > 1)
|
||||
return;
|
||||
|
||||
function executeCallbacks() {
|
||||
while (callbacks.length) {
|
||||
let callback = callbacks.shift();
|
||||
if (callback) {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
// We want to proceed even if a callback fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let numProvidersRemaining = this._providers.size;
|
||||
for (let [provider, links] of this._providers) {
|
||||
this._populateProviderCache(provider, () => {
|
||||
if (--numProvidersRemaining == 0)
|
||||
executeCallbacks();
|
||||
}, aForce);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current set of links contained in the grid.
|
||||
* @return The links in the grid.
|
||||
*/
|
||||
getLinks: function Links_getLinks() {
|
||||
let links = this._getMergedProviderLinks();
|
||||
|
||||
let sites = new Set();
|
||||
|
||||
// Filter duplicate base domains.
|
||||
links = links.filter(function (link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
link.baseDomain = site;
|
||||
if (site == null || sites.has(site))
|
||||
return false;
|
||||
sites.add(site);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return links;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the links cache.
|
||||
*/
|
||||
resetCache: function Links_resetCache() {
|
||||
for (let provider of this._providers.keys()) {
|
||||
this._providers.set(provider, null);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Compares two links.
|
||||
* @param aLink1 The first link.
|
||||
* @param aLink2 The second link.
|
||||
* @return A negative number if aLink1 is ordered before aLink2, zero if
|
||||
* aLink1 and aLink2 have the same ordering, or a positive number if
|
||||
* aLink1 is ordered after aLink2.
|
||||
*
|
||||
* @note compareLinks's this object is bound to Links below.
|
||||
*/
|
||||
compareLinks: function Links_compareLinks(aLink1, aLink2) {
|
||||
for (let prop of this._sortProperties) {
|
||||
if (!(prop in aLink1) || !(prop in aLink2))
|
||||
throw new Error("Comparable link missing required property: " + prop);
|
||||
}
|
||||
return aLink2.frecency - aLink1.frecency ||
|
||||
aLink2.lastVisitDate - aLink1.lastVisitDate ||
|
||||
aLink1.url.localeCompare(aLink2.url);
|
||||
},
|
||||
|
||||
_incrementSiteMap: function(map, link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
map.set(site, (map.get(site) || 0) + 1);
|
||||
},
|
||||
|
||||
_decrementSiteMap: function(map, link) {
|
||||
let site = RemoteNewTabUtils.extractSite(link.url);
|
||||
let previousURLCount = map.get(site);
|
||||
if (previousURLCount === 1) {
|
||||
map.delete(site);
|
||||
} else {
|
||||
map.set(site, previousURLCount - 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the siteMap cache based on the link given and whether we need
|
||||
* to increment or decrement it. We do this by iterating over all stored providers
|
||||
* to find which provider this link already exists in. For providers that
|
||||
* have this link, we will adjust siteMap for them accordingly.
|
||||
*
|
||||
* @param aLink The link that will affect siteMap
|
||||
* @param increment A boolean for whether to increment or decrement siteMap
|
||||
*/
|
||||
_adjustSiteMapAndNotify: function(aLink, increment=true) {
|
||||
for (let [provider, cache] of this._providers) {
|
||||
// We only update siteMap if aLink is already stored in linkMap.
|
||||
if (cache.linkMap.get(aLink.url)) {
|
||||
if (increment) {
|
||||
this._incrementSiteMap(cache.siteMap, aLink);
|
||||
continue;
|
||||
}
|
||||
this._decrementSiteMap(cache.siteMap, aLink);
|
||||
}
|
||||
}
|
||||
this._callObservers("onLinkChanged", aLink);
|
||||
},
|
||||
|
||||
populateProviderCache: function(provider, callback) {
|
||||
if (!this._providers.has(provider)) {
|
||||
throw new Error("Can only populate provider cache for existing provider.");
|
||||
}
|
||||
|
||||
return this._populateProviderCache(provider, callback, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls getLinks on the given provider and populates our cache for it.
|
||||
* @param aProvider The provider whose cache will be populated.
|
||||
* @param aCallback The callback to call when finished.
|
||||
* @param aForce When true, populates the provider's cache even when it's
|
||||
* already filled.
|
||||
*/
|
||||
_populateProviderCache: function (aProvider, aCallback, aForce) {
|
||||
let cache = this._providers.get(aProvider);
|
||||
let createCache = !cache;
|
||||
if (createCache) {
|
||||
cache = {
|
||||
// Start with a resolved promise.
|
||||
populatePromise: new Promise(resolve => resolve()),
|
||||
};
|
||||
this._providers.set(aProvider, cache);
|
||||
}
|
||||
// Chain the populatePromise so that calls are effectively queued.
|
||||
cache.populatePromise = cache.populatePromise.then(() => {
|
||||
return new Promise(resolve => {
|
||||
if (!createCache && !aForce) {
|
||||
aCallback();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
aProvider.getLinks(links => {
|
||||
// Filter out null and undefined links so we don't have to deal with
|
||||
// them in getLinks when merging links from providers.
|
||||
links = links.filter((link) => !!link);
|
||||
cache.sortedLinks = links;
|
||||
cache.siteMap = links.reduce((map, link) => {
|
||||
this._incrementSiteMap(map, link);
|
||||
return map;
|
||||
}, new Map());
|
||||
cache.linkMap = links.reduce((map, link) => {
|
||||
map.set(link.url, link);
|
||||
return map;
|
||||
}, new Map());
|
||||
aCallback();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges the cached lists of links from all providers whose lists are cached.
|
||||
* @return The merged list.
|
||||
*/
|
||||
_getMergedProviderLinks: function Links__getMergedProviderLinks() {
|
||||
// Build a list containing a copy of each provider's sortedLinks list.
|
||||
let linkLists = [];
|
||||
for (let provider of this._providers.keys()) {
|
||||
let links = this._providers.get(provider);
|
||||
if (links && links.sortedLinks) {
|
||||
linkLists.push(links.sortedLinks.slice());
|
||||
}
|
||||
}
|
||||
|
||||
function getNextLink() {
|
||||
let minLinks = null;
|
||||
for (let links of linkLists) {
|
||||
if (links.length &&
|
||||
(!minLinks || Links.compareLinks(links[0], minLinks[0]) < 0))
|
||||
minLinks = links;
|
||||
}
|
||||
return minLinks ? minLinks.shift() : null;
|
||||
}
|
||||
|
||||
let finalLinks = [];
|
||||
for (let nextLink = getNextLink();
|
||||
nextLink && finalLinks.length < this.maxNumLinks;
|
||||
nextLink = getNextLink()) {
|
||||
finalLinks.push(nextLink);
|
||||
}
|
||||
|
||||
return finalLinks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by a provider to notify us when a single link changes.
|
||||
* @param aProvider The provider whose link changed.
|
||||
* @param aLink The link that changed. If the link is new, it must have all
|
||||
* of the _sortProperties. Otherwise, it may have as few or as
|
||||
* many as is convenient.
|
||||
* @param aIndex The current index of the changed link in the sortedLinks
|
||||
cache in _providers. Defaults to -1 if the provider doesn't know the index
|
||||
* @param aDeleted Boolean indicating if the provider has deleted the link.
|
||||
*/
|
||||
onLinkChanged: function Links_onLinkChanged(aProvider, aLink, aIndex=-1, aDeleted=false) {
|
||||
if (!("url" in aLink))
|
||||
throw new Error("Changed links must have a url property");
|
||||
|
||||
let links = this._providers.get(aProvider);
|
||||
if (!links)
|
||||
// This is not an error, it just means that between the time the provider
|
||||
// was added and the future time we call getLinks on it, it notified us of
|
||||
// a change.
|
||||
return;
|
||||
|
||||
let { sortedLinks, siteMap, linkMap } = links;
|
||||
let existingLink = linkMap.get(aLink.url);
|
||||
let insertionLink = null;
|
||||
|
||||
if (existingLink) {
|
||||
// Update our copy's position in O(lg n) by first removing it from its
|
||||
// list. It's important to do this before modifying its properties.
|
||||
if (this._sortProperties.some(prop => prop in aLink)) {
|
||||
let idx = aIndex;
|
||||
if (idx < 0) {
|
||||
idx = this._indexOf(sortedLinks, existingLink);
|
||||
} else if (this.compareLinks(aLink, sortedLinks[idx]) != 0) {
|
||||
throw new Error("aLink should be the same as sortedLinks[idx]");
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
throw new Error("Link should be in _sortedLinks if in _linkMap");
|
||||
}
|
||||
sortedLinks.splice(idx, 1);
|
||||
|
||||
if (aDeleted) {
|
||||
linkMap.delete(existingLink.url);
|
||||
this._decrementSiteMap(siteMap, existingLink);
|
||||
} else {
|
||||
// Update our copy's properties.
|
||||
Object.assign(existingLink, aLink);
|
||||
|
||||
// Finally, reinsert our copy below.
|
||||
insertionLink = existingLink;
|
||||
}
|
||||
}
|
||||
// Update our copy's title in O(1).
|
||||
if ("title" in aLink && aLink.title != existingLink.title) {
|
||||
existingLink.title = aLink.title;
|
||||
}
|
||||
}
|
||||
else if (this._sortProperties.every(prop => prop in aLink)) {
|
||||
// Before doing the O(lg n) insertion below, do an O(1) check for the
|
||||
// common case where the new link is too low-ranked to be in the list.
|
||||
if (sortedLinks.length && sortedLinks.length == aProvider.maxNumLinks) {
|
||||
let lastLink = sortedLinks[sortedLinks.length - 1];
|
||||
if (this.compareLinks(lastLink, aLink) < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Copy the link object so that changes later made to it by the caller
|
||||
// don't affect our copy.
|
||||
insertionLink = {};
|
||||
for (let prop in aLink) {
|
||||
insertionLink[prop] = aLink[prop];
|
||||
}
|
||||
linkMap.set(aLink.url, insertionLink);
|
||||
this._incrementSiteMap(siteMap, aLink);
|
||||
}
|
||||
|
||||
if (insertionLink) {
|
||||
let idx = this._insertionIndexOf(sortedLinks, insertionLink);
|
||||
sortedLinks.splice(idx, 0, insertionLink);
|
||||
if (sortedLinks.length > aProvider.maxNumLinks) {
|
||||
let lastLink = sortedLinks.pop();
|
||||
linkMap.delete(lastLink.url);
|
||||
this._decrementSiteMap(siteMap, lastLink);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by a provider to notify us when many links change.
|
||||
*/
|
||||
onManyLinksChanged: function Links_onManyLinksChanged(aProvider) {
|
||||
this._populateProviderCache(aProvider, () => {}, true);
|
||||
},
|
||||
|
||||
_indexOf: function Links__indexOf(aArray, aLink) {
|
||||
return this._binsearch(aArray, aLink, "indexOf");
|
||||
},
|
||||
|
||||
_insertionIndexOf: function Links__insertionIndexOf(aArray, aLink) {
|
||||
return this._binsearch(aArray, aLink, "insertionIndexOf");
|
||||
},
|
||||
|
||||
_binsearch: function Links__binsearch(aArray, aLink, aMethod) {
|
||||
return BinarySearch[aMethod](this.compareLinks, aArray, aLink);
|
||||
},
|
||||
|
||||
_callObservers(methodName, ...args) {
|
||||
for (let obs of this._observers) {
|
||||
if (typeof(obs[methodName]) == "function") {
|
||||
try {
|
||||
obs[methodName](this, ...args);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Links.compareLinks = Links.compareLinks.bind(Links);
|
||||
|
||||
/**
|
||||
* Singleton that checks if a given link should be displayed on about:newtab
|
||||
* or if we should rather not do it for security reasons. URIs that inherit
|
||||
* their caller's principal will be filtered.
|
||||
*/
|
||||
let LinkChecker = {
|
||||
_cache: {},
|
||||
|
||||
get flags() {
|
||||
return Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL |
|
||||
Ci.nsIScriptSecurityManager.DONT_REPORT_ERRORS;
|
||||
},
|
||||
|
||||
checkLoadURI: function LinkChecker_checkLoadURI(aURI) {
|
||||
if (!(aURI in this._cache))
|
||||
this._cache[aURI] = this._doCheckLoadURI(aURI);
|
||||
|
||||
return this._cache[aURI];
|
||||
},
|
||||
|
||||
_doCheckLoadURI: function Links_doCheckLoadURI(aURI) {
|
||||
try {
|
||||
Services.scriptSecurityManager.
|
||||
checkLoadURIStrWithPrincipal(gPrincipal, aURI, this.flags);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// We got a weird URI or one that would inherit the caller's principal.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ExpirationFilter = {
|
||||
init: function ExpirationFilter_init() {
|
||||
PageThumbs.addExpirationFilter(this);
|
||||
},
|
||||
|
||||
filterForThumbnailExpiration:
|
||||
function ExpirationFilter_filterForThumbnailExpiration(aCallback) {
|
||||
Links.populateCache(function () {
|
||||
let urls = [];
|
||||
|
||||
// Add all URLs to the list that we want to keep thumbnails for.
|
||||
for (let link of Links.getLinks().slice(0, 25)) {
|
||||
if (link && link.url)
|
||||
urls.push(link.url);
|
||||
}
|
||||
|
||||
aCallback(urls);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Singleton that provides the public API of this JSM.
|
||||
*/
|
||||
this.RemoteNewTabUtils = {
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Extract a "site" from a url in a way that multiple urls of a "site" returns
|
||||
* the same "site."
|
||||
* @param aUrl Url spec string
|
||||
* @return The "site" string or null
|
||||
*/
|
||||
extractSite: function Links_extractSite(url) {
|
||||
let host;
|
||||
try {
|
||||
// Note that nsIURI.asciiHost throws NS_ERROR_FAILURE for some types of
|
||||
// URIs, including jar and moz-icon URIs.
|
||||
host = Services.io.newURI(url, null, null).asciiHost;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip off common subdomains of the same site (e.g., www, load balancer)
|
||||
return host.replace(/^(m|mobile|www\d*)\./, "");
|
||||
},
|
||||
|
||||
init: function RemoteNewTabUtils_init() {
|
||||
if (this.initWithoutProviders()) {
|
||||
PlacesProvider.init();
|
||||
Links.addProvider(PlacesProvider);
|
||||
}
|
||||
},
|
||||
|
||||
initWithoutProviders: function RemoteNewTabUtils_initWithoutProviders() {
|
||||
if (!this._initialized) {
|
||||
this._initialized = true;
|
||||
ExpirationFilter.init();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getProviderLinks: function(aProvider) {
|
||||
let cache = Links._providers.get(aProvider);
|
||||
if (cache && cache.sortedLinks) {
|
||||
return cache.sortedLinks;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
isTopSiteGivenProvider: function(aSite, aProvider) {
|
||||
let cache = Links._providers.get(aProvider);
|
||||
if (cache && cache.siteMap) {
|
||||
return cache.siteMap.has(aSite);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
isTopPlacesSite: function(aSite) {
|
||||
return this.isTopSiteGivenProvider(aSite, PlacesProvider);
|
||||
},
|
||||
|
||||
links: Links,
|
||||
linkChecker: LinkChecker,
|
||||
placesProvider: PlacesProvider
|
||||
};
|
@ -15,8 +15,6 @@ if not CONFIG['RELEASE_BUILD']:
|
||||
'NewTabPrefsProvider.jsm',
|
||||
'NewTabURL.jsm',
|
||||
'PlacesProvider.jsm',
|
||||
'RemoteAboutNewTab.jsm',
|
||||
'RemoteNewTabUtils.jsm',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
@ -1,375 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// See also browser/base/content/test/newtab/.
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
Cu.import("resource:///modules/RemoteNewTabUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* validCacheMidPopulation() {
|
||||
let expectedLinks = makeLinks(0, 3, 1);
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
let promise = new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
// isTopSiteGivenProvider() and getProviderLinks() should still return results
|
||||
// even when cache is empty or being populated.
|
||||
do_check_false(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider));
|
||||
do_check_links(RemoteNewTabUtils.getProviderLinks(provider), []);
|
||||
|
||||
yield promise;
|
||||
|
||||
// Once the cache is populated, we get the expected results
|
||||
do_check_true(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider));
|
||||
do_check_links(RemoteNewTabUtils.getProviderLinks(provider), expectedLinks);
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function* notifyLinkDelete() {
|
||||
let expectedLinks = makeLinks(0, 3, 1);
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Remove a link.
|
||||
let removedLink = expectedLinks[2];
|
||||
provider.notifyLinkChanged(removedLink, 2, true);
|
||||
let links = RemoteNewTabUtils.links._providers.get(provider);
|
||||
|
||||
// Check that sortedLinks is correctly updated.
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks.slice(0, 2));
|
||||
|
||||
// Check that linkMap is accurately updated.
|
||||
do_check_eq(links.linkMap.size, 2);
|
||||
do_check_true(links.linkMap.get(expectedLinks[0].url));
|
||||
do_check_true(links.linkMap.get(expectedLinks[1].url));
|
||||
do_check_false(links.linkMap.get(removedLink.url));
|
||||
|
||||
// Check that siteMap is correctly updated.
|
||||
do_check_eq(links.siteMap.size, 2);
|
||||
do_check_true(links.siteMap.has(RemoteNewTabUtils.extractSite(expectedLinks[0].url)));
|
||||
do_check_true(links.siteMap.has(RemoteNewTabUtils.extractSite(expectedLinks[1].url)));
|
||||
do_check_false(links.siteMap.has(RemoteNewTabUtils.extractSite(removedLink.url)));
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function populatePromise() {
|
||||
let count = 0;
|
||||
let expectedLinks = makeLinks(0, 10, 2);
|
||||
|
||||
let getLinksFcn = Task.async(function* (callback) {
|
||||
//Should not be calling getLinksFcn twice
|
||||
count++;
|
||||
do_check_eq(count, 1);
|
||||
yield Promise.resolve();
|
||||
callback(expectedLinks);
|
||||
});
|
||||
|
||||
let provider = new TestProvider(getLinksFcn);
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
RemoteNewTabUtils.links.populateProviderCache(provider, () => {});
|
||||
RemoteNewTabUtils.links.populateProviderCache(provider, () => {
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* isTopSiteGivenProvider() {
|
||||
let expectedLinks = makeLinks(0, 10, 2);
|
||||
|
||||
// The lowest 2 frecencies have the same base domain.
|
||||
expectedLinks[expectedLinks.length - 2].url = expectedLinks[expectedLinks.length - 1].url + "Test";
|
||||
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example1.com", provider), false);
|
||||
|
||||
// Push out frecency 2 because the maxNumLinks is reached when adding frecency 3
|
||||
let newLink = makeLink(3);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// There is still a frecent url with example2 domain, so it's still frecent.
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example3.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
|
||||
|
||||
// Push out frecency 3
|
||||
newLink = makeLink(5);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// Push out frecency 4
|
||||
newLink = makeLink(9);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
|
||||
// Our count reached 0 for the example2.com domain so it's no longer a frecent site.
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example5.com", provider), true);
|
||||
do_check_eq(RemoteNewTabUtils.isTopSiteGivenProvider("example2.com", provider), false);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function* multipleProviders() {
|
||||
// Make each provider generate RemoteNewTabUtils.links.maxNumLinks links to check
|
||||
// that no more than maxNumLinks are actually returned in the merged list.
|
||||
let evenLinks = makeLinks(0, 2 * RemoteNewTabUtils.links.maxNumLinks, 2);
|
||||
let evenProvider = new TestProvider(done => done(evenLinks));
|
||||
let oddLinks = makeLinks(0, 2 * RemoteNewTabUtils.links.maxNumLinks - 1, 2);
|
||||
let oddProvider = new TestProvider(done => done(oddLinks));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(evenProvider);
|
||||
RemoteNewTabUtils.links.addProvider(oddProvider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
let links = RemoteNewTabUtils.links.getLinks();
|
||||
let expectedLinks = makeLinks(RemoteNewTabUtils.links.maxNumLinks,
|
||||
2 * RemoteNewTabUtils.links.maxNumLinks,
|
||||
1);
|
||||
do_check_eq(links.length, RemoteNewTabUtils.links.maxNumLinks);
|
||||
do_check_links(links, expectedLinks);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(evenProvider);
|
||||
RemoteNewTabUtils.links.removeProvider(oddProvider);
|
||||
});
|
||||
|
||||
add_task(function* changeLinks() {
|
||||
let expectedLinks = makeLinks(0, 20, 2);
|
||||
let provider = new TestProvider(done => done(expectedLinks));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a new link.
|
||||
let newLink = makeLink(19);
|
||||
expectedLinks.splice(1, 0, newLink);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a link that's changed sort criteria.
|
||||
newLink.frecency = 17;
|
||||
expectedLinks.splice(1, 1);
|
||||
expectedLinks.splice(2, 0, newLink);
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
frecency: 17,
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a link that's changed title.
|
||||
newLink.title = "My frecency is now 17";
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
title: newLink.title,
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of a new link again, but this time make it overflow maxNumLinks.
|
||||
provider.maxNumLinks = expectedLinks.length;
|
||||
newLink = makeLink(21);
|
||||
expectedLinks.unshift(newLink);
|
||||
expectedLinks.pop();
|
||||
do_check_eq(expectedLinks.length, provider.maxNumLinks); // Sanity check.
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
// Notify of many links changed.
|
||||
expectedLinks = makeLinks(0, 3, 1);
|
||||
provider.notifyManyLinksChanged();
|
||||
|
||||
// Since _populateProviderCache() is async, we must wait until the provider's
|
||||
// populate promise has been resolved.
|
||||
yield RemoteNewTabUtils.links._providers.get(provider).populatePromise;
|
||||
|
||||
// RemoteNewTabUtils.links will now repopulate its cache
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), expectedLinks);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function* oneProviderAlreadyCached() {
|
||||
let links1 = makeLinks(0, 10, 1);
|
||||
let provider1 = new TestProvider(done => done(links1));
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider1);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links1);
|
||||
|
||||
let links2 = makeLinks(10, 20, 1);
|
||||
let provider2 = new TestProvider(done => done(links2));
|
||||
RemoteNewTabUtils.links.addProvider(provider2);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links2.concat(links1));
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider1);
|
||||
RemoteNewTabUtils.links.removeProvider(provider2);
|
||||
});
|
||||
|
||||
add_task(function* newLowRankedLink() {
|
||||
// Init a provider with 10 links and make its maximum number also 10.
|
||||
let links = makeLinks(0, 10, 1);
|
||||
let provider = new TestProvider(done => done(links));
|
||||
provider.maxNumLinks = links.length;
|
||||
|
||||
RemoteNewTabUtils.initWithoutProviders();
|
||||
RemoteNewTabUtils.links.addProvider(provider);
|
||||
|
||||
yield new Promise(resolve => RemoteNewTabUtils.links.populateCache(resolve));
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
// Notify of a new link that's low-ranked enough not to make the list.
|
||||
let newLink = makeLink(0);
|
||||
provider.notifyLinkChanged(newLink);
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
// Notify about the new link's title change.
|
||||
provider.notifyLinkChanged({
|
||||
url: newLink.url,
|
||||
title: "a new title",
|
||||
});
|
||||
do_check_links(RemoteNewTabUtils.links.getLinks(), links);
|
||||
|
||||
RemoteNewTabUtils.links.removeProvider(provider);
|
||||
});
|
||||
|
||||
add_task(function extractSite() {
|
||||
// All these should extract to the same site
|
||||
[ "mozilla.org",
|
||||
"m.mozilla.org",
|
||||
"mobile.mozilla.org",
|
||||
"www.mozilla.org",
|
||||
"www3.mozilla.org",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_eq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted same " + host);
|
||||
});
|
||||
|
||||
// All these should extract to the same subdomain
|
||||
[ "bugzilla.mozilla.org",
|
||||
"www.bugzilla.mozilla.org",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_eq(RemoteNewTabUtils.extractSite(url), "bugzilla.mozilla.org", "extracted eTLD+2 " + host);
|
||||
});
|
||||
|
||||
// All these should not extract to the same site
|
||||
[ "bugzilla.mozilla.org",
|
||||
"bug123.bugzilla.mozilla.org",
|
||||
"too.many.levels.bugzilla.mozilla.org",
|
||||
"m2.mozilla.org",
|
||||
"mobile30.mozilla.org",
|
||||
"ww.mozilla.org",
|
||||
"ww2.mozilla.org",
|
||||
"wwwww.mozilla.org",
|
||||
"wwwww50.mozilla.org",
|
||||
"wwws.mozilla.org",
|
||||
"secure.mozilla.org",
|
||||
"secure10.mozilla.org",
|
||||
"many.levels.deep.mozilla.org",
|
||||
"just.check.in",
|
||||
"192.168.0.1",
|
||||
"localhost",
|
||||
].forEach(host => {
|
||||
let url = "http://" + host;
|
||||
do_check_neq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted diff " + host);
|
||||
});
|
||||
|
||||
// All these should not extract to the same site
|
||||
[ "about:blank",
|
||||
"file:///Users/user/file",
|
||||
"chrome://browser/something",
|
||||
"ftp://ftp.mozilla.org/",
|
||||
].forEach(url => {
|
||||
do_check_neq(RemoteNewTabUtils.extractSite(url), "mozilla.org", "extracted diff url " + url);
|
||||
});
|
||||
});
|
||||
|
||||
function TestProvider(getLinksFn) {
|
||||
this.getLinks = getLinksFn;
|
||||
this._observers = new Set();
|
||||
}
|
||||
|
||||
TestProvider.prototype = {
|
||||
addObserver: function (observer) {
|
||||
this._observers.add(observer);
|
||||
},
|
||||
notifyLinkChanged: function (link, index=-1, deleted=false) {
|
||||
this._notifyObservers("onLinkChanged", link, index, deleted);
|
||||
},
|
||||
notifyManyLinksChanged: function () {
|
||||
this._notifyObservers("onManyLinksChanged");
|
||||
},
|
||||
_notifyObservers: function () {
|
||||
let observerMethodName = arguments[0];
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(this);
|
||||
for (let obs of this._observers) {
|
||||
if (obs[observerMethodName])
|
||||
obs[observerMethodName].apply(RemoteNewTabUtils.links, args);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function do_check_links(actualLinks, expectedLinks) {
|
||||
do_check_true(Array.isArray(actualLinks));
|
||||
do_check_eq(actualLinks.length, expectedLinks.length);
|
||||
for (let i = 0; i < expectedLinks.length; i++) {
|
||||
let expected = expectedLinks[i];
|
||||
let actual = actualLinks[i];
|
||||
do_check_eq(actual.url, expected.url);
|
||||
do_check_eq(actual.title, expected.title);
|
||||
do_check_eq(actual.frecency, expected.frecency);
|
||||
do_check_eq(actual.lastVisitDate, expected.lastVisitDate);
|
||||
}
|
||||
}
|
||||
|
||||
function makeLinks(frecRangeStart, frecRangeEnd, step) {
|
||||
let links = [];
|
||||
// Remember, links are ordered by frecency descending.
|
||||
for (let i = frecRangeEnd; i > frecRangeStart; i -= step) {
|
||||
links.push(makeLink(i));
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
function makeLink(frecency) {
|
||||
return {
|
||||
url: "http://example" + frecency + ".com/",
|
||||
title: "My frecency is " + frecency,
|
||||
frecency: frecency,
|
||||
lastVisitDate: 0,
|
||||
};
|
||||
}
|
@ -8,4 +8,3 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
[test_NewTabPrefsProvider.js]
|
||||
[test_NewTabURL.js]
|
||||
[test_PlacesProvider.js]
|
||||
[test_RemoteNewTabUtils.js]
|
||||
|
@ -29,12 +29,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
|
||||
if(!AppConstants.RELEASE_BUILD) {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteAboutNewTab",
|
||||
"resource:///modules/RemoteAboutNewTab.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider",
|
||||
"resource:///modules/NewTabPrefsProvider.jsm");
|
||||
}
|
||||
@ -777,9 +771,6 @@ BrowserGlue.prototype = {
|
||||
AboutNewTab.init();
|
||||
|
||||
if(!AppConstants.RELEASE_BUILD) {
|
||||
RemoteNewTabUtils.init();
|
||||
RemoteNewTabUtils.links.addProvider(DirectoryLinksProvider);
|
||||
RemoteAboutNewTab.init();
|
||||
NewTabPrefsProvider.prefs.init();
|
||||
}
|
||||
|
||||
@ -1098,10 +1089,7 @@ BrowserGlue.prototype = {
|
||||
CustomizationTabPreloader.uninit();
|
||||
WebappManager.uninit();
|
||||
|
||||
if (!AppConstants.RELEASE_BUILD) {
|
||||
RemoteAboutNewTab.uninit();
|
||||
NewTabPrefsProvider.prefs.uninit();
|
||||
}
|
||||
AboutNewTab.uninit();
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (Services.prefs.getBoolPref("dom.identity.enabled")) {
|
||||
|
@ -32,13 +32,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
|
||||
"@mozilla.org/network/effective-tld-service;1",
|
||||
"nsIEffectiveTLDService");
|
||||
|
||||
// ensure remote new tab doesn't go beyond aurora
|
||||
if (!AppConstants.RELEASE_BUILD) {
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils",
|
||||
"resource:///modules/RemoteNewTabUtils.jsm");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
|
||||
return new TextDecoder();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user