This commit is contained in:
Wes Kocher 2015-02-04 17:21:04 -08:00
Родитель 95217cc09f 6eb63d75a3
Коммит 0562d10377
55 изменённых файлов: 1953 добавлений и 1497 удалений

Просмотреть файл

@ -295,7 +295,7 @@ let gFxAccounts = {
// the first device (so the user is prompted to create an account).
// If there is an email address it is the "join the party" flow, so the
// user is prompted to sign in with the address they previously used.
let msg, upgradeLabel, upgradeAccessKey;
let msg, upgradeLabel, upgradeAccessKey, learnMoreLink;
if (this._migrationInfo.email) {
msg = this.strings.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
[this._migrationInfo.email],
@ -306,6 +306,7 @@ let gFxAccounts = {
msg = this.strings.GetStringFromName("needUserLong");
upgradeLabel = this.strings.GetStringFromName("upgradeToFxA.label");
upgradeAccessKey = this.strings.GetStringFromName("upgradeToFxA.accessKey");
learnMoreLink = this.fxaMigrator.learnMoreLink;
}
note = new Weave.Notification(
undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [
@ -313,7 +314,7 @@ let gFxAccounts = {
this._expectingNotifyClose = true;
this.fxaMigrator.createFxAccount(window);
}),
]
], learnMoreLink
);
break;
}

Просмотреть файл

@ -73,6 +73,18 @@
notification.priority,
notification.buttons);
node.notification = notification;
if (notification.link) {
let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let link = node.ownerDocument.createElementNS(XULNS, "label");
link.className = "text-link";
link.setAttribute("value", notification.link.text);
link.href = notification.link.href;
let desc = node.ownerDocument.getAnonymousElementByAttribute(
node, "anonid", "messageText"
);
desc.appendChild(link);
}
]]></body>
</method>

Просмотреть файл

@ -12,26 +12,24 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/LoadContextInfo.jsm");
// Import common head.
let (commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false)) {
let commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false);
if (commonFile) {
let uri = Services.io.newFileURI(commonFile);
Services.scriptloader.loadSubScript(uri.spec, this);
}
// Put any other stuff relative to this test folder below.
XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
Cu.import("resource:///modules/PlacesUIUtils.jsm");
return PlacesUIUtils;
});
const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
// Needed by some test that relies on having an app registered.
let (XULAppInfo = {
let XULAppInfo = {
vendor: "Mozilla",
name: "PlacesTest",
ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
@ -48,19 +46,19 @@ let (XULAppInfo = {
Ci.nsIXULAppInfo,
Ci.nsIXULRuntime,
])
}) {
let XULAppInfoFactory = {
createInstance: function (outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return XULAppInfo.QueryInterface(iid);
}
};
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
"XULAppInfo", "@mozilla.org/xre/app-info;1",
XULAppInfoFactory);
}
};
let XULAppInfoFactory = {
createInstance: function (outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return XULAppInfo.QueryInterface(iid);
}
};
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
"XULAppInfo", "@mozilla.org/xre/app-info;1",
XULAppInfoFactory);
// Smart bookmarks constants.
const SMART_BOOKMARKS_VERSION = 7;
@ -70,3 +68,41 @@ const SMART_BOOKMARKS_ON_MENU = 3; // Takes into account the additional separat
// Default bookmarks constants.
const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
const DEFAULT_BOOKMARKS_ON_MENU = 1;
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
function checkItemHasAnnotation(guid, name) {
return PlacesUtils.promiseItemId(guid).then(id => {
let hasAnnotation = PlacesUtils.annotations.itemHasAnnotation(id, name);
Assert.ok(hasAnnotation, `Expected annotation ${name}`);
});
}
function waitForImportAndSmartBookmarks() {
return Promise.all([
promiseTopicObserved("bookmarks-restore-success"),
PlacesTestUtils.promiseAsyncUpdates()
]);
}
function promiseEndUpdateBatch() {
return new Promise(resolve => {
PlacesUtils.bookmarks.addObserver({
__proto__: NavBookmarkObserver.prototype,
onEndUpdateBatch: resolve
}, false);
});
}
let createCorruptDB = Task.async(function* () {
let dbPath = OS.Path.join(OS.Constants.Path.profileDir, "places.sqlite");
yield OS.File.remove(dbPath);
// Create a corrupt database.
let dir = yield OS.File.getCurrentDirectory();
let src = OS.Path.join(dir, "corruptDB.sqlite");
yield OS.File.copy(src, dbPath);
// Check there's a DB now.
Assert.ok((yield OS.File.exists(dbPath)), "should have a DB now");
});

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
let gluesvc = Cc["@mozilla.org/browser/browserglue;1"].
@ -18,64 +17,88 @@ function run_test() {
run_next_test();
}
add_task(function smart_bookmarks_disabled() {
add_task(function* smart_bookmarks_disabled() {
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
gluesvc.ensurePlacesDefaultQueriesInitialized();
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, 0);
Assert.equal(smartBookmarkItemIds.length, 0);
do_print("check that pref has not been bumped up");
do_check_eq(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
Assert.equal(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
});
add_task(function create_smart_bookmarks() {
add_task(function* create_smart_bookmarks() {
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_neq(smartBookmarkItemIds.length, 0);
Assert.notEqual(smartBookmarkItemIds.length, 0);
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
add_task(function remove_smart_bookmark_and_restore() {
add_task(function* remove_smart_bookmark_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_print("remove one smart bookmark and restore");
PlacesUtils.bookmarks.removeItem(smartBookmarkItemIds[0]);
let guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
yield PlacesUtils.bookmarks.remove(guid);
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
add_task(function move_smart_bookmark_rename_and_restore() {
add_task(function* move_smart_bookmark_rename_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_print("smart bookmark should be restored in place");
let parent = PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]);
let oldTitle = PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]);
let guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
let bm = yield PlacesUtils.bookmarks.fetch(guid);
let oldTitle = bm.title;
// create a subfolder and move inside it
let newParent =
PlacesUtils.bookmarks.createFolder(parent, "test",
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.bookmarks.moveItem(smartBookmarkItemIds[0], newParent,
PlacesUtils.bookmarks.DEFAULT_INDEX);
// change title
PlacesUtils.bookmarks.setItemTitle(smartBookmarkItemIds[0], "new title");
let subfolder = yield PlacesUtils.bookmarks.insert({
parentGuid: bm.parentGuid,
title: "test",
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
type: PlacesUtils.bookmarks.TYPE_FOLDER
});
// change title and move into new subfolder
yield PlacesUtils.bookmarks.update({
guid: guid,
parentGuid: subfolder.guid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
title: "new title"
});
// restore
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]), newParent);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]), oldTitle);
Assert.equal(smartBookmarkItemIds.length, smartBookmarksCount);
guid = yield PlacesUtils.promiseItemGuid(smartBookmarkItemIds[0]);
bm = yield PlacesUtils.bookmarks.fetch(guid);
Assert.equal(bm.parentGuid, subfolder.guid);
Assert.equal(bm.title, oldTitle);
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
Assert.ok(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});

Просмотреть файл

@ -9,78 +9,51 @@
* database is corrupt and one backup is available.
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "bs",
"@mozilla.org/browser/nav-bookmarks-service;1",
"nsINavBookmarksService");
XPCOMUtils.defineLazyServiceGetter(this, "anno",
"@mozilla.org/browser/annotation-service;1",
"nsIAnnotationService");
let bookmarksObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {
let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
do_check_neq(itemId, -1);
if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
continue_test();
},
onItemAdded: function() {},
onItemRemoved: function(id, folder, index, itemType) {},
onItemChanged: function() {},
onItemVisited: function(id, visitID, time) {},
onItemMoved: function() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
function run_test() {
do_test_pending();
// Create our bookmarks.html copying bookmarks.glue.html to the profile
// folder. It should be ignored.
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
// Create our JSON backup copying bookmarks.glue.json to the profile folder.
remove_all_JSON_backups();
// Create our JSON backup from bookmarks.glue.json.
create_JSON_backup("bookmarks.glue.json");
// Remove current database file.
let db = gProfD.clone();
db.append("places.sqlite");
if (db.exists()) {
db.remove(false);
do_check_false(db.exists());
}
run_next_test();
}
do_register_cleanup(function () {
remove_bookmarks_html();
remove_all_JSON_backups();
return PlacesUtils.bookmarks.eraseEverything();
});
add_task(function* test_main() {
// Create a corrupt database.
let corruptDB = gTestDir.clone();
corruptDB.append("corruptDB.sqlite");
corruptDB.copyTo(gProfD, "places.sqlite");
do_check_true(db.exists());
yield createCorruptDB();
// Initialize nsBrowserGlue before Places.
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue);
// Initialize Places through the History Service.
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
// Check the database was corrupt.
// nsBrowserGlue uses databaseStatus to manage initialization.
do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
// The test will continue once restore has finished and smart bookmarks
// have been created.
bs.addObserver(bookmarksObserver, false);
}
yield promiseEndUpdateBatch();
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
function continue_test() {
// Check that JSON backup has been restored.
// Notice restore from JSON notification is fired before smart bookmarks creation.
let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
do_check_eq(bs.getItemTitle(itemId), "examplejson");
remove_bookmarks_html();
remove_all_JSON_backups();
do_test_finished();
}
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(bm.title, "examplejson");
});

Просмотреть файл

@ -9,73 +9,44 @@
* is corrupt but a JSON backup is not available.
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "bs",
"@mozilla.org/browser/nav-bookmarks-service;1",
"nsINavBookmarksService");
XPCOMUtils.defineLazyServiceGetter(this, "anno",
"@mozilla.org/browser/annotation-service;1",
"nsIAnnotationService");
let bookmarksObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {
let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
do_check_neq(itemId, -1);
if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
continue_test();
},
onItemAdded: function() {},
onItemRemoved: function(id, folder, index, itemType) {},
onItemChanged: function() {},
onItemVisited: function(id, visitID, time) {},
onItemMoved: function() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
function run_test() {
do_test_pending();
// Create bookmarks.html in the profile.
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
// Remove JSON backup from profile.
remove_all_JSON_backups();
// Remove current database file.
let db = gProfD.clone();
db.append("places.sqlite");
if (db.exists()) {
db.remove(false);
do_check_false(db.exists());
}
run_next_test();
}
do_register_cleanup(remove_bookmarks_html);
add_task(function* () {
// Create a corrupt database.
let corruptDB = gTestDir.clone();
corruptDB.append("corruptDB.sqlite");
corruptDB.copyTo(gProfD, "places.sqlite");
do_check_true(db.exists());
yield createCorruptDB();
// Initialize nsBrowserGlue before Places.
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue);
// Initialize Places through the History Service.
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
// Check the database was corrupt.
// nsBrowserGlue uses databaseStatus to manage initialization.
do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
// The test will continue once import has finished and smart bookmarks
// have been created.
bs.addObserver(bookmarksObserver, false);
}
yield promiseEndUpdateBatch();
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
function continue_test() {
// Check that bookmarks html has been restored.
let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
do_check_eq(bs.getItemTitle(itemId), "example");
remove_bookmarks_html();
do_test_finished();
}
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(bm.title, "example");
});

Просмотреть файл

@ -9,72 +9,42 @@
* corrupt, nor a JSON backup nor bookmarks.html are available.
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "bs",
"@mozilla.org/browser/nav-bookmarks-service;1",
"nsINavBookmarksService");
XPCOMUtils.defineLazyServiceGetter(this, "anno",
"@mozilla.org/browser/annotation-service;1",
"nsIAnnotationService");
let bookmarksObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {
let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
do_check_neq(itemId, -1);
if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
continue_test();
},
onItemAdded: function() {},
onItemRemoved: function(id, folder, index, itemType) {},
onItemChanged: function() {},
onItemVisited: function(id, visitID, time) {},
onItemMoved: function() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
function run_test() {
do_test_pending();
// Remove bookmarks.html from profile.
remove_bookmarks_html();
// Remove JSON backup from profile.
remove_all_JSON_backups();
// Remove current database file.
let db = gProfD.clone();
db.append("places.sqlite");
if (db.exists()) {
db.remove(false);
do_check_false(db.exists());
}
run_next_test();
}
add_task(function* () {
// Create a corrupt database.
let corruptDB = gTestDir.clone();
corruptDB.append("corruptDB.sqlite");
corruptDB.copyTo(gProfD, "places.sqlite");
do_check_true(db.exists());
yield createCorruptDB();
// Initialize nsBrowserGlue before Places.
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue);
// Initialize Places through the History Service.
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
// Check the database was corrupt.
// nsBrowserGlue uses databaseStatus to manage initialization.
do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CORRUPT);
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CORRUPT);
// The test will continue once import has finished and smart bookmarks
// have been created.
bs.addObserver(bookmarksObserver, false);
}
yield promiseEndUpdateBatch();
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
function continue_test() {
// Check that default bookmarks have been restored.
let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
do_check_true(itemId > 0);
do_check_eq(bs.getItemTitle(itemId), "Getting Started");
do_test_finished();
}
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
do_check_eq(bm.title, "Getting Started");
});

Просмотреть файл

@ -13,12 +13,10 @@ const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
const TOPIC_CUSTOMIZATION_COMPLETE = "distribution-customization-complete";
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
function run_test()
{
do_test_pending();
function run_test() {
// Set special pref to load distribution.ini from the profile folder.
Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
// Copy distribution.ini file to the profile dir.
let distroDir = gProfD.clone();
distroDir.leafName = "distribution";
@ -28,78 +26,72 @@ function run_test()
iniFile.remove(false);
print("distribution.ini already exists, did some test forget to cleanup?");
}
let testDistributionFile = gTestDir.clone();
testDistributionFile.append("distribution.ini");
testDistributionFile.copyTo(distroDir, "distribution.ini");
do_check_true(testDistributionFile.exists());
Assert.ok(testDistributionFile.exists());
// Disable Smart Bookmarks creation.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
// Initialize Places through the History Service and check that a new
// database has been created.
do_check_eq(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
// Force distribution.
Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
// Test will continue on customization complete notification.
Services.obs.addObserver(function(aSubject, aTopic, aData) {
Services.obs.removeObserver(arguments.callee,
TOPIC_CUSTOMIZATION_COMPLETE,
false);
do_execute_soon(onCustomizationComplete);
}, TOPIC_CUSTOMIZATION_COMPLETE, false);
run_next_test();
}
function onCustomizationComplete()
{
// Check the custom bookmarks exist on menu.
let menuItemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
do_check_neq(menuItemId, -1);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(menuItemId),
"Menu Link Before");
menuItemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
1 + DEFAULT_BOOKMARKS_ON_MENU);
do_check_neq(menuItemId, -1);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(menuItemId),
"Menu Link After");
// Check the custom bookmarks exist on toolbar.
let toolbarItemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_neq(toolbarItemId, -1);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(toolbarItemId),
"Toolbar Link Before");
toolbarItemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
1 + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_neq(toolbarItemId, -1);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(toolbarItemId),
"Toolbar Link After");
// Check the bmprocessed pref has been created.
do_check_true(Services.prefs.getBoolPref(PREF_BMPROCESSED));
// Check distribution prefs have been created.
do_check_eq(Services.prefs.getCharPref(PREF_DISTRIBUTION_ID), "516444");
do_test_finished();
}
do_register_cleanup(function() {
do_register_cleanup(function () {
// Remove the distribution file, even if the test failed, otherwise all
// next tests will import it.
let iniFile = gProfD.clone();
iniFile.leafName = "distribution";
iniFile.append("distribution.ini");
if (iniFile.exists())
if (iniFile.exists()) {
iniFile.remove(false);
do_check_false(iniFile.exists());
}
Assert.ok(!iniFile.exists());
});
add_task(function* () {
// Disable Smart Bookmarks creation.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
// Initialize Places through the History Service and check that a new
// database has been created.
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
// Force distribution.
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
// Test will continue on customization complete notification.
yield promiseTopicObserved(TOPIC_CUSTOMIZATION_COMPLETE);
// Check the custom bookmarks exist on menu.
let menuItem = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
});
Assert.equal(menuItem.title, "Menu Link Before");
menuItem = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 1 + DEFAULT_BOOKMARKS_ON_MENU
});
Assert.equal(menuItem.title, "Menu Link After");
// Check the custom bookmarks exist on toolbar.
let toolbarItem = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
Assert.equal(toolbarItem.title, "Toolbar Link Before");
toolbarItem = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 1 + DEFAULT_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(toolbarItem.title, "Toolbar Link After");
// Check the bmprocessed pref has been created.
Assert.ok(Services.prefs.getBoolPref(PREF_BMPROCESSED));
// Check distribution prefs have been created.
Assert.equal(Services.prefs.getCharPref(PREF_DISTRIBUTION_ID), "516444");
});

Просмотреть файл

@ -10,79 +10,61 @@
const PREF_SMART_BOOKMARKS_VERSION = "browser.places.smartBookmarksVersion";
function run_test() {
do_test_pending();
// Create our bookmarks.html copying bookmarks.glue.html to the profile
// folder. It should be ignored.
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
// Remove current database file.
let db = gProfD.clone();
db.append("places.sqlite");
if (db.exists()) {
db.remove(false);
do_check_false(db.exists());
}
clearDB();
run_next_test();
}
do_register_cleanup(remove_bookmarks_html);
add_task(function* test_migrate_bookmarks() {
// Initialize Places through the History Service and check that a new
// database has been created.
do_check_eq(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
// A migrator would run before nsBrowserGlue Places initialization, so mimic
// that behavior adding a bookmark and notifying the migration.
let bg = Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIObserver);
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
bg.observe(null, "initial-migration-will-import-default-bookmarks", null);
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder, uri("http://mozilla.org/"),
PlacesUtils.bookmarks.DEFAULT_INDEX, "migrated");
let bookmarksObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {
// Check if the currently finished batch created the smart bookmarks.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_neq(itemId, -1);
if (PlacesUtils.annotations
.itemHasAnnotation(itemId, "Places/SmartBookmark")) {
do_execute_soon(onSmartBookmarksCreation);
}
},
onItemAdded: function() {},
onItemRemoved: function(id, folder, index, itemType) {},
onItemChanged: function() {},
onItemVisited: function(id, visitID, time) {},
onItemMoved: function() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
// The test will continue once import has finished and smart bookmarks
// have been created.
PlacesUtils.bookmarks.addObserver(bookmarksObserver, false);
yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: "http://mozilla.org/",
title: "migrated"
});
let promise = promiseEndUpdateBatch();
bg.observe(null, "initial-migration-did-import-default-bookmarks", null);
}
yield promise;
function onSmartBookmarksCreation() {
// Check the created bookmarks still exist.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
SMART_BOOKMARKS_ON_MENU);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "migrated");
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
// Check the created bookmark still exists.
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: SMART_BOOKMARKS_ON_MENU
});
Assert.equal(bm.title, "migrated");
// Check that we have not imported any new bookmark.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
SMART_BOOKMARKS_ON_MENU + 1)
do_check_eq(itemId, -1);
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
SMART_BOOKMARKS_ON_MENU)
do_check_eq(itemId, -1);
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: SMART_BOOKMARKS_ON_MENU + 1
})));
remove_bookmarks_html();
do_test_finished();
}
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_MENU
})));
});

Просмотреть файл

@ -15,269 +15,228 @@ const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
const TOPICDATA_FORCE_PLACES_INIT = "force-places-init";
let bg = Cc["@mozilla.org/browser/browserglue;1"].
getService(Ci.nsIBrowserGlue);
getService(Ci.nsIObserver);
function waitForImportAndSmartBookmarks(aCallback) {
Services.obs.addObserver(function waitImport() {
Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
// Delay to test eventual smart bookmarks creation.
do_execute_soon(function () {
PlacesTestUtils.promiseAsyncUpdates().then(aCallback);
});
}, "bookmarks-restore-success", false);
}
[
// This test must be the first one.
function test_checkPreferences() {
// Initialize Places through the History Service and check that a new
// database has been created.
do_check_eq(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
// Wait for Places init notification.
Services.obs.addObserver(function(aSubject, aTopic, aData) {
Services.obs.removeObserver(arguments.callee,
"places-browser-init-complete");
do_execute_soon(function () {
// Ensure preferences status.
do_check_false(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
try {
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
do_throw("importBookmarksHTML pref should not exist");
}
catch(ex) {}
try {
do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
do_throw("importBookmarksHTML pref should not exist");
}
catch(ex) {}
run_next_test();
});
}, "places-browser-init-complete", false);
},
function test_import()
{
do_print("Import from bookmarks.html if importBookmarksHTML is true.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been imported, and a smart bookmark has been
// created.
itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
SMART_BOOKMARKS_ON_TOOLBAR);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
// Check preferences have been reverted.
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
run_next_test();
});
// Force nsBrowserGlue::_initPlaces().
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
},
function test_import_noSmartBookmarks()
{
do_print("import from bookmarks.html, but don't create smart bookmarks \
if they are disabled");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
// Check preferences have been reverted.
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
run_next_test();
});
// Force nsBrowserGlue::_initPlaces().
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
},
function test_import_autoExport_updatedSmartBookmarks()
{
do_print("Import from bookmarks.html, but don't create smart bookmarks \
if autoExportHTML is true and they are at latest version");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
// Check preferences have been reverted.
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
},
function test_import_autoExport_oldSmartBookmarks()
{
do_print("Import from bookmarks.html, and create smart bookmarks if \
autoExportHTML is true and they are not at latest version.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
SMART_BOOKMARKS_ON_TOOLBAR);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
// Check preferences have been reverted.
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
},
function test_restore()
{
do_print("restore from default bookmarks.html if \
restore_default_bookmarks is true.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been restored.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
SMART_BOOKMARKS_ON_TOOLBAR);
do_check_true(itemId > 0);
// Check preferences have been reverted.
do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
},
function test_restore_import()
{
do_print("setting both importBookmarksHTML and \
restore_default_bookmarks should restore defaults.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
let itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_eq(itemId, -1);
// Set preferences.
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
waitForImportAndSmartBookmarks(function () {
// Check bookmarks.html has been restored.
itemId =
PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
SMART_BOOKMARKS_ON_TOOLBAR);
do_check_true(itemId > 0);
// Check preferences have been reverted.
do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
}
].forEach(add_test);
do_register_cleanup(function () {
remove_all_bookmarks();
remove_bookmarks_html();
remove_all_JSON_backups();
});
function run_test()
{
function run_test() {
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
remove_all_JSON_backups();
// Create our JSON backup from bookmarks.glue.json.
create_JSON_backup("bookmarks.glue.json");
run_next_test();
}
do_register_cleanup(function () {
remove_bookmarks_html();
remove_all_JSON_backups();
return PlacesUtils.bookmarks.eraseEverything();
});
function simulatePlacesInit() {
do_print("Simulate Places init");
let promise = waitForImportAndSmartBookmarks();
// Force nsBrowserGlue::_initPlaces().
bg.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_FORCE_PLACES_INIT);
return promise;
}
add_task(function* test_checkPreferences() {
// Initialize Places through the History Service and check that a new
// database has been created.
Assert.equal(PlacesUtils.history.databaseStatus,
PlacesUtils.history.DATABASE_STATUS_CREATE);
// Wait for Places init notification.
yield promiseTopicObserved("places-browser-init-complete");
// Ensure preferences status.
Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
Assert.throws(() => Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
Assert.throws(() => Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
});
add_task(function* test_import() {
do_print("Import from bookmarks.html if importBookmarksHTML is true.");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
yield simulatePlacesInit();
// Check bookmarks.html has been imported, and a smart bookmark has been
// created.
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(bm.title, "example");
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
});
add_task(function* test_import_noSmartBookmarks() {
do_print("import from bookmarks.html, but don't create smart bookmarks " +
"if they are disabled");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
yield simulatePlacesInit();
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
Assert.equal(bm.title, "example");
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
});
add_task(function* test_import_autoExport_updatedSmartBookmarks() {
do_print("Import from bookmarks.html, but don't create smart bookmarks " +
"if autoExportHTML is true and they are at latest version");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
yield simulatePlacesInit();
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
Assert.equal(bm.title, "example");
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
});
add_task(function* test_import_autoExport_oldSmartBookmarks() {
do_print("Import from bookmarks.html, and create smart bookmarks if " +
"autoExportHTML is true and they are not at latest version.");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
yield simulatePlacesInit();
// Check bookmarks.html has been imported, but smart bookmarks have not
// been created.
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(bm.title, "example");
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
});
add_task(function* test_restore() {
do_print("restore from default bookmarks.html if " +
"restore_default_bookmarks is true.");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
yield simulatePlacesInit();
// Check bookmarks.html has been restored.
Assert.ok(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
}));
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
});
add_task(function* test_restore_import() {
do_print("setting both importBookmarksHTML and " +
"restore_default_bookmarks should restore defaults.");
yield PlacesUtils.bookmarks.eraseEverything();
// Sanity check: we should not have any bookmark on the toolbar.
Assert.ok(!(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
})));
// Set preferences.
Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
yield simulatePlacesInit();
// Check bookmarks.html has been restored.
Assert.ok(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
}));
// Check preferences have been reverted.
Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
Assert.ok(!Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
});

Просмотреть файл

@ -9,74 +9,54 @@
* database has been created and one backup is available.
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "bs",
"@mozilla.org/browser/nav-bookmarks-service;1",
"nsINavBookmarksService");
XPCOMUtils.defineLazyServiceGetter(this, "anno",
"@mozilla.org/browser/annotation-service;1",
"nsIAnnotationService");
let bookmarksObserver = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {
let itemId = bs.getIdForItemAt(bs.toolbarFolder, 0);
do_check_neq(itemId, -1);
if (anno.itemHasAnnotation(itemId, "Places/SmartBookmark"))
continue_test();
},
onItemAdded: function() {},
onItemRemoved: function(id, folder, index, itemType) {},
onItemChanged: function() {},
onItemVisited: function(id, visitID, time) {},
onItemMoved: function() {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
function run_test() {
do_test_pending();
// Create our bookmarks.html copying bookmarks.glue.html to the profile
// folder. It will be ignored.
// Create our bookmarks.html from bookmarks.glue.html.
create_bookmarks_html("bookmarks.glue.html");
// Create our JSON backup copying bookmarks.glue.json to the profile
// folder. It will be ignored.
remove_all_JSON_backups();
// Create our JSON backup from bookmarks.glue.json.
create_JSON_backup("bookmarks.glue.json");
// Remove current database file.
let db = gProfD.clone();
db.append("places.sqlite");
if (db.exists()) {
db.remove(false);
do_check_false(db.exists());
}
clearDB();
run_next_test();
}
do_register_cleanup(function () {
remove_bookmarks_html();
remove_all_JSON_backups();
return PlacesUtils.bookmarks.eraseEverything();
});
add_task(function* test_main() {
// Initialize nsBrowserGlue before Places.
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue);
// Initialize Places through the History Service.
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
// Check a new database has been created.
// nsBrowserGlue uses databaseStatus to manage initialization.
do_check_eq(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
Assert.equal(hs.databaseStatus, hs.DATABASE_STATUS_CREATE);
// The test will continue once restore has finished and smart bookmarks
// have been created.
bs.addObserver(bookmarksObserver, false);
}
yield promiseEndUpdateBatch();
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
function continue_test() {
// Check that JSON backup has been restored.
// Notice restore from JSON notification is fired before smart bookmarks creation.
let itemId = bs.getIdForItemAt(bs.toolbarFolder, SMART_BOOKMARKS_ON_TOOLBAR);
do_check_eq(bs.getItemTitle(itemId), "examplejson");
remove_bookmarks_html();
remove_all_JSON_backups();
do_test_finished();
}
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: SMART_BOOKMARKS_ON_TOOLBAR
});
Assert.equal(bm.title, "examplejson");
});

Просмотреть файл

@ -14,273 +14,13 @@ const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
const PREF_IMPORT_BOOKMARKS_HTML = "browser.places.importBookmarksHTML";
const PREF_RESTORE_DEFAULT_BOOKMARKS = "browser.bookmarks.restore_default_bookmarks";
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
/**
* Rebuilds smart bookmarks listening to console output to report any message or
* exception generated when calling ensurePlacesDefaultQueriesInitialized().
*/
function rebuildSmartBookmarks() {
let consoleListener = {
observe: function(aMsg) {
print("Got console message: " + aMsg.message);
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIConsoleListener
]),
};
Services.console.reset();
Services.console.registerListener(consoleListener);
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue)
.ensurePlacesDefaultQueriesInitialized();
Services.console.unregisterListener(consoleListener);
function run_test() {
remove_bookmarks_html();
remove_all_JSON_backups();
run_next_test();
}
let tests = [];
//------------------------------------------------------------------------------
tests.push({
description: "All smart bookmarks are created if smart bookmarks version is 0.",
exec: function() {
// Sanity check: we should have default bookmark.
do_check_neq(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0), -1);
do_check_neq(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0), -1);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
rebuildSmartBookmarks();
// Count items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "An existing smart bookmark is replaced when version changes.",
exec: function() {
// Sanity check: we have a smart bookmark on the toolbar.
let itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_neq(itemId, -1);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
// Change its title.
PlacesUtils.bookmarks.setItemTitle(itemId, "new title");
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "new title");
// Sanity check items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmark has been replaced, itemId has changed.
itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
do_check_neq(itemId, -1);
do_check_neq(PlacesUtils.bookmarks.getItemTitle(itemId), "new title");
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "bookmarks position is retained when version changes.",
exec: function() {
// Sanity check items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
let itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
let firstItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId);
itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
let secondItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmarks are still in correct position.
itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), firstItemTitle);
itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId, SMART_BOOKMARKS_ANNO));
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), secondItemTitle);
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "moved bookmarks position is retained when version changes.",
exec: function() {
// Sanity check items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
let itemId1 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId1, SMART_BOOKMARKS_ANNO));
let firstItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId1);
let itemId2 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId2, SMART_BOOKMARKS_ANNO));
let secondItemTitle = PlacesUtils.bookmarks.getItemTitle(itemId2);
// Move the first smart bookmark to the end of the menu.
PlacesUtils.bookmarks.moveItem(itemId1, PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
do_check_eq(itemId1, PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX));
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmarks are still in correct position.
itemId2 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId2, SMART_BOOKMARKS_ANNO));
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId2), secondItemTitle);
itemId1 = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
do_check_true(PlacesUtils.annotations.itemHasAnnotation(itemId1, SMART_BOOKMARKS_ANNO));
do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId1), firstItemTitle);
// Move back the smart bookmark to the original position.
PlacesUtils.bookmarks.moveItem(itemId1, PlacesUtils.bookmarksMenuFolderId, 1);
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "An explicitly removed smart bookmark should not be recreated.",
exec: function() {
// Remove toolbar's smart bookmarks
PlacesUtils.bookmarks.removeItem(PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0));
// Sanity check items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
// We should not have recreated the smart bookmark on toolbar.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
tests.push({
description: "Even if a smart bookmark has been removed recreate it if version is 0.",
exec: function() {
// Sanity check items.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
rebuildSmartBookmarks();
// Count items.
// We should not have recreated the smart bookmark on toolbar.
do_check_eq(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
do_check_eq(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
do_check_eq(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
next_test();
}
});
//------------------------------------------------------------------------------
do_register_cleanup(() => PlacesUtils.bookmarks.eraseEverything());
function countFolderChildren(aFolderItemId) {
let rootNode = PlacesUtils.getFolderContents(aFolderItemId).root;
@ -295,57 +35,303 @@ function countFolderChildren(aFolderItemId) {
return cc;
}
function next_test() {
if (tests.length) {
// Execute next test.
let test = tests.shift();
print("\nTEST: " + test.description);
test.exec();
}
else {
// Clean up database from all bookmarks.
remove_all_bookmarks();
do_test_finished();
}
/**
* Rebuilds smart bookmarks listening to console output to report any message or
* exception generated when calling ensurePlacesDefaultQueriesInitialized().
*/
function rebuildSmartBookmarks() {
let consoleListener = {
observe: function(aMsg) {
do_throw("Got console message: " + aMsg.message);
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIConsoleListener
]),
};
Services.console.reset();
Services.console.registerListener(consoleListener);
Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue)
.ensurePlacesDefaultQueriesInitialized();
Services.console.unregisterListener(consoleListener);
}
function run_test() {
do_test_pending();
remove_bookmarks_html();
remove_all_JSON_backups();
add_task(function* setup() {
// Initialize browserGlue, but remove it's listener to places-init-complete.
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
// Initialize Places.
PlacesUtils.history;
// Observes Places initialisation complete.
Services.obs.addObserver(function waitPlaceInitComplete() {
Services.obs.removeObserver(waitPlaceInitComplete, "places-browser-init-complete");
// Ensure preferences status.
do_check_false(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
try {
do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
do_throw("importBookmarksHTML pref should not exist");
}
catch(ex) {}
// Wait for Places init notification.
yield promiseTopicObserved("places-browser-init-complete");
waitForImportAndSmartBookmarks(next_test);
}, "places-browser-init-complete", false);
// Ensure preferences status.
Assert.ok(!Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
Assert.ok(!Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
Assert.throws(() => Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
// Usually places init would async notify to glue, but we want to avoid
// randomness here, thus we fire the notification synchronously.
bg.observe(null, "places-init-complete", null);
}
yield waitForImportAndSmartBookmarks();
});
function waitForImportAndSmartBookmarks(aCallback) {
Services.obs.addObserver(function waitImport() {
Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
// Delay to test eventual smart bookmarks creation.
do_execute_soon(function () {
PlacesTestUtils.promiseAsyncUpdates().then(aCallback);
});
}, "bookmarks-restore-success", false);
}
add_task(function* test_version_0() {
do_print("All smart bookmarks are created if smart bookmarks version is 0.");
// Sanity check: we should have default bookmark.
Assert.ok(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
}));
Assert.ok(yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
}));
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
rebuildSmartBookmarks();
// Count items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});
add_task(function* test_version_change() {
do_print("An existing smart bookmark is replaced when version changes.");
// Sanity check: we have a smart bookmark on the toolbar.
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
// Change its title.
yield PlacesUtils.bookmarks.update({guid: bm.guid, title: "new title"});
bm = yield PlacesUtils.bookmarks.fetch({guid: bm.guid});
Assert.equal(bm.title, "new title");
// Sanity check items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmark has been replaced, itemId has changed.
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
Assert.notEqual(bm.title, "new title");
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});
add_task(function* test_version_change_pos() {
do_print("bookmarks position is retained when version changes.");
// Sanity check items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
let firstItemTitle = bm.title;
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 1
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
let secondItemTitle = bm.title;
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmarks are still in correct position.
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
Assert.equal(bm.title, firstItemTitle);
bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 1
});
yield checkItemHasAnnotation(bm.guid, SMART_BOOKMARKS_ANNO);
Assert.equal(bm.title, secondItemTitle);
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});
add_task(function* test_version_change_pos_moved() {
do_print("moved bookmarks position is retained when version changes.");
// Sanity check items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
let bm1 = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
});
yield checkItemHasAnnotation(bm1.guid, SMART_BOOKMARKS_ANNO);
let firstItemTitle = bm1.title;
let bm2 = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 1
});
yield checkItemHasAnnotation(bm2.guid, SMART_BOOKMARKS_ANNO);
let secondItemTitle = bm2.title;
// Move the first smart bookmark to the end of the menu.
yield PlacesUtils.bookmarks.update({
parentGuid: PlacesUtils.bookmarks.menuGuid,
guid: bm1.guid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
Assert.equal(bm.guid, bm1.guid);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check smart bookmarks are still in correct position.
bm2 = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: 0
});
yield checkItemHasAnnotation(bm2.guid, SMART_BOOKMARKS_ANNO);
Assert.equal(bm2.title, secondItemTitle);
bm1 = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
yield checkItemHasAnnotation(bm1.guid, SMART_BOOKMARKS_ANNO);
Assert.equal(bm1.title, firstItemTitle);
// Move back the smart bookmark to the original position.
yield PlacesUtils.bookmarks.update({
parentGuid: PlacesUtils.bookmarks.menuGuid,
guid: bm1.guid,
index: 1
});
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});
add_task(function* test_recreation() {
do_print("An explicitly removed smart bookmark should not be recreated.");
// Remove toolbar's smart bookmarks
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: 0
});
yield PlacesUtils.bookmarks.remove(bm.guid);
// Sanity check items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 1);
rebuildSmartBookmarks();
// Count items.
// We should not have recreated the smart bookmark on toolbar.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});
add_task(function* test_recreation_version_0() {
do_print("Even if a smart bookmark has been removed recreate it if version is 0.");
// Sanity check items.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Set preferences.
Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
rebuildSmartBookmarks();
// Count items.
// We should not have recreated the smart bookmark on toolbar.
Assert.equal(countFolderChildren(PlacesUtils.toolbarFolderId),
SMART_BOOKMARKS_ON_TOOLBAR + DEFAULT_BOOKMARKS_ON_TOOLBAR);
Assert.equal(countFolderChildren(PlacesUtils.bookmarksMenuFolderId),
SMART_BOOKMARKS_ON_MENU + DEFAULT_BOOKMARKS_ON_MENU);
// Check version has been updated.
Assert.equal(Services.prefs.getIntPref(PREF_SMART_BOOKMARKS_VERSION),
SMART_BOOKMARKS_VERSION);
});

Просмотреть файл

@ -8,96 +8,42 @@
* Tests that we build a working leftpane in various corruption situations.
*/
function run_test() {
// We want empty roots.
remove_all_bookmarks();
// Sanity check.
Assert.ok(!!PlacesUIUtils);
// Check getters.
gLeftPaneFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId");
Assert.equal(typeof(gLeftPaneFolderIdGetter.get), "function");
gAllBookmarksFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "allBookmarksFolderId");
Assert.equal(typeof(gAllBookmarksFolderIdGetter.get), "function");
run_next_test();
}
do_register_cleanup(remove_all_bookmarks);
// Used to store the original leftPaneFolderId getter.
let gLeftPaneFolderIdGetter;
let gAllBookmarksFolderIdGetter;
// Used to store the original left Pane status as a JSON string.
let gReferenceHierarchy;
let gLeftPaneFolderId;
// Third party annotated folder.
let gFolderId;
// Corruption cases.
let gTests = [
function test1() {
print("1. Do nothing, checks test calibration.");
},
function test2() {
print("2. Delete the left pane folder.");
PlacesUtils.bookmarks.removeItem(gLeftPaneFolderId);
},
function test3() {
print("3. Delete a child of the left pane folder.");
let id = PlacesUtils.bookmarks.getIdForItemAt(gLeftPaneFolderId, 0);
PlacesUtils.bookmarks.removeItem(id);
},
function test4() {
print("4. Delete AllBookmarks.");
PlacesUtils.bookmarks.removeItem(PlacesUIUtils.allBookmarksFolderId);
},
function test5() {
print("5. Create a duplicated left pane folder.");
let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
"PlacesRoot",
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.annotations.setItemAnnotation(id, ORGANIZER_FOLDER_ANNO,
"PlacesRoot", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
},
function test6() {
print("6. Create a duplicated left pane query.");
let id = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
"AllBookmarks",
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.annotations.setItemAnnotation(id, ORGANIZER_QUERY_ANNO,
"AllBookmarks", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
},
function test7() {
print("7. Remove the left pane folder annotation.");
PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
ORGANIZER_FOLDER_ANNO);
},
function test8() {
print("8. Remove a left pane query annotation.");
PlacesUtils.annotations.removeItemAnnotation(PlacesUIUtils.allBookmarksFolderId,
ORGANIZER_QUERY_ANNO);
},
function test9() {
print("9. Remove a child of AllBookmarks.");
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUIUtils.allBookmarksFolderId, 0);
PlacesUtils.bookmarks.removeItem(id);
},
];
function run_test() {
// We want empty roots.
remove_all_bookmarks();
// Sanity check.
do_check_true(!!PlacesUIUtils);
// Check getters.
gLeftPaneFolderIdGetter = PlacesUIUtils.__lookupGetter__("leftPaneFolderId");
do_check_eq(typeof(gLeftPaneFolderIdGetter), "function");
gAllBookmarksFolderIdGetter = PlacesUIUtils.__lookupGetter__("allBookmarksFolderId");
do_check_eq(typeof(gAllBookmarksFolderIdGetter), "function");
add_task(function* () {
// Add a third party bogus annotated item. Should not be removed.
gFolderId = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
"test",
PlacesUtils.bookmarks.DEFAULT_INDEX);
PlacesUtils.annotations.setItemAnnotation(gFolderId, ORGANIZER_QUERY_ANNO,
let folder = yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "test",
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
type: PlacesUtils.bookmarks.TYPE_FOLDER
});
let folderId = yield PlacesUtils.promiseItemId(folder.guid);
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
"test", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
@ -105,37 +51,101 @@ function run_test() {
// as reference value.
gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
gReferenceHierarchy = folderIdToHierarchy(gLeftPaneFolderId);
do_test_pending();
run_next_test();
}
function run_next_test() {
if (gTests.length) {
// Create corruption.
let test = gTests.shift();
test();
while (gTests.length) {
// Run current test.
yield Task.spawn(gTests.shift());
// Regenerate getters.
PlacesUIUtils.__defineGetter__("leftPaneFolderId", gLeftPaneFolderIdGetter);
Object.defineProperty(PlacesUIUtils, "leftPaneFolderId", gLeftPaneFolderIdGetter);
gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
PlacesUIUtils.__defineGetter__("allBookmarksFolderId", gAllBookmarksFolderIdGetter);
Object.defineProperty(PlacesUIUtils, "allBookmarksFolderId", gAllBookmarksFolderIdGetter);
// Check the new left pane folder.
Task.spawn(function() {
let leftPaneHierarchy = folderIdToHierarchy(gLeftPaneFolderId)
if (gReferenceHierarchy != leftPaneHierarchy) {
do_throw("hierarchies differ!\n" + gReferenceHierarchy +
"\n" + leftPaneHierarchy);
}
do_check_eq(PlacesUtils.bookmarks.getItemTitle(gFolderId), "test");
// Go to next test.
run_next_test();
let leftPaneHierarchy = folderIdToHierarchy(gLeftPaneFolderId)
Assert.equal(gReferenceHierarchy, leftPaneHierarchy);
folder = yield PlacesUtils.bookmarks.fetch({guid: folder.guid});
Assert.equal(folder.title, "test");
}
});
// Corruption cases.
let gTests = [
function* test1() {
print("1. Do nothing, checks test calibration.");
},
function* test2() {
print("2. Delete the left pane folder.");
let guid = yield PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
yield PlacesUtils.bookmarks.remove(guid);
},
function* test3() {
print("3. Delete a child of the left pane folder.");
let guid = yield PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
let bm = yield PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
yield PlacesUtils.bookmarks.remove(bm.guid);
},
function* test4() {
print("4. Delete AllBookmarks.");
let guid = yield PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
yield PlacesUtils.bookmarks.remove(guid);
},
function* test5() {
print("5. Create a duplicated left pane folder.");
let folder = yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "PlacesRoot",
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
type: PlacesUtils.bookmarks.TYPE_FOLDER
});
let folderId = yield PlacesUtils.promiseItemId(folder.guid);
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_FOLDER_ANNO,
"PlacesRoot", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
},
function* test6() {
print("6. Create a duplicated left pane query.");
let folder = yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "AllBookmarks",
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
type: PlacesUtils.bookmarks.TYPE_FOLDER
});
let folderId = yield PlacesUtils.promiseItemId(folder.guid);
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
"AllBookmarks", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
},
function* test7() {
print("7. Remove the left pane folder annotation.");
PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
ORGANIZER_FOLDER_ANNO);
},
function* test8() {
print("8. Remove a left pane query annotation.");
PlacesUtils.annotations.removeItemAnnotation(PlacesUIUtils.allBookmarksFolderId,
ORGANIZER_QUERY_ANNO);
},
function* test9() {
print("9. Remove a child of AllBookmarks.");
let guid = yield PlacesUtils.promiseItemGuid(PlacesUIUtils.allBookmarksFolderId);
let bm = yield PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
yield PlacesUtils.bookmarks.remove(bm.guid);
}
else {
// All tests finished.
remove_all_bookmarks();
do_test_finished();
}
}
];
/**
* Convert a folder item id to a JSON representation of it and its contents.

Просмотреть файл

@ -313,6 +313,16 @@ let gSyncPane = {
[email], 1) :
sb.GetStringFromName("needUserLong");
// The "Learn more" link.
if (!email) {
let learnMoreLink = document.createElement("label");
learnMoreLink.className = "text-link";
let { text, href } = fxaMigrator.learnMoreLink;
learnMoreLink.setAttribute("value", text);
learnMoreLink.href = href;
elt.appendChild(learnMoreLink);
}
// The "upgrade" button.
let button = document.getElementById("sync-migrate-upgrade");
button.setAttribute("label",

Просмотреть файл

@ -213,6 +213,16 @@ let gSyncPane = {
[email], 1) :
sb.GetStringFromName("needUserLong");
// The "Learn more" link.
if (!email) {
let learnMoreLink = document.createElement("label");
learnMoreLink.className = "text-link";
let { text, href } = fxaMigrator.learnMoreLink;
learnMoreLink.setAttribute("value", text);
learnMoreLink.href = href;
elt.appendChild(learnMoreLink);
}
// The "upgrade" button.
let button = document.getElementById("sync-migrate-upgrade");
button.setAttribute("label",

Просмотреть файл

@ -138,6 +138,8 @@ function PlayerWidget(player, containerEl) {
this.onStateChanged = this.onStateChanged.bind(this);
this.onPlayPauseBtnClick = this.onPlayPauseBtnClick.bind(this);
this.metaDataComponent = new PlayerMetaDataHeader();
}
PlayerWidget.prototype = {
@ -159,6 +161,7 @@ PlayerWidget.prototype = {
this.stopTimelineAnimation();
this.stopListeners();
this.metaDataComponent.destroy();
this.el.remove();
this.playPauseBtnEl = this.currentTimeEl = this.timeDisplayEl = null;
@ -184,45 +187,8 @@ PlayerWidget.prototype = {
}
});
// Animation header
let titleEl = createNode({
parent: this.el,
attributes: {
"class": "animation-title"
}
});
let titleHTML = "";
// Name.
if (state.name) {
// Css animations have names.
titleHTML += L10N.getStr("player.animationNameLabel");
titleHTML += "<strong>" + state.name + "</strong>";
} else {
// Css transitions don't.
titleHTML += L10N.getStr("player.transitionNameLabel");
}
// Duration, delay and iteration count.
titleHTML += "<span class='meta-data'>";
titleHTML += L10N.getStr("player.animationDurationLabel");
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
this.getFormattedTime(state.duration)) + "</strong>";
if (state.delay) {
titleHTML += L10N.getStr("player.animationDelayLabel");
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
this.getFormattedTime(state.delay)) + "</strong>";
}
if (state.iterationCount !== 1) {
titleHTML += L10N.getStr("player.animationIterationCountLabel");
let count = state.iterationCount || L10N.getStr("player.infiniteIterationCount");
titleHTML += "<strong>" + count + "</strong>";
}
titleHTML += "</span>";
titleEl.innerHTML = titleHTML;
this.metaDataComponent.createMarkup(this.el);
this.metaDataComponent.render(state);
// Timeline widget.
let timelineEl = createNode({
@ -296,18 +262,6 @@ PlayerWidget.prototype = {
this.displayTime(state.currentTime);
},
/**
* Format time as a string.
* @param {Number} time Defaults to the player's currentTime.
* @return {String} The formatted time, e.g. "10.55"
*/
getFormattedTime: function(time) {
return (time/1000).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
},
/**
* Executed when the playPause button is clicked.
* Note that tests may want to call this callback directly rather than
@ -328,7 +282,8 @@ PlayerWidget.prototype = {
*/
onStateChanged: function() {
let state = this.player.state;
this.updateWidgetState(state.playState);
this.updateWidgetState(state);
this.metaDataComponent.render(state);
switch (state.playState) {
case "finished":
@ -354,7 +309,7 @@ PlayerWidget.prototype = {
pause: function() {
// Switch to the right className on the element right away to avoid waiting
// for the next state update to change the playPause icon.
this.updateWidgetState("paused");
this.updateWidgetState({playState: "paused"});
return this.player.pause().then(() => {
this.stopTimelineAnimation();
});
@ -368,12 +323,12 @@ PlayerWidget.prototype = {
play: function() {
// Switch to the right className on the element right away to avoid waiting
// for the next state update to change the playPause icon.
this.updateWidgetState("running");
this.updateWidgetState({playState: "running"});
this.startTimelineAnimation();
return this.player.play();
},
updateWidgetState: function(playState) {
updateWidgetState: function({playState}) {
this.el.className = "player-widget " + playState;
},
@ -417,7 +372,7 @@ PlayerWidget.prototype = {
// Set the time label value.
this.timeDisplayEl.textContent = L10N.getFormatStr("player.timeLabel",
this.getFormattedTime(time));
L10N.numberWithDecimals(time / 1000, 2));
// Set the timeline slider value.
if (!state.iterationCount && time !== state.duration) {
@ -437,6 +392,162 @@ PlayerWidget.prototype = {
}
};
/**
* UI component responsible for displaying and updating the player meta-data:
* name, duration, iterations, delay.
* The parent UI component for this should drive its updates by calling
* render(state) whenever it wants the component to update.
*/
function PlayerMetaDataHeader() {
// Store the various state pieces we need to only refresh the UI when things
// change.
this.state = {};
}
PlayerMetaDataHeader.prototype = {
createMarkup: function(containerEl) {
// The main title element.
this.el = createNode({
parent: containerEl,
attributes: {
"class": "animation-title"
}
});
// Animation name (value hidden by default since transitions don't have names).
this.nameLabel = createNode({
parent: this.el,
nodeType: "span"
});
this.nameValue = createNode({
parent: this.el,
nodeType: "strong",
attributes: {
"style": "display:none;"
}
});
// Animation duration, delay and iteration container.
let metaData = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"class": "meta-data"
}
});
// Animation duration.
this.durationLabel = createNode({
parent: metaData,
nodeType: "span"
});
this.durationLabel.textContent = L10N.getStr("player.animationDurationLabel");
this.durationValue = createNode({
parent: metaData,
nodeType: "strong"
});
// Animation delay (hidden by default since there may not be a delay).
this.delayLabel = createNode({
parent: metaData,
nodeType: "span",
attributes: {
"style": "display:none;"
}
});
this.delayLabel.textContent = L10N.getStr("player.animationDelayLabel");
this.delayValue = createNode({
parent: metaData,
nodeType: "strong"
});
// Animation iteration count (also hidden by default since we don't display
// single iterations).
this.iterationLabel = createNode({
parent: metaData,
nodeType: "span",
attributes: {
"style": "display:none;"
}
});
this.iterationLabel.textContent = L10N.getStr("player.animationIterationCountLabel");
this.iterationValue = createNode({
parent: metaData,
nodeType: "strong",
attributes: {
"style": "display:none;"
}
});
},
destroy: function() {
this.state = null;
this.el.remove();
this.el = null;
this.nameLabel = this.nameValue = null;
this.durationLabel = this.durationValue = null;
this.delayLabel = this.delayValue = null;
this.iterationLabel = this.iterationValue = null;
},
render: function(state) {
// Update the name if needed.
if (state.name !== this.state.name) {
if (state.name) {
// Css animations have names.
this.nameLabel.textContent = L10N.getStr("player.animationNameLabel");
this.nameValue.style.display = "inline";
this.nameValue.textContent = state.name;
} else {
// Css transitions don't.
this.nameLabel.textContent = L10N.getStr("player.transitionNameLabel");
this.nameValue.style.display = "none";
}
}
// update the duration value if needed.
if (state.duration !== this.state.duration) {
this.durationValue.textContent = L10N.getFormatStr("player.timeLabel",
L10N.numberWithDecimals(state.duration / 1000, 2));
}
// Update the delay if needed.
if (state.delay !== this.state.delay) {
if (state.delay) {
this.delayLabel.style.display = "inline";
this.delayValue.style.display = "inline";
this.delayValue.textContent = L10N.getFormatStr("player.timeLabel",
L10N.numberWithDecimals(state.delay / 1000, 2));
} else {
// Hide the delay elements if there is no delay defined.
this.delayLabel.style.display = "none";
this.delayValue.style.display = "none";
}
}
// Update the iterationCount if needed.
if (state.iterationCount !== this.state.iterationCount) {
if (state.iterationCount !== 1) {
this.iterationLabel.style.display = "inline";
this.iterationValue.style.display = "inline";
let count = state.iterationCount ||
L10N.getStr("player.infiniteIterationCount");
this.iterationValue.innerHTML = count;
} else {
// Hide the iteration elements if iteration is 1.
this.iterationLabel.style.display = "none";
this.iterationValue.style.display = "none";
}
}
this.state = state;
}
};
/**
* DOM node creation helper function.
* @param {Object} Options to customize the node to be created.

Просмотреть файл

@ -22,3 +22,4 @@ support-files =
[browser_animation_timeline_animates.js]
[browser_animation_timeline_waits_for_delay.js]
[browser_animation_ui_updates_when_animation_changes.js]
[browser_animation_ui_updates_when_animation_data_changes.js]

Просмотреть файл

@ -13,12 +13,22 @@ add_task(function*() {
info("Selecting a node with an animation that doesn't repeat");
yield selectNode(".long", inspector);
let widget = panel.playerWidgets[0];
let metaDataLabels = widget.el.querySelectorAll(".animation-title .meta-data strong");
is(metaDataLabels.length, 1, "Only the duration is shown");
ok(isNodeVisible(widget.metaDataComponent.durationValue),
"The duration value is shown");
ok(!isNodeVisible(widget.metaDataComponent.delayValue),
"The delay value is hidden");
ok(!isNodeVisible(widget.metaDataComponent.iterationValue),
"The iteration count is hidden");
info("Selecting a node with an animation that repeats several times");
yield selectNode(".delayed", inspector);
widget = panel.playerWidgets[0];
let iterationLabel = widget.el.querySelectorAll(".animation-title .meta-data strong")[2];
is(iterationLabel.textContent, "10", "The iteration is shown");
ok(isNodeVisible(widget.metaDataComponent.durationValue),
"The duration value is shown");
ok(isNodeVisible(widget.metaDataComponent.delayValue),
"The delay value is shown");
ok(isNodeVisible(widget.metaDataComponent.iterationValue),
"The iteration count is shown");
});

Просмотреть файл

@ -8,6 +8,8 @@
// slider don't show values bigger than the animation duration (which would
// happen if the local requestAnimationFrame loop didn't stop correctly).
let L10N = new ViewHelpers.L10N();
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
@ -35,6 +37,6 @@ add_task(function*() {
is(widget.currentTimeEl.value, front.state.duration,
"The timeline slider has the right value");
is(widget.timeDisplayEl.textContent,
widget.getFormattedTime(front.state.duration) + "s",
L10N.numberWithDecimals(front.state.duration / 1000, 2) + "s",
"The timeline slider has the right value");
});

Просмотреть файл

@ -26,9 +26,11 @@ add_task(function*() {
ok(metaDataEl, "The meta-data element exists");
let metaDataEls = metaDataEl.querySelectorAll("strong");
is(metaDataEls.length, 2, "2 meta-data elements were found");
is(metaDataEls[0].textContent, "2.00s",
is(metaDataEls.length, 3, "3 meta-data elements were found");
is(metaDataEls[0].textContent, "2s",
"The first meta-data is the duration, and is correct");
ok(!isNodeVisible(metaDataEls[1]),
"The second meta-data is hidden, since there's no delay on the animation");
info("Select the node with the delayed animation");
yield selectNode(".delayed", inspector);
@ -40,10 +42,13 @@ add_task(function*() {
metaDataEls = titleEl.querySelectorAll(".meta-data strong");
is(metaDataEls.length, 3,
"3 meta-data elements were found for the delayed animation");
is(metaDataEls[0].textContent, "3.00s",
is(metaDataEls[0].textContent, "3s",
"The first meta-data is the duration, and is correct");
is(metaDataEls[1].textContent, "60.00s",
ok(isNodeVisible(metaDataEls[0]), "The duration is shown");
is(metaDataEls[1].textContent, "60s",
"The second meta-data is the delay, and is correct");
ok(isNodeVisible(metaDataEls[1]), "The delay is shown");
is(metaDataEls[2].textContent, "10",
"The third meta-data is the iteration count, and is correct");
ok(isNodeVisible(metaDataEls[2]), "The iteration count is shown");
});

Просмотреть файл

@ -7,6 +7,8 @@
// Test that once an animation is paused and its widget is refreshed, the right
// initial time is displayed.
let L10N = new ViewHelpers.L10N();
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
@ -25,6 +27,7 @@ add_task(function*() {
widget = panel.playerWidgets[0];
ok(widget.el.classList.contains("paused"), "The widget is still in paused mode");
is(widget.timeDisplayEl.textContent,
widget.getFormattedTime(widget.player.state.currentTime) + "s",
L10N.numberWithDecimals(widget.player.state.currentTime / 1000, 2) + "s",
"The initial time has been set to the player's");
});

Просмотреть файл

@ -20,5 +20,5 @@ add_task(function*() {
is(timeline.value, 0, "The timeline is at 0 since the animation hasn't started");
let timeLabel = widget.timeDisplayEl;
is(timeLabel.textContent, "0.00s", "The current time is 0");
is(timeLabel.textContent, "0s", "The current time is 0");
});

Просмотреть файл

@ -0,0 +1,45 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Verify that if the animation's duration, iterations or delay change in
// content, then the widget reflects the changes.
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {panel, inspector} = yield openAnimationInspector();
info("Select the test node");
yield selectNode(".animated", inspector);
info("Get the player widget");
let widget = panel.playerWidgets[0];
yield setStyle(widget, "animationDuration", "5.5s");
is(widget.metaDataComponent.durationValue.textContent, "5.50s",
"The widget shows the new duration");
yield setStyle(widget, "animationIterationCount", "300");
is(widget.metaDataComponent.iterationValue.textContent, "300",
"The widget shows the new iteration count");
yield setStyle(widget, "animationDelay", "45s");
is(widget.metaDataComponent.delayValue.textContent, "45s",
"The widget shows the new delay");
});
function* setStyle(widget, name, value) {
info("Change the animation style via the content DOM. Setting " +
name + " to " + value);
yield executeInContent("Test:SetNodeStyle", {
propertyName: name,
propertyValue: value
}, {
node: getNode(".animated")
});
info("Wait for the next state update");
yield widget.player.once(widget.player.AUTO_REFRESH_EVENT);
}

Просмотреть файл

@ -26,3 +26,21 @@ addMessageListener("Test:ToggleAnimationPlayer", function(msg) {
sendAsyncMessage("Test:ToggleAnimationPlayer");
});
/**
* Set a given style property value on a node. This is useful to dynamically
* change an animation's duration or delay for instance.
* @param {Object} data
* - {String} propertyName The name of the property to set.
* - {String} propertyValue The value for the property.
* @param {Object} objects
* - {DOMNode} node The node to use
*/
addMessageListener("Test:SetNodeStyle", function(msg) {
let {propertyName, propertyValue} = msg.data;
let {node} = msg.objects;
node.style[propertyName] = propertyValue;
sendAsyncMessage("Test:SetNodeStyle");
});

Просмотреть файл

@ -54,7 +54,7 @@
left: 10px;
background: red;
animation: simple-animation 2s
animation: simple-animation 2s;
}
.long {
@ -62,7 +62,7 @@
left: 10px;
background: blue;
animation: simple-animation 120s
animation: simple-animation 120s;
}
@keyframes simple-animation {

Просмотреть файл

@ -5,11 +5,12 @@
"use strict";
const Cu = Components.utils;
let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
let TargetFactory = devtools.TargetFactory;
let {console} = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const TargetFactory = devtools.TargetFactory;
const {console} = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
const {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
// All tests are asynchronous
waitForExplicitFinish();
@ -263,12 +264,30 @@ function executeInContent(name, data={}, objects={}, expectResponse=true) {
* Simulate a click on the playPause button of a playerWidget.
*/
let togglePlayPauseButton = Task.async(function*(widget) {
let nextState = widget.player.state.playState === "running" ? "paused" : "running";
// Note that instead of simulating a real event here, the callback is just
// called. This is better because the callback returns a promise, so we know
// when the player is paused, and we don't really care to test that simulating
// a DOM event actually works.
yield widget.onPlayPauseBtnClick();
let onClicked = widget.onPlayPauseBtnClick();
// Verify that the button's state is changed immediately, even if it will be
// changed anyway with the next auto-refresh.
ok(widget.el.classList.contains(nextState),
"The button's state was changed in the UI before the request was sent");
yield onClicked;
// Wait for the next sate change event to make sure the state is updated
yield widget.player.once(widget.player.AUTO_REFRESH_EVENT);
});
/**
* Is the given node visible in the page (rendered in the frame tree).
* @param {DOMNode}
* @return {Boolean}
*/
function isNodeVisible(node) {
return !!node.getClientRects().length;
}

Просмотреть файл

@ -7,6 +7,25 @@
* Master view handler for the performance tool.
*/
let PerformanceView = {
_state: null,
// Mapping of state to selectors for different panes
// of the main profiler view. Used in `PerformanceView.setState()`
states: {
empty: [
{ deck: "#performance-view", pane: "#empty-notice" }
],
recording: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#recording-notice" }
],
recorded: [
{ deck: "#performance-view", pane: "#performance-view-content" },
{ deck: "#details-pane-container", pane: "#details-pane" }
]
},
/**
* Sets up the view with event binding and main subviews.
*/
@ -18,13 +37,20 @@ let PerformanceView = {
this._onImportButtonClick = this._onImportButtonClick.bind(this);
this._lockRecordButton = this._lockRecordButton.bind(this);
this._unlockRecordButton = this._unlockRecordButton.bind(this);
this._onRecordingSelected = this._onRecordingSelected.bind(this);
this._onRecordingStopped = this._onRecordingStopped.bind(this);
this._recordButton.addEventListener("click", this._onRecordButtonClick);
for (let button of $$(".record-button")) {
button.addEventListener("click", this._onRecordButtonClick);
}
this._importButton.addEventListener("click", this._onImportButtonClick);
// Bind to controller events to unlock the record button
PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
this.setState("empty");
return promise.all([
RecordingsView.initialize(),
@ -38,11 +64,14 @@ let PerformanceView = {
* Unbinds events and destroys subviews.
*/
destroy: function () {
this._recordButton.removeEventListener("click", this._onRecordButtonClick);
for (let button of $$(".record-button")) {
button.removeEventListener("click", this._onRecordButtonClick);
}
this._importButton.removeEventListener("click", this._onImportButtonClick);
PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
return promise.all([
RecordingsView.destroy(),
@ -52,6 +81,29 @@ let PerformanceView = {
]);
},
/**
* Sets the state of the profiler view. Possible options are "empty",
* "recording", "recorded".
*/
setState: function (state) {
let viewConfig = this.states[state];
if (!viewConfig) {
throw new Error(`Invalid state for PerformanceView: ${state}`);
}
for (let { deck, pane } of viewConfig) {
$(deck).selectedPanel = $(pane);
}
this._state = state;
},
/**
* Returns the state of the PerformanceView.
*/
getState: function () {
return this._state;
},
/**
* Adds the `locked` attribute on the record button. This prevents it
* from being clicked while recording is started or stopped.
@ -67,6 +119,20 @@ let PerformanceView = {
this._recordButton.removeAttribute("locked");
},
/**
* When a recording is complete.
*/
_onRecordingStopped: function (_, recording) {
this._unlockRecordButton();
// If this recording stopped is the current recording, set the
// state to "recorded". A stopped recording doesn't necessarily
// have to be the current recording (console.profileEnd, for example)
if (recording === PerformanceController.getCurrentRecording()) {
this.setState("recorded");
}
},
/**
* Handler for clicking the record button.
*/
@ -94,6 +160,17 @@ let PerformanceView = {
if (fp.show() == Ci.nsIFilePicker.returnOK) {
this.emit(EVENTS.UI_IMPORT_RECORDING, fp.file);
}
},
/**
* Fired when a recording is selected. Used to toggle the profiler view state.
*/
_onRecordingSelected: function (_, recording) {
if (recording.isRecording()) {
this.setState("recording");
} else {
this.setState("recorded");
}
}
};

Просмотреть файл

@ -65,7 +65,7 @@
<hbox id="recordings-controls"
class="devtools-toolbarbutton-group">
<toolbarbutton id="record-button"
class="devtools-toolbarbutton"
class="devtools-toolbarbutton record-button"
tooltiptext="&profilerUI.recordButton.tooltip;"/>
<toolbarbutton id="import-button"
class="devtools-toolbarbutton"
@ -111,98 +111,122 @@
</hbox>
</toolbar>
<vbox id="overview-pane">
<hbox id="markers-overview"/>
<hbox id="memory-overview"/>
<hbox id="time-framerate"/>
</vbox>
<deck id="details-pane" flex="1">
<hbox id="waterfall-view" flex="1">
<vbox id="waterfall-breakdown" flex="1" />
<splitter class="devtools-side-splitter"/>
<vbox id="waterfall-details"
class="theme-sidebar"
width="150"
height="150"/>
<deck id="performance-view" flex="1">
<hbox id="empty-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<label value="&profilerUI.emptyNotice1;"/>
<button class="devtools-toolbarbutton record-button"
standalone="true" />
<label value="&profilerUI.emptyNotice2;"/>
</hbox>
<vbox id="performance-view-content" flex="1">
<vbox id="overview-pane">
<hbox id="markers-overview"/>
<hbox id="memory-overview"/>
<hbox id="time-framerate"/>
</vbox>
<deck id="details-pane-container" flex="1">
<hbox id="recording-notice"
class="notice-container"
align="center"
pack="center"
flex="1">
<label value="&profilerUI.stopNotice1;"/>
<button class="devtools-toolbarbutton record-button"
standalone="true"
checked="true" />
<label value="&profilerUI.stopNotice2;"/>
</hbox>
<deck id="details-pane" flex="1">
<hbox id="waterfall-view" flex="1">
<vbox id="waterfall-breakdown" flex="1" />
<splitter class="devtools-side-splitter"/>
<vbox id="waterfall-details"
class="theme-sidebar"
width="150"
height="150"/>
</hbox>
<vbox id="js-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
<vbox id="js-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="js-flamegraph-view" flex="1">
</hbox>
<vbox id="memory-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="allocations"
crop="end"
value="&profilerUI.table.totalAlloc;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="self-allocations"
crop="end"
value="&profilerUI.table.selfAlloc;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="memory-flamegraph-view" flex="1">
</hbox>
</deck>
</deck>
</vbox>
<hbox id="js-flamegraph-view" flex="1">
</hbox>
<vbox id="memory-calltree-view" flex="1">
<hbox class="call-tree-headers-container">
<label class="plain call-tree-header"
type="duration"
crop="end"
value="&profilerUI.table.totalDuration2;"/>
<label class="plain call-tree-header"
type="percentage"
crop="end"
value="&profilerUI.table.totalPercentage;"/>
<label class="plain call-tree-header"
type="allocations"
crop="end"
value="&profilerUI.table.totalAlloc;"/>
<label class="plain call-tree-header"
type="self-duration"
crop="end"
value="&profilerUI.table.selfDuration2;"/>
<label class="plain call-tree-header"
type="self-percentage"
crop="end"
value="&profilerUI.table.selfPercentage;"/>
<label class="plain call-tree-header"
type="self-allocations"
crop="end"
value="&profilerUI.table.selfAlloc;"/>
<label class="plain call-tree-header"
type="samples"
crop="end"
value="&profilerUI.table.samples;"/>
<label class="plain call-tree-header"
type="function"
crop="end"
value="&profilerUI.table.function;"/>
</hbox>
<vbox class="call-tree-cells-container" flex="1"/>
</vbox>
<hbox id="memory-flamegraph-view" flex="1">
<!-- TODO: bug 1077461 -->
</hbox>
</deck>
</vbox>
</hbox>
</window>

Просмотреть файл

@ -52,6 +52,8 @@ support-files =
[browser_perf-shared-connection-02.js]
[browser_perf-shared-connection-03.js]
[browser_perf-ui-recording.js]
[browser_perf-recording-notices-01.js]
[browser_perf-recording-notices-02.js]
[browser_perf_recordings-io-01.js]
[browser_perf_recordings-io-02.js]
[browser_perf_recordings-io-03.js]

Просмотреть файл

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording notice panes are toggled in correct scenarios
* for initialization and a single recording.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, PerformanceView, RecordingsView } = panel.panelWin;
let MAIN_CONTAINER = $("#performance-view");
let EMPTY = $("#empty-notice");
let CONTENT = $("#performance-view-content");
let CONTENT_CONTAINER = $("#details-pane-container");
let RECORDING = $("#recording-notice");
let DETAILS = $("#details-pane");
is(PerformanceView.getState(), "empty", "correct default state");
is(MAIN_CONTAINER.selectedPanel, EMPTY, "showing empty panel on load");
yield startRecording(panel);
is(PerformanceView.getState(), "recording", "correct state during recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
yield stopRecording(panel);
is(PerformanceView.getState(), "recorded", "correct state after recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, DETAILS, "showing rendered graphs");
yield teardown(panel);
finish();
}

Просмотреть файл

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording notice panes are toggled when going between
* a completed recording and an in-progress recording.
*/
function spawnTest () {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, PerformanceController, PerformanceView, RecordingsView } = panel.panelWin;
let MAIN_CONTAINER = $("#performance-view");
let CONTENT = $("#performance-view-content");
let CONTENT_CONTAINER = $("#details-pane-container");
let RECORDING = $("#recording-notice");
let DETAILS = $("#details-pane");
yield startRecording(panel);
yield stopRecording(panel);
yield startRecording(panel);
is(PerformanceView.getState(), "recording", "correct state during recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
let select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
RecordingsView.selectedIndex = 0;
yield select;
is(PerformanceView.getState(), "recorded", "correct state during recording but selecting a completed recording");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, DETAILS, "showing recorded panel");
select = once(PerformanceController, EVENTS.RECORDING_SELECTED);
RecordingsView.selectedIndex = 1;
yield select;
is(PerformanceView.getState(), "recording", "correct state when switching back to recording in progress");
is(MAIN_CONTAINER.selectedPanel, CONTENT, "showing main view with timeline");
is(CONTENT_CONTAINER.selectedPanel, RECORDING, "showing recording panel");
yield stopRecording(panel);
yield teardown(panel);
finish();
}

Просмотреть файл

@ -34,10 +34,29 @@
-moz-padding-end: 8px;
}
/* Recording Notice */
#performance-view .notice-container {
font-size: 120%;
background-color: var(--theme-toolbar-background);
color: var(--theme-body-color);
padding-bottom: 20vh;
}
#performance-view .notice-container button {
min-width: 30px;
min-height: 28px;
margin: 0;
}
/* Overview Panel */
.notice-container button,
#record-button {
list-style-image: url(profiler-stopwatch.svg);
}
.notice-container button[checked],
#record-button[checked] {
list-style-image: url(profiler-stopwatch-checked.svg);
}

Просмотреть файл

@ -891,8 +891,7 @@
.theme-light .scrollbutton-up > .toolbarbutton-icon,
.theme-light .scrollbutton-down > .toolbarbutton-icon,
.theme-light #black-boxed-message-button .button-icon,
.theme-light #profiling-notice-button .button-icon,
.theme-light #canvas-debugging-empty-notice-button .button-icon,
.theme-light .notice-container button .button-icon,
.theme-light #requests-menu-perf-notice-button .button-icon,
.theme-light #requests-menu-network-summary-button .button-icon,
.theme-light .event-tooltip-debugger-icon,
@ -906,7 +905,8 @@
.theme-light .devtools-tab[icon-invertable][selected] > image,
.theme-light .devtools-tab[icon-invertable][highlighted] > image,
.theme-light #record-snapshot[checked] > image,
.theme-light #profiler-start[checked] > image {
.theme-light #profiler-start[checked] > image,
.theme-light .notice-container button[checked] .button-icon {
filter: none !important;
}
@ -958,4 +958,4 @@
to {
transform: rotate(360deg);
}
}
}

Просмотреть файл

@ -10,7 +10,6 @@ import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.ThreadUtils;
@ -22,7 +21,7 @@ import android.content.Context;
import android.util.Log;
import android.widget.Toast;
public final class ReadingListHelper implements GeckoEventListener, NativeEventListener {
public final class ReadingListHelper implements NativeEventListener {
private static final String LOGTAG = "ReadingListHelper";
protected final Context context;
@ -30,43 +29,31 @@ public final class ReadingListHelper implements GeckoEventListener, NativeEventL
public ReadingListHelper(Context context) {
this.context = context;
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
"Reader:AddToList", "Reader:FaviconRequest");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
"Reader:ListStatusRequest", "Reader:RemoveFromList");
"Reader:AddToList", "Reader:FaviconRequest", "Reader:ListStatusRequest", "Reader:RemoveFromList");
}
public void uninit() {
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
"Reader:AddToList", "Reader:FaviconRequest");
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
"Reader:ListStatusRequest", "Reader:RemoveFromList");
}
@Override
public void handleMessage(String event, JSONObject message) {
switch(event) {
case "Reader:AddToList": {
handleAddToList(message);
break;
}
case "Reader:FaviconRequest": {
handleReaderModeFaviconRequest(message.optString("url"));
break;
}
}
"Reader:AddToList", "Reader:FaviconRequest", "Reader:ListStatusRequest", "Reader:RemoveFromList");
}
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
switch(event) {
case "Reader:AddToList": {
handleAddToList(callback, message);
break;
}
case "Reader:FaviconRequest": {
handleReaderModeFaviconRequest(callback, message.getString("url"));
break;
}
case "Reader:RemoveFromList": {
handleRemoveFromList(message.getString("url"));
break;
}
case "Reader:ListStatusRequest": {
handleReadingListStatusRequest(callback, message.getString("url"));
break;
@ -77,10 +64,21 @@ public final class ReadingListHelper implements GeckoEventListener, NativeEventL
/**
* A page can be added to the ReadingList by long-tap of the page-action
* icon, or by tapping the readinglist-add icon in the ReaderMode banner.
*
* This method will only add new items, not update existing items.
*/
private void handleAddToList(final JSONObject message) {
private void handleAddToList(final EventCallback callback, final NativeJSObject message) {
final ContentResolver cr = context.getContentResolver();
final String url = message.optString("url");
final String url = message.getString("url");
// We can't access a NativeJSObject from the background thread, so we need to get the
// values here, even if we may not use them to insert an item into the DB.
final ContentValues values = new ContentValues();
values.put(ReadingListItems.URL, url);
values.put(ReadingListItems.TITLE, message.getString("title"));
values.put(ReadingListItems.LENGTH, message.getInt("length"));
values.put(ReadingListItems.EXCERPT, message.getString("excerpt"));
values.put(ReadingListItems.CONTENT_STATUS, message.getInt("status"));
final BrowserDB db = GeckoProfile.get(context).getDB();
ThreadUtils.postToBackgroundThread(new Runnable() {
@ -88,29 +86,21 @@ public final class ReadingListHelper implements GeckoEventListener, NativeEventL
public void run() {
if (db.isReadingListItem(cr, url)) {
showToast(R.string.reading_list_duplicate, Toast.LENGTH_SHORT);
callback.sendError("URL already in reading list: " + url);
} else {
final ContentValues values = new ContentValues();
values.put(ReadingListItems.URL, url);
values.put(ReadingListItems.TITLE, message.optString("title"));
values.put(ReadingListItems.LENGTH, message.optInt("length"));
values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
values.put(ReadingListItems.CONTENT_STATUS, message.optInt("status"));
db.addReadingListItem(cr, values);
showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
callback.sendSuccess(url);
}
}
});
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Added", url));
}
/**
* Gecko (ReaderMode) requests the page favicon to append to the
* document head for display.
*/
private void handleReaderModeFaviconRequest(final String url) {
private void handleReaderModeFaviconRequest(final EventCallback callback, final String url) {
final BrowserDB db = GeckoProfile.get(context).getDB();
(new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
@Override
@ -121,7 +111,6 @@ public final class ReadingListHelper implements GeckoEventListener, NativeEventL
@Override
public void onPostExecute(String faviconUrl) {
JSONObject args = new JSONObject();
if (faviconUrl != null) {
try {
args.put("url", url);
@ -130,9 +119,7 @@ public final class ReadingListHelper implements GeckoEventListener, NativeEventL
Log.w(LOGTAG, "Error building JSON favicon arguments.", e);
}
}
GeckoAppShell.sendEventToGecko(
GeckoEvent.createBroadcastEvent("Reader:FaviconReturn", args.toString()));
callback.sendSuccess(args.toString());
}
}).execute();
}

Просмотреть файл

@ -770,6 +770,7 @@ sync_java_files = [
'background/bagheera/BoundedByteArrayEntity.java',
'background/bagheera/DeflateHelper.java',
'background/common/DateUtils.java',
'background/common/EditorBranch.java',
'background/common/GlobalConstants.java',
'background/common/log/Logger.java',
'background/common/log/writers/AndroidLevelCachingLogWriter.java',
@ -781,6 +782,7 @@ sync_java_files = [
'background/common/log/writers/StringLogWriter.java',
'background/common/log/writers/TagLogWriter.java',
'background/common/log/writers/ThreadLocalTagLogWriter.java',
'background/common/PrefsBranch.java',
'background/common/telemetry/TelemetryWrapper.java',
'background/datareporting/TelemetryRecorder.java',
'background/db/CursorDumper.java',

Просмотреть файл

@ -0,0 +1,82 @@
/* 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/. */
package org.mozilla.gecko.background.common;
import java.util.Set;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class EditorBranch implements Editor {
private final String prefix;
private Editor editor;
public EditorBranch(final SharedPreferences prefs, final String prefix) {
if (!prefix.endsWith(".")) {
throw new IllegalArgumentException("No trailing period in prefix.");
}
this.prefix = prefix;
this.editor = prefs.edit();
}
public void apply() {
// Android <=r8 SharedPreferences.Editor does not contain apply() for overriding.
this.editor.commit();
}
@Override
public Editor clear() {
this.editor = this.editor.clear();
return this;
}
@Override
public boolean commit() {
return this.editor.commit();
}
@Override
public Editor putBoolean(String key, boolean value) {
this.editor = this.editor.putBoolean(prefix + key, value);
return this;
}
@Override
public Editor putFloat(String key, float value) {
this.editor = this.editor.putFloat(prefix + key, value);
return this;
}
@Override
public Editor putInt(String key, int value) {
this.editor = this.editor.putInt(prefix + key, value);
return this;
}
@Override
public Editor putLong(String key, long value) {
this.editor = this.editor.putLong(prefix + key, value);
return this;
}
@Override
public Editor putString(String key, String value) {
this.editor = this.editor.putString(prefix + key, value);
return this;
}
// Not marking as Override, because Android <= 10 doesn't have
// putStringSet. Neither can we implement it.
public Editor putStringSet(String key, Set<String> value) {
throw new RuntimeException("putStringSet not available.");
}
@Override
public Editor remove(String key) {
this.editor = this.editor.remove(prefix + key);
return this;
}
}

Просмотреть файл

@ -0,0 +1,83 @@
/* 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/. */
package org.mozilla.gecko.background.common;
import java.util.Map;
import java.util.Set;
import android.content.SharedPreferences;
/**
* A wrapper around a portion of the SharedPreferences space.
*/
public class PrefsBranch implements SharedPreferences {
private final SharedPreferences prefs;
private final String prefix; // Including trailing period.
public PrefsBranch(SharedPreferences prefs, String prefix) {
if (!prefix.endsWith(".")) {
throw new IllegalArgumentException("No trailing period in prefix.");
}
this.prefs = prefs;
this.prefix = prefix;
}
@Override
public boolean contains(String key) {
return prefs.contains(prefix + key);
}
@Override
public Editor edit() {
return new EditorBranch(prefs, prefix);
}
@Override
public Map<String, ?> getAll() {
// Not implemented. TODO
return null;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return prefs.getBoolean(prefix + key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return prefs.getFloat(prefix + key, defValue);
}
@Override
public int getInt(String key, int defValue) {
return prefs.getInt(prefix + key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return prefs.getLong(prefix + key, defValue);
}
@Override
public String getString(String key, String defValue) {
return prefs.getString(prefix + key, defValue);
}
// Not marking as Override, because Android <= 10 doesn't have
// getStringSet. Neither can we implement it.
public Set<String> getStringSet(String key, Set<String> defValue) {
throw new RuntimeException("getStringSet not available.");
}
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
prefs.registerOnSharedPreferenceChangeListener(listener);
}
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
prefs.unregisterOnSharedPreferenceChangeListener(listener);
}
}

Просмотреть файл

@ -13,6 +13,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.mozilla.gecko.background.common.PrefsBranch;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.crypto.PersistedCrypto5Keys;
@ -23,158 +24,6 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class SyncConfiguration {
public class EditorBranch implements Editor {
private final String prefix;
private Editor editor;
public EditorBranch(SyncConfiguration config, String prefix) {
if (!prefix.endsWith(".")) {
throw new IllegalArgumentException("No trailing period in prefix.");
}
this.prefix = prefix;
this.editor = config.getEditor();
}
public void apply() {
// Android <=r8 SharedPreferences.Editor does not contain apply() for overriding.
this.editor.commit();
}
@Override
public Editor clear() {
this.editor = this.editor.clear();
return this;
}
@Override
public boolean commit() {
return this.editor.commit();
}
@Override
public Editor putBoolean(String key, boolean value) {
this.editor = this.editor.putBoolean(prefix + key, value);
return this;
}
@Override
public Editor putFloat(String key, float value) {
this.editor = this.editor.putFloat(prefix + key, value);
return this;
}
@Override
public Editor putInt(String key, int value) {
this.editor = this.editor.putInt(prefix + key, value);
return this;
}
@Override
public Editor putLong(String key, long value) {
this.editor = this.editor.putLong(prefix + key, value);
return this;
}
@Override
public Editor putString(String key, String value) {
this.editor = this.editor.putString(prefix + key, value);
return this;
}
// Not marking as Override, because Android <= 10 doesn't have
// putStringSet. Neither can we implement it.
public Editor putStringSet(String key, Set<String> value) {
throw new RuntimeException("putStringSet not available.");
}
@Override
public Editor remove(String key) {
this.editor = this.editor.remove(prefix + key);
return this;
}
}
/**
* A wrapper around a portion of the SharedPreferences space.
*
* @author rnewman
*
*/
public class ConfigurationBranch implements SharedPreferences {
private final SyncConfiguration config;
private final String prefix; // Including trailing period.
public ConfigurationBranch(SyncConfiguration syncConfiguration,
String prefix) {
if (!prefix.endsWith(".")) {
throw new IllegalArgumentException("No trailing period in prefix.");
}
this.config = syncConfiguration;
this.prefix = prefix;
}
@Override
public boolean contains(String key) {
return config.getPrefs().contains(prefix + key);
}
@Override
public Editor edit() {
return new EditorBranch(config, prefix);
}
@Override
public Map<String, ?> getAll() {
// Not implemented. TODO
return null;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return config.getPrefs().getBoolean(prefix + key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return config.getPrefs().getFloat(prefix + key, defValue);
}
@Override
public int getInt(String key, int defValue) {
return config.getPrefs().getInt(prefix + key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return config.getPrefs().getLong(prefix + key, defValue);
}
@Override
public String getString(String key, String defValue) {
return config.getPrefs().getString(prefix + key, defValue);
}
// Not marking as Override, because Android <= 10 doesn't have
// getStringSet. Neither can we implement it.
public Set<String> getStringSet(String key, Set<String> defValue) {
throw new RuntimeException("getStringSet not available.");
}
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
config.getPrefs().registerOnSharedPreferenceChangeListener(listener);
}
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
config.getPrefs().unregisterOnSharedPreferenceChangeListener(listener);
}
}
private static final String LOG_TAG = "SyncConfiguration";
// These must be set in GlobalSession's constructor.
@ -299,11 +148,11 @@ public class SyncConfiguration {
/**
* Return a convenient accessor for part of prefs.
* @return
* A ConfigurationBranch object representing this
* A PrefsBranch object representing this
* section of the preferences space.
*/
public ConfigurationBranch getBranch(String prefix) {
return new ConfigurationBranch(this, prefix);
public PrefsBranch getBranch(String prefix) {
return new PrefsBranch(this.getPrefs(), prefix);
}
/**

Просмотреть файл

@ -7,8 +7,8 @@ package org.mozilla.gecko.sync;
import java.io.IOException;
import org.json.simple.parser.ParseException;
import org.mozilla.gecko.background.common.PrefsBranch;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.sync.SyncConfiguration.ConfigurationBranch;
import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
import android.content.SharedPreferences.Editor;
@ -20,7 +20,7 @@ public class SynchronizerConfiguration {
public RepositorySessionBundle remoteBundle;
public RepositorySessionBundle localBundle;
public SynchronizerConfiguration(ConfigurationBranch config) throws NonObjectJSONException, IOException, ParseException {
public SynchronizerConfiguration(PrefsBranch config) throws NonObjectJSONException, IOException, ParseException {
this.load(config);
}
@ -31,7 +31,7 @@ public class SynchronizerConfiguration {
}
// This should get partly shuffled back into SyncConfiguration, I think.
public void load(ConfigurationBranch config) throws NonObjectJSONException, IOException, ParseException {
public void load(PrefsBranch config) throws NonObjectJSONException, IOException, ParseException {
if (config == null) {
throw new IllegalArgumentException("config cannot be null.");
}
@ -51,7 +51,7 @@ public class SynchronizerConfiguration {
Logger.debug(LOG_TAG, "Loaded SynchronizerConfiguration. syncID: " + syncID + ", remoteBundle: " + remoteBundle + ", localBundle: " + localBundle);
}
public void persist(ConfigurationBranch config) {
public void persist(PrefsBranch config) {
if (config == null) {
throw new IllegalArgumentException("config cannot be null.");
}

Просмотреть файл

@ -71,7 +71,6 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
protected Driver mDriver;
protected Actions mActions;
protected Activity mActivity;
protected String mProfile;
protected abstract Intent createActivityIntent();
@ -140,12 +139,12 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2<A
// Start the activity.
final Intent intent = createActivityIntent();
setActivityIntent(intent);
mActivity = getActivity();
// Set up Robotium.solo and Driver objects
mSolo = new Solo(getInstrumentation(), mActivity);
mDriver = new FennecNativeDriver(mActivity, mSolo, mRootPath);
mActions = new FennecNativeActions(mActivity, mSolo, getInstrumentation(), mAsserter);
Activity tempActivity = getActivity();
mSolo = new Solo(getInstrumentation(), tempActivity);
mDriver = new FennecNativeDriver(tempActivity, mSolo, mRootPath);
mActions = new FennecNativeActions(tempActivity, mSolo, getInstrumentation(), mAsserter);
}
/**

Просмотреть файл

@ -103,7 +103,7 @@ abstract class BaseTest extends BaseRobocopTest {
super.setUp();
mDevice = new Device();
mDatabaseHelper = new DatabaseHelper(mActivity, mAsserter);
mDatabaseHelper = new DatabaseHelper(getActivity(), mAsserter);
// Ensure Robocop tests have access to network, and are run with Display powered on.
throwIfHttpGetFails();
@ -732,7 +732,7 @@ abstract class BaseTest extends BaseRobocopTest {
}
public final void runOnUiThreadSync(Runnable runnable) {
RobocopUtils.runOnUiThreadSync(mActivity, runnable);
RobocopUtils.runOnUiThreadSync(getActivity(), runnable);
}
/* Tap the "star" (bookmark) button to bookmark or un-bookmark the current page */

Просмотреть файл

@ -17,11 +17,6 @@ let Reader = {
observe: function Reader_observe(aMessage, aTopic, aData) {
switch (aTopic) {
case "Reader:Added": {
let mm = window.getGroupMessageManager("browsers");
mm.broadcastAsyncMessage("Reader:Added", { url: aData });
break;
}
case "Reader:Removed": {
let uri = Services.io.newURI(aData, null, null);
ReaderMode.removeArticleFromCache(uri).catch(e => Cu.reportError("Error removing article from cache: " + e));
@ -56,10 +51,13 @@ let Reader = {
receiveMessage: function(message) {
switch (message.name) {
case "Reader:AddToList":
this.addArticleToReadingList(message.data.article);
case "Reader:AddToList": {
// If the article is coming from reader mode, we must have fetched it already.
let article = message.data.article;
article.status = this.STATUS_FETCHED_ARTICLE;
this._addArticleToReadingList(article);
break;
}
case "Reader:ArticleGet":
this._getArticle(message.data.url, message.target).then((article) => {
message.target.messageManager.sendAsyncMessage("Reader:ArticleData", { article: article });
@ -67,14 +65,11 @@ let Reader = {
break;
case "Reader:FaviconRequest": {
let observer = (s, t, d) => {
Services.obs.removeObserver(observer, "Reader:FaviconReturn", false);
message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", JSON.parse(d));
};
Services.obs.addObserver(observer, "Reader:FaviconReturn", false);
Messaging.sendRequest({
Messaging.sendRequestForResult({
type: "Reader:FaviconRequest",
url: message.data.url
}).then(data => {
message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", JSON.parse(data));
});
break;
}
@ -200,31 +195,30 @@ let Reader = {
article = {
url: urlWithoutRef,
title: tab.browser.contentDocument.title,
length: 0,
excerpt: "",
status: this.STATUS_FETCH_FAILED_UNSUPPORTED_FORMAT,
};
} else {
article.status = this.STATUS_FETCHED_ARTICLE;
}
this.addArticleToReadingList(article);
this._addArticleToReadingList(article);
}),
addArticleToReadingList: function(article) {
if (!article || !article.url) {
Cu.reportError("addArticleToReadingList requires article with valid URL");
return;
}
Messaging.sendRequest({
_addArticleToReadingList: function(article) {
Messaging.sendRequestForResult({
type: "Reader:AddToList",
url: truncate(article.url, MAX_URI_LENGTH),
title: truncate(article.title || "", MAX_TITLE_LENGTH),
length: article.length || 0,
excerpt: article.excerpt || "",
title: truncate(article.title, MAX_TITLE_LENGTH),
length: article.length,
excerpt: article.excerpt,
status: article.status,
});
ReaderMode.storeArticleInCache(article).catch(e => Cu.reportError("Error storing article in cache: " + e));
}).then((url) => {
let mm = window.getGroupMessageManager("browsers");
mm.broadcastAsyncMessage("Reader:Added", { url: url });
ReaderMode.storeArticleInCache(article).catch(e => Cu.reportError("Error storing article in cache: " + e));
}).catch(Cu.reportError);
},
/**

Просмотреть файл

@ -146,7 +146,7 @@ let lazilyLoadedObserverScripts = [
["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
["Reader", ["Reader:Added", "Reader:Removed", "Gesture:DoubleTap"], "chrome://browser/content/Reader.js"],
["Reader", ["Reader:Removed", "Gesture:DoubleTap"], "chrome://browser/content/Reader.js"],
];
if (AppConstants.MOZ_WEBRTC) {
lazilyLoadedObserverScripts.push(

Просмотреть файл

@ -34,6 +34,7 @@ def test(mod, path, entity = None):
if path == "chrome/region.properties":
# only region.properties exceptions remain
if (re.match(r"browser\.search\.order\.[1-9]", entity) or
re.match(r"browser\.search\.[a-zA-Z]+\.US", entity) or
re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
re.match(r"gecko\.handlerService\.schemes\.", entity) or
re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity) or

Просмотреть файл

@ -341,7 +341,7 @@ public class SearchActivity extends Locales.LocaleAwareFragmentActivity
@Override
public void onAnimationEnd(Animator animation) {
// Don't do anything if the activity is destroyed before the animation ends.
if (SearchActivity.this.isDestroyed()) {
if (searchEngineManager == null) {
return;
}

Просмотреть файл

@ -34,6 +34,7 @@ def test(mod, path, entity = None):
if path == "chrome/region.properties":
# only region.properties exceptions remain
if (re.match(r"browser\.search\.order\.[1-9]", entity) or
re.match(r"browser\.search\.[a-zA-Z]+\.US", entity) or
re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
re.match(r"gecko\.handlerService\.schemes\.", entity) or
re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity) or

Просмотреть файл

@ -524,8 +524,22 @@ Migrator.prototype = {
default:
throw new Error("Unexpected telemetry flag: " + flag);
}
}
}
},
get learnMoreLink() {
try {
var url = Services.prefs.getCharPref("app.support.baseURL");
} catch (err) {
return null;
}
url += "sync-upgrade";
let sb = Services.strings.createBundle("chrome://weave/locale/services/sync.properties");
return {
text: sb.GetStringFromName("sync.eol.learnMore.label"),
href: Services.urlFormatter.formatURL(url),
};
},
};
// We expose a singleton
this.EXPORTED_SYMBOLS = ["fxaMigrator"];

Просмотреть файл

@ -84,7 +84,7 @@ this.Notifications = {
* A basic notification. Subclass this to create more complex notifications.
*/
this.Notification =
function Notification(title, description, iconURL, priority, buttons) {
function Notification(title, description, iconURL, priority, buttons, link) {
this.title = title;
this.description = description;
@ -96,6 +96,9 @@ this.Notification =
if (buttons)
this.buttons = buttons;
if (link)
this.link = link;
}
// We set each prototype property individually instead of redefining

Просмотреть файл

@ -108,10 +108,7 @@ function DBConn(aForceNewConnection) {
let dbConn = gDBConn = Services.storage.openDatabase(file);
// Be sure to cleanly close this connection.
Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
Services.obs.removeObserver(DBCloseCallback, aTopic);
dbConn.asyncClose();
}, "profile-before-change", false);
promiseTopicObserved("profile-before-change").then(() => dbConn.asyncClose());
}
return gDBConn.connectionReady ? gDBConn : null;
@ -379,15 +376,12 @@ function check_no_bookmarks() {
*/
function promiseTopicObserved(aTopic)
{
let deferred = Promise.defer();
Services.obs.addObserver(
function PTO_observe(aSubject, aTopic, aData) {
Services.obs.removeObserver(PTO_observe, aTopic);
deferred.resolve([aSubject, aData]);
return new Promise(resolve => {
Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
Services.obs.removeObserver(observe, aTopic);
resolve([aSubject, aData]);
}, aTopic, false);
return deferred.promise;
});
}
/**
@ -606,10 +600,7 @@ function is_time_ordered(before, after) {
*/
function waitForConnectionClosed(aCallback)
{
Services.obs.addObserver(function WFCCCallback() {
Services.obs.removeObserver(WFCCCallback, "places-connection-closed");
aCallback();
}, "places-connection-closed", false);
promiseTopicObserved("places-connection-closed").then(aCallback);
shutdownPlaces();
}

Просмотреть файл

@ -720,9 +720,10 @@ let Links = {
_providers: new Set(),
/**
* A mapping from each provider to an object { sortedLinks, linkMap }.
* sortedLinks is the cached, sorted array of links for the provider. linkMap
* is a Map from link URLs to link objects.
* 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.
* linkMap is a Map from link URLs to link objects.
*/
_providerLinks: new Map(),
@ -862,6 +863,21 @@ let Links = {
aLink1.url.localeCompare(aLink2.url);
},
_incrementSiteMap: function(map, link) {
let site = NewTabUtils.extractSite(link.url);
map.set(site, (map.get(site) || 0) + 1);
},
_decrementSiteMap: function(map, link) {
let site = NewTabUtils.extractSite(link.url);
let previousURLCount = map.get(site);
if (previousURLCount === 1) {
map.delete(site);
} else {
map.set(site, previousURLCount - 1);
}
},
/**
* Calls getLinks on the given provider and populates our cache for it.
* @param aProvider The provider whose cache will be populated.
@ -879,6 +895,10 @@ let Links = {
links = links.filter((link) => !!link);
this._providerLinks.set(aProvider, {
sortedLinks: links,
siteMap: links.reduce((map, link) => {
this._incrementSiteMap(map, link);
return map;
}, new Map()),
linkMap: links.reduce((map, link) => {
map.set(link.url, link);
return map;
@ -938,7 +958,7 @@ let Links = {
// a change.
return;
let { sortedLinks, linkMap } = links;
let { sortedLinks, siteMap, linkMap } = links;
let existingLink = linkMap.get(aLink.url);
let insertionLink = null;
let updatePages = false;
@ -983,6 +1003,7 @@ let Links = {
insertionLink[prop] = aLink[prop];
}
linkMap.set(aLink.url, insertionLink);
this._incrementSiteMap(siteMap, aLink);
}
if (insertionLink) {
@ -991,6 +1012,7 @@ let Links = {
if (sortedLinks.length > aProvider.maxNumLinks) {
let lastLink = sortedLinks.pop();
linkMap.delete(lastLink.url);
this._decrementSiteMap(siteMap, lastLink);
}
updatePages = true;
}
@ -1187,6 +1209,14 @@ this.NewTabUtils = {
return false;
},
isTopSiteGivenProvider: function(aSite, aProvider) {
return Links._providerLinks.get(aProvider).siteMap.has(aSite);
},
isTopPlacesSite: function(aSite) {
return this.isTopSiteGivenProvider(aSite, PlacesProvider);
},
/**
* Restores all sites that have been removed from the grid.
*/

Просмотреть файл

@ -11,7 +11,46 @@ function run_test() {
run_next_test();
}
add_test(function multipleProviders() {
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;
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
NewTabUtils.links.populateCache(function () {}, false);
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
do_check_eq(NewTabUtils.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(NewTabUtils.isTopSiteGivenProvider("example3.com", provider), true);
do_check_eq(NewTabUtils.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(NewTabUtils.isTopSiteGivenProvider("example5.com", provider), true);
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example2.com", provider), false);
NewTabUtils.links.removeProvider(provider);
});
add_task(function multipleProviders() {
// Make each provider generate NewTabUtils.links.maxNumLinks links to check
// that no more than maxNumLinks are actually returned in the merged list.
let evenLinks = makeLinks(0, 2 * NewTabUtils.links.maxNumLinks, 2);
@ -35,10 +74,9 @@ add_test(function multipleProviders() {
NewTabUtils.links.removeProvider(evenProvider);
NewTabUtils.links.removeProvider(oddProvider);
run_next_test();
});
add_test(function changeLinks() {
add_task(function changeLinks() {
let expectedLinks = makeLinks(0, 20, 2);
let provider = new TestProvider(done => done(expectedLinks));
@ -91,7 +129,6 @@ add_test(function changeLinks() {
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
NewTabUtils.links.removeProvider(provider);
run_next_test();
});
add_task(function oneProviderAlreadyCached() {

Просмотреть файл

@ -25,4 +25,5 @@ run-if = appname == "firefox"
[test_XPIcancel.js]
[test_XPIStates.js]
[include:xpcshell-shared.ini]

Просмотреть файл

@ -4,20 +4,36 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
notification,
.messageText > .text-link {
color: InfoText !important;
}
notification {
background-color: InfoBackground;
color: InfoText;
text-shadow: none;
}
notification[type="info"],
notification[type="info"] .messageText > .text-link {
color: -moz-DialogText !important;
}
notification[type="info"] {
background-color: -moz-Dialog;
color: -moz-DialogText;
}
notification[type="critical"],
notification[type="critical"] .messageText > .text-link {
color: white !important;
}
notification[type="critical"] {
background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
color: white;
}
.messageText > .text-link {
text-decoration: underline;
}
.notification-inner {

Просмотреть файл

@ -10,27 +10,43 @@ notification {
text-shadow: none;
}
notification[type="info"],
notification[type="info"] .messageText > .text-link {
color: rgba(255,255,255,0.95) !important;
}
notification[type="info"] {
color: rgba(255,255,255,0.95);
background: url("chrome://global/skin/notification/info-bar-background.png") #404040 repeat-x top left;
border-top: 1px solid #707070;
border-bottom: 1px solid #2a2a2a;
}
notification[type="warning"],
notification[type="warning"] .messageText > .text-link {
color: rgba(0,0,0,0.95) !important;
}
notification[type="warning"] {
color: rgba(0,0,0,0.95);
background: url("chrome://global/skin/notification/warning-bar-background.png") #ffc703 repeat-x top left;
border-top: 1px solid #ffe970;
border-bottom: 1px solid #bf8a01;
}
notification[type="critical"],
notification[type="critical"] .messageText > .text-link {
color: rgba(255,255,255,0.95) !important;
}
notification[type="critical"] {
color: rgba(255,255,255,0.95);
background: url("chrome://global/skin/notification/critical-bar-background.png") #980000 repeat-x top left;
border-top: 1px solid #e35959;
border-bottom: 1px solid #5d0000;
}
.messageText > .text-link {
text-decoration: underline;
}
.messageImage {
width: 16px;
height: 16px;

Просмотреть файл

@ -4,20 +4,36 @@
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
notification,
.messageText > .text-link {
color: InfoText !important;
}
notification {
background-color: InfoBackground;
color: InfoText;
text-shadow: none;
}
notification[type="info"],
notification[type="info"] .messageText > .text-link {
color: -moz-DialogText !important;
}
notification[type="info"] {
background-color: -moz-Dialog;
color: -moz-DialogText;
}
notification[type="critical"],
notification[type="critical"] .messageText > .text-link {
color: white !important;
}
notification[type="critical"] {
background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
color: white;
}
.messageText > .text-link {
text-decoration: underline;
}
.messageImage {