Bug 975211 - Create backend logic to provide list of Tiles and associated metadata (image, text, background color) [r=adw]

Package a list of directory links to expose via DirectoryLinksProvider.jsm
This commit is contained in:
Olivier Yiptong 2014-03-27 01:03:42 -07:00
parent c59f972c4b
commit f34b0065c9
7 changed files with 420 additions and 0 deletions

View File

@ -1312,6 +1312,8 @@ pref("browser.newtabpage.rows", 3);
// number of columns of newtab grid
pref("browser.newtabpage.columns", 3);
pref("browser.newtabpage.directorySource", "chrome://global/content/directoryLinks.json");
// Enable the DOM fullscreen API.
pref("full-screen-api.enabled", true);

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,7 @@ toolkit.jar:
* content/global/customizeToolbar.js (customizeToolbar.js)
content/global/customizeToolbar.xul (customizeToolbar.xul)
content/global/devicestorage.properties (devicestorage.properties)
content/global/directoryLinks.json (directoryLinks.json)
content/global/editMenuOverlay.js (editMenuOverlay.js)
*+ content/global/editMenuOverlay.xul (editMenuOverlay.xul)
content/global/finddialog.js (finddialog.js)

View File

@ -0,0 +1,196 @@
/* 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 = ["DirectoryLinksProvider"];
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
/**
* Gets the currently selected locale for display.
* @return the selected locale or "en-US" if none is selected
*/
function getLocale() {
let matchOS;
try {
matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE);
}
catch (e) {}
if (matchOS) {
return Services.locale.getLocaleComponentForUserAgent();
}
try {
let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
Ci.nsIPrefLocalizedString);
if (locale) {
return locale.data;
}
}
catch (e) {}
try {
return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
}
catch (e) {}
return "en-US";
}
// The preference that tells whether to match the OS locale
const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
// The preference that tells what locale the user selected
const PREF_SELECTED_LOCALE = "general.useragent.locale";
// The preference that tells where to obtain directory links
const PREF_DIRECTORY_SOURCE = "browser.newtabpage.directorySource";
// The frecency of a directory link
const DIRECTORY_FRECENCY = 1000;
/**
* Singleton that serves as the provider of directory links.
* Directory links are a hard-coded set of links shown if a user's link
* inventory is empty.
*/
let DirectoryLinksProvider = {
__linksURL: null,
_observers: [],
get _prefs() Object.freeze({
linksURL: PREF_DIRECTORY_SOURCE,
matchOSLocale: PREF_MATCH_OS_LOCALE,
prefSelectedLocale: PREF_SELECTED_LOCALE,
}),
get _linksURL() {
if (!this.__linksURL) {
try {
this.__linksURL = Services.prefs.getCharPref(this._prefs["linksURL"]);
}
catch (e) {
Cu.reportError("Error fetching directory links url from prefs: " + e);
}
}
return this.__linksURL;
},
observe: function DirectoryLinksProvider_observe(aSubject, aTopic, aData) {
if (aTopic == "nsPref:changed") {
if (aData == this._prefs["linksURL"]) {
delete this.__linksURL;
}
this._callObservers("onManyLinksChanged");
}
},
_addPrefsObserver: function DirectoryLinksProvider_addObserver() {
for (let pref in this._prefs) {
let prefName = this._prefs[pref];
Services.prefs.addObserver(prefName, this, false);
}
},
_removePrefsObserver: function DirectoryLinksProvider_removeObserver() {
for (let pref in this._prefs) {
let prefName = this._prefs[pref];
Services.prefs.removeObserver(prefName, this);
}
},
/**
* Fetches the current set of directory links.
* @param aCallback a callback that is provided a set of links.
*/
_fetchLinks: function DirectoryLinksProvider_fetchLinks(aCallback) {
try {
NetUtil.asyncFetch(this._linksURL, (aInputStream, aResult, aRequest) => {
let output;
if (Components.isSuccessCode(aResult)) {
try {
let json = NetUtil.readInputStreamToString(aInputStream,
aInputStream.available(),
{charset: "UTF-8"});
let locale = getLocale();
output = JSON.parse(json)[locale];
}
catch (e) {
Cu.reportError(e);
}
}
else {
Cu.reportError(new Error("the fetch of " + this._linksURL + "was unsuccessful"));
}
aCallback(output || []);
});
}
catch (e) {
Cu.reportError(e);
aCallback([]);
}
},
/**
* Gets the current set of directory links.
* @param aCallback The function that the array of links is passed to.
*/
getLinks: function DirectoryLinksProvider_getLinks(aCallback) {
this._fetchLinks(rawLinks => {
// all directory links have a frecency of DIRECTORY_FRECENCY
aCallback(rawLinks.map((link, position) => {
link.frecency = DIRECTORY_FRECENCY;
link.lastVisitDate = rawLinks.length - position;
return link;
}));
});
},
init: function DirectoryLinksProvider_init() {
this._addPrefsObserver();
},
/**
* Return the object to its pre-init state
*/
reset: function DirectoryLinksProvider_reset() {
delete this.__linksURL;
this._removePrefsObserver();
this._removeObservers();
},
addObserver: function DirectoryLinksProvider_addObserver(aObserver) {
this._observers.push(aObserver);
},
_callObservers: function DirectoryLinksProvider__callObservers(aMethodName, aArg) {
for (let obs of this._observers) {
if (typeof(obs[aMethodName]) == "function") {
try {
obs[aMethodName](this, aArg);
} catch (err) {
Cu.reportError(err);
}
}
}
},
_removeObservers: function() {
while (this._observers.length) {
this._observers.pop();
}
}
};

View File

@ -17,6 +17,7 @@ EXTRA_JS_MODULES += [
'DeferredTask.jsm',
'Deprecated.jsm',
'Dict.jsm',
'DirectoryLinksProvider.jsm',
'FileUtils.jsm',
'Finder.jsm',
'Geometry.jsm',

View File

@ -0,0 +1,152 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
/**
* This file tests the DirectoryLinksProvider singleton in the DirectoryLinksProvider.jsm module.
*/
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
const DIRECTORY_FRECENCY = 1000;
const kTestSource = 'data:application/json,{"en-US": [{"url":"http://example.com","title":"TestSource"}]}';
function isIdentical(actual, expected) {
if (expected == null) {
do_check_eq(actual, expected);
}
else if (typeof expected == "object") {
// Make sure all the keys match up
do_check_eq(Object.keys(actual).sort() + "", Object.keys(expected).sort());
// Recursively check each value individually
Object.keys(expected).forEach(key => {
isIdentical(actual[key], expected[key]);
});
}
else {
do_check_eq(actual, expected);
}
}
function fetchData(provider) {
let deferred = Promise.defer();
provider.getLinks(linkData => {
deferred.resolve(linkData);
});
return deferred.promise;
}
function run_test() {
run_next_test();
}
add_task(function test_DirectoryLinksProvider__linkObservers() {
let deferred = Promise.defer();
let testObserver = {
onManyLinksChanged: function() {
deferred.resolve();
}
}
let provider = DirectoryLinksProvider;
provider.init();
provider.addObserver(testObserver);
do_check_eq(provider._observers.length, 1);
Services.prefs.setCharPref(provider._prefs['linksURL'], kTestSource);
yield deferred.promise;
provider._removeObservers();
do_check_eq(provider._observers.length, 0);
provider.reset();
Services.prefs.clearUserPref(provider._prefs['linksURL']);
});
add_task(function test_DirectoryLinksProvider__linksURL_locale() {
let data = {
"en-US": [{url: "http://example.com", title: "US"}],
"zh-CN": [
{url: "http://example.net", title: "CN"},
{url:"http://example.net/2", title: "CN2"}
],
};
let dataURI = 'data:application/json,' + JSON.stringify(data);
let provider = DirectoryLinksProvider;
Services.prefs.setCharPref(provider._prefs['linksURL'], dataURI);
Services.prefs.setCharPref('general.useragent.locale', 'en-US');
// set up the observer
provider.init();
do_check_eq(provider._linksURL, dataURI);
let links;
let expected_data;
links = yield fetchData(provider);
do_check_eq(links.length, 1);
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
isIdentical(links, expected_data);
Services.prefs.setCharPref('general.useragent.locale', 'zh-CN');
links = yield fetchData(provider);
do_check_eq(links.length, 2)
expected_data = [
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2},
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}
];
isIdentical(links, expected_data);
provider.reset();
Services.prefs.clearUserPref('general.useragent.locale');
Services.prefs.clearUserPref(provider._prefs['linksURL']);
});
add_task(function test_DirectoryLinksProvider__prefObserver_url() {
let provider = DirectoryLinksProvider;
Services.prefs.setCharPref('general.useragent.locale', 'en-US');
Services.prefs.setCharPref(provider._prefs['linksURL'], kTestSource);
// set up the observer
provider.init();
do_check_eq(provider._linksURL, kTestSource);
let links = yield fetchData(provider);
do_check_eq(links.length, 1);
let expectedData = [{url: "http://example.com", title: "TestSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
isIdentical(links, expectedData);
// tests these 2 things:
// 1. observer trigger on pref change
// 2. invalid source url
let exampleUrl = 'http://example.com/bad';
Services.prefs.setCharPref(provider._prefs['linksURL'], exampleUrl);
do_check_eq(provider._linksURL, exampleUrl);
let newLinks = yield fetchData(provider);
isIdentical(newLinks, []);
provider.reset();
Services.prefs.clearUserPref('general.useragent.locale')
Services.prefs.clearUserPref(provider._prefs['linksURL']);
});
add_task(function test_DirectoryLinksProvider_getLinks_noLocaleData() {
let provider = DirectoryLinksProvider;
Services.prefs.setCharPref('general.useragent.locale', 'zh-CN');
Services.prefs.setCharPref(provider._prefs['linksURL'], kTestSource);
let links = yield fetchData(provider);
do_check_eq(links.length, 0);
provider.reset();
Services.prefs.clearUserPref('general.useragent.locale')
Services.prefs.clearUserPref(provider._prefs['linksURL']);
});

View File

@ -10,6 +10,7 @@ support-files =
[test_AsyncShutdown.js]
[test_DeferredTask.js]
[test_dict.js]
[test_DirectoryLinksProvider.js]
[test_FileUtils.js]
[test_Http.js]
[test_Log.js]