/* 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, RemoteNewTabLocation, RemoteNewTabUtils, Task */ /* 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, "RemoteNewTabLocation", "resource:///modules/RemoteNewTabLocation.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesProvider", "resource:///modules/PlacesProvider.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", "resource:///modules/NewTabPrefsProvider.jsm"); let RemoteAboutNewTab = { pageListener: null, /** * Initialize the RemotePageManager and add all message listeners for this page */ init: function() { RemoteNewTabLocation.init(); this.pageListener = new RemotePages("about:remote-newtab"); 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: RemoteNewTabLocation.href, origin: RemoteNewTabLocation.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() { RemoteNewTabLocation.uninit(); this._removeObservers(); this.pageListener.destroy(); this.pageListener = null; }, };