mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
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:
parent
c59f972c4b
commit
f34b0065c9
@ -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);
|
||||
|
||||
|
67
toolkit/content/directoryLinks.json
Normal file
67
toolkit/content/directoryLinks.json
Normal file
File diff suppressed because one or more lines are too long
@ -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)
|
||||
|
196
toolkit/modules/DirectoryLinksProvider.jsm
Normal file
196
toolkit/modules/DirectoryLinksProvider.jsm
Normal 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();
|
||||
}
|
||||
}
|
||||
};
|
@ -17,6 +17,7 @@ EXTRA_JS_MODULES += [
|
||||
'DeferredTask.jsm',
|
||||
'Deprecated.jsm',
|
||||
'Dict.jsm',
|
||||
'DirectoryLinksProvider.jsm',
|
||||
'FileUtils.jsm',
|
||||
'Finder.jsm',
|
||||
'Geometry.jsm',
|
||||
|
152
toolkit/modules/tests/xpcshell/test_DirectoryLinksProvider.js
Normal file
152
toolkit/modules/tests/xpcshell/test_DirectoryLinksProvider.js
Normal 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']);
|
||||
});
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user