1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/DirectoryLinksProvider.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,204 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["DirectoryLinksProvider"]; 1.11 + 1.12 +const Ci = Components.interfaces; 1.13 +const Cc = Components.classes; 1.14 +const Cu = Components.utils; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", 1.20 + "resource://gre/modules/NetUtil.jsm"); 1.21 + 1.22 +/** 1.23 + * Gets the currently selected locale for display. 1.24 + * @return the selected locale or "en-US" if none is selected 1.25 + */ 1.26 +function getLocale() { 1.27 + let matchOS; 1.28 + try { 1.29 + matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE); 1.30 + } 1.31 + catch (e) {} 1.32 + 1.33 + if (matchOS) { 1.34 + return Services.locale.getLocaleComponentForUserAgent(); 1.35 + } 1.36 + 1.37 + try { 1.38 + let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE, 1.39 + Ci.nsIPrefLocalizedString); 1.40 + if (locale) { 1.41 + return locale.data; 1.42 + } 1.43 + } 1.44 + catch (e) {} 1.45 + 1.46 + try { 1.47 + return Services.prefs.getCharPref(PREF_SELECTED_LOCALE); 1.48 + } 1.49 + catch (e) {} 1.50 + 1.51 + return "en-US"; 1.52 +} 1.53 + 1.54 +// The preference that tells whether to match the OS locale 1.55 +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; 1.56 + 1.57 +// The preference that tells what locale the user selected 1.58 +const PREF_SELECTED_LOCALE = "general.useragent.locale"; 1.59 + 1.60 +// The preference that tells where to obtain directory links 1.61 +const PREF_DIRECTORY_SOURCE = "browser.newtabpage.directorySource"; 1.62 + 1.63 +// The frecency of a directory link 1.64 +const DIRECTORY_FRECENCY = 1000; 1.65 + 1.66 +const LINK_TYPES = Object.freeze([ 1.67 + "sponsored", 1.68 + "affiliate", 1.69 + "organic", 1.70 +]); 1.71 + 1.72 +/** 1.73 + * Singleton that serves as the provider of directory links. 1.74 + * Directory links are a hard-coded set of links shown if a user's link 1.75 + * inventory is empty. 1.76 + */ 1.77 +let DirectoryLinksProvider = { 1.78 + 1.79 + __linksURL: null, 1.80 + 1.81 + _observers: [], 1.82 + 1.83 + get _prefs() Object.freeze({ 1.84 + linksURL: PREF_DIRECTORY_SOURCE, 1.85 + matchOSLocale: PREF_MATCH_OS_LOCALE, 1.86 + prefSelectedLocale: PREF_SELECTED_LOCALE, 1.87 + }), 1.88 + 1.89 + get _linksURL() { 1.90 + if (!this.__linksURL) { 1.91 + try { 1.92 + this.__linksURL = Services.prefs.getCharPref(this._prefs["linksURL"]); 1.93 + } 1.94 + catch (e) { 1.95 + Cu.reportError("Error fetching directory links url from prefs: " + e); 1.96 + } 1.97 + } 1.98 + return this.__linksURL; 1.99 + }, 1.100 + 1.101 + get linkTypes() LINK_TYPES, 1.102 + 1.103 + observe: function DirectoryLinksProvider_observe(aSubject, aTopic, aData) { 1.104 + if (aTopic == "nsPref:changed") { 1.105 + if (aData == this._prefs["linksURL"]) { 1.106 + delete this.__linksURL; 1.107 + } 1.108 + this._callObservers("onManyLinksChanged"); 1.109 + } 1.110 + }, 1.111 + 1.112 + _addPrefsObserver: function DirectoryLinksProvider_addObserver() { 1.113 + for (let pref in this._prefs) { 1.114 + let prefName = this._prefs[pref]; 1.115 + Services.prefs.addObserver(prefName, this, false); 1.116 + } 1.117 + }, 1.118 + 1.119 + _removePrefsObserver: function DirectoryLinksProvider_removeObserver() { 1.120 + for (let pref in this._prefs) { 1.121 + let prefName = this._prefs[pref]; 1.122 + Services.prefs.removeObserver(prefName, this); 1.123 + } 1.124 + }, 1.125 + 1.126 + /** 1.127 + * Fetches the current set of directory links. 1.128 + * @param aCallback a callback that is provided a set of links. 1.129 + */ 1.130 + _fetchLinks: function DirectoryLinksProvider_fetchLinks(aCallback) { 1.131 + try { 1.132 + NetUtil.asyncFetch(this._linksURL, (aInputStream, aResult, aRequest) => { 1.133 + let output; 1.134 + if (Components.isSuccessCode(aResult)) { 1.135 + try { 1.136 + let json = NetUtil.readInputStreamToString(aInputStream, 1.137 + aInputStream.available(), 1.138 + {charset: "UTF-8"}); 1.139 + let locale = getLocale(); 1.140 + output = JSON.parse(json)[locale]; 1.141 + } 1.142 + catch (e) { 1.143 + Cu.reportError(e); 1.144 + } 1.145 + } 1.146 + else { 1.147 + Cu.reportError(new Error("the fetch of " + this._linksURL + "was unsuccessful")); 1.148 + } 1.149 + aCallback(output || []); 1.150 + }); 1.151 + } 1.152 + catch (e) { 1.153 + Cu.reportError(e); 1.154 + aCallback([]); 1.155 + } 1.156 + }, 1.157 + 1.158 + /** 1.159 + * Gets the current set of directory links. 1.160 + * @param aCallback The function that the array of links is passed to. 1.161 + */ 1.162 + getLinks: function DirectoryLinksProvider_getLinks(aCallback) { 1.163 + this._fetchLinks(rawLinks => { 1.164 + // all directory links have a frecency of DIRECTORY_FRECENCY 1.165 + aCallback(rawLinks.map((link, position) => { 1.166 + link.frecency = DIRECTORY_FRECENCY; 1.167 + link.lastVisitDate = rawLinks.length - position; 1.168 + return link; 1.169 + })); 1.170 + }); 1.171 + }, 1.172 + 1.173 + init: function DirectoryLinksProvider_init() { 1.174 + this._addPrefsObserver(); 1.175 + }, 1.176 + 1.177 + /** 1.178 + * Return the object to its pre-init state 1.179 + */ 1.180 + reset: function DirectoryLinksProvider_reset() { 1.181 + delete this.__linksURL; 1.182 + this._removePrefsObserver(); 1.183 + this._removeObservers(); 1.184 + }, 1.185 + 1.186 + addObserver: function DirectoryLinksProvider_addObserver(aObserver) { 1.187 + this._observers.push(aObserver); 1.188 + }, 1.189 + 1.190 + _callObservers: function DirectoryLinksProvider__callObservers(aMethodName, aArg) { 1.191 + for (let obs of this._observers) { 1.192 + if (typeof(obs[aMethodName]) == "function") { 1.193 + try { 1.194 + obs[aMethodName](this, aArg); 1.195 + } catch (err) { 1.196 + Cu.reportError(err); 1.197 + } 1.198 + } 1.199 + } 1.200 + }, 1.201 + 1.202 + _removeObservers: function() { 1.203 + while (this._observers.length) { 1.204 + this._observers.pop(); 1.205 + } 1.206 + } 1.207 +};