зеркало из https://github.com/mozilla/gecko-dev.git
Merge fx-team to m-c a=merge
This commit is contained in:
Коммит
0562d10377
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче