mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 00:07:30 +09:00
Remove browser tests - Part 1: The Tests (except for experiments)
This commit is contained in:
parent
fa08bd44f7
commit
261374f310
1697 changed files with 0 additions and 144364 deletions
|
|
@ -1,9 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../../../../../testing/mochitest/browser.eslintrc.js",
|
||||
"../../../../../testing/mochitest/mochitest.eslintrc.js",
|
||||
]
|
||||
};
|
||||
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
[browser_undo_notification.js]
|
||||
[browser_undo_notification_wording.js]
|
||||
[browser_undo_notification_multiple_dismissal.js]
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
let scope = {};
|
||||
Cu.import("resource:///modules/AutoMigrate.jsm", scope);
|
||||
let oldCanUndo = scope.AutoMigrate.canUndo;
|
||||
let oldUndo = scope.AutoMigrate.undo;
|
||||
registerCleanupFunction(function() {
|
||||
scope.AutoMigrate.canUndo = oldCanUndo;
|
||||
scope.AutoMigrate.undo = oldUndo;
|
||||
});
|
||||
|
||||
const kExpectedNotificationId = "automigration-undo";
|
||||
|
||||
add_task(function* autoMigrationUndoNotificationShows() {
|
||||
let getNotification = browser =>
|
||||
gBrowser.getNotificationBox(browser).getNotificationWithValue(kExpectedNotificationId);
|
||||
|
||||
scope.AutoMigrate.canUndo = () => true;
|
||||
let undoCalled;
|
||||
scope.AutoMigrate.undo = () => { undoCalled = true };
|
||||
for (let url of ["about:newtab", "about:home"]) {
|
||||
undoCalled = false;
|
||||
// Can't use pushPrefEnv because of bug 1323779
|
||||
Services.prefs.setCharPref("browser.migrate.automigrate.browser", "someunknownbrowser");
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
|
||||
let browser = tab.linkedBrowser;
|
||||
if (!getNotification(browser)) {
|
||||
info(`Notification for ${url} not immediately present, waiting for it.`);
|
||||
yield BrowserTestUtils.waitForNotificationBar(gBrowser, browser, kExpectedNotificationId);
|
||||
}
|
||||
|
||||
ok(true, `Got notification for ${url}`);
|
||||
let notification = getNotification(browser);
|
||||
let notificationBox = notification.parentNode;
|
||||
notification.querySelector("button.notification-button-default").click();
|
||||
ok(!undoCalled, "Undo should not be called when clicking the default button");
|
||||
is(notification, notificationBox._closedNotification, "Notification should be closing");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
||||
undoCalled = false;
|
||||
Services.prefs.setCharPref("browser.migrate.automigrate.browser", "chrome");
|
||||
tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
|
||||
browser = tab.linkedBrowser;
|
||||
if (!getNotification(browser)) {
|
||||
info(`Notification for ${url} not immediately present, waiting for it.`);
|
||||
yield BrowserTestUtils.waitForNotificationBar(gBrowser, browser, kExpectedNotificationId);
|
||||
}
|
||||
|
||||
ok(true, `Got notification for ${url}`);
|
||||
notification = getNotification(browser);
|
||||
notificationBox = notification.parentNode;
|
||||
// Set up the survey:
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["browser.migrate.automigrate.undo-survey", "https://example.com/?browser=%IMPORTEDBROWSER%"],
|
||||
["browser.migrate.automigrate.undo-survey-locales", "en-US"],
|
||||
]});
|
||||
let tabOpenedPromise = BrowserTestUtils.waitForNewTab(gBrowser, "https://example.com/?browser=Google%20Chrome");
|
||||
notification.querySelector("button:not(.notification-button-default)").click();
|
||||
ok(undoCalled, "Undo should be called when clicking the non-default (Don't Keep) button");
|
||||
is(notification, notificationBox._closedNotification, "Notification should be closing");
|
||||
let surveyTab = yield tabOpenedPromise;
|
||||
ok(surveyTab, "Should have opened a tab with a survey");
|
||||
yield BrowserTestUtils.removeTab(surveyTab);
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
|
||||
const kExpectedNotificationId = "automigration-undo";
|
||||
|
||||
/**
|
||||
* Pretend we can undo something, trigger a notification, pick the undo option,
|
||||
* and verify that the notifications are all dismissed immediately.
|
||||
*/
|
||||
add_task(function* checkNotificationsDismissed() {
|
||||
yield SpecialPowers.pushPrefEnv({set: [
|
||||
["browser.migrate.automigrate.enabled", true],
|
||||
["browser.migrate.automigrate.ui.enabled", true],
|
||||
]});
|
||||
let getNotification = browser =>
|
||||
gBrowser.getNotificationBox(browser).getNotificationWithValue(kExpectedNotificationId);
|
||||
|
||||
Services.prefs.setCharPref("browser.migrate.automigrate.browser", "someunknownbrowser");
|
||||
|
||||
let {guid, lastModified} = yield PlacesUtils.bookmarks.insert(
|
||||
{title: "Some imported bookmark", parentGuid: PlacesUtils.bookmarks.toolbarGuid, url: "http://www.example.com"}
|
||||
);
|
||||
|
||||
let testUndoData = {
|
||||
visits: [],
|
||||
bookmarks: [{guid, lastModified: lastModified.getTime()}],
|
||||
logins: [],
|
||||
};
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, "initialMigrationMetadata.jsonlz4");
|
||||
registerCleanupFunction(() => {
|
||||
return OS.File.remove(path, {ignoreAbsent: true});
|
||||
});
|
||||
yield OS.File.writeAtomic(path, JSON.stringify(testUndoData), {
|
||||
encoding: "utf-8",
|
||||
compression: "lz4",
|
||||
tmpPath: path + ".tmp",
|
||||
});
|
||||
|
||||
let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", false);
|
||||
if (!getNotification(firstTab.linkedBrowser)) {
|
||||
info(`Notification not immediately present on first tab, waiting for it.`);
|
||||
yield BrowserTestUtils.waitForNotificationBar(gBrowser, firstTab.linkedBrowser, kExpectedNotificationId);
|
||||
}
|
||||
let secondTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", false);
|
||||
if (!getNotification(secondTab.linkedBrowser)) {
|
||||
info(`Notification not immediately present on second tab, waiting for it.`);
|
||||
yield BrowserTestUtils.waitForNotificationBar(gBrowser, secondTab.linkedBrowser, kExpectedNotificationId);
|
||||
}
|
||||
|
||||
// Create a listener for the removal in the first tab, and a listener for bookmarks removal,
|
||||
// then click 'Don't keep' in the second tab, and verify that the notification is removed
|
||||
// before we start removing bookmarks.
|
||||
let haveRemovedBookmark = false;
|
||||
let bmObserver;
|
||||
let bookmarkRemovedPromise = new Promise(resolve => {
|
||||
bmObserver = {
|
||||
onItemRemoved(itemId, parentId, index, itemType, uri, removedGuid) {
|
||||
if (guid == removedGuid) {
|
||||
haveRemovedBookmark = true;
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(bmObserver, false);
|
||||
registerCleanupFunction(() => PlacesUtils.bookmarks.removeObserver(bmObserver));
|
||||
});
|
||||
|
||||
let firstTabNotificationRemovedPromise = new Promise(resolve => {
|
||||
let notification = getNotification(firstTab.linkedBrowser);
|
||||
// Save this reference because notification.parentNode will be null once it's removed.
|
||||
let notificationBox = notification.parentNode;
|
||||
let mut = new MutationObserver(mutations => {
|
||||
// Yucky, but we have to detect either the removal via animation (with marginTop)
|
||||
// or when the element is removed. We can't just detect the element being removed
|
||||
// because this happens asynchronously (after the animation) and so it'd race
|
||||
// with the rest of the undo happening.
|
||||
for (let mutation of mutations) {
|
||||
if (mutation.target == notification && mutation.attributeName == "style" &&
|
||||
parseInt(notification.style.marginTop, 10) < 0) {
|
||||
ok(!haveRemovedBookmark, "Should not have removed bookmark yet");
|
||||
mut.disconnect();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
if (mutation.target == notificationBox && mutation.removedNodes.length &&
|
||||
mutation.removedNodes[0] == notification) {
|
||||
ok(!haveRemovedBookmark, "Should not have removed bookmark yet");
|
||||
mut.disconnect();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
mut.observe(notification.parentNode, {childList: true});
|
||||
mut.observe(notification, {attributes: true});
|
||||
});
|
||||
|
||||
let prefResetPromise = new Promise(resolve => {
|
||||
const kObservedPref = "browser.migrate.automigrate.browser";
|
||||
let obs = () => {
|
||||
Services.prefs.removeObserver(kObservedPref, obs);
|
||||
ok(!Services.prefs.prefHasUserValue(kObservedPref),
|
||||
"Pref should have been reset");
|
||||
resolve();
|
||||
};
|
||||
Services.prefs.addObserver(kObservedPref, obs, false);
|
||||
});
|
||||
|
||||
// Click "Don't keep" button:
|
||||
let notificationToActivate = getNotification(secondTab.linkedBrowser);
|
||||
notificationToActivate.querySelector("button:not(.notification-button-default)").click();
|
||||
info("Waiting for notification to be removed in first (background) tab");
|
||||
yield firstTabNotificationRemovedPromise;
|
||||
info("Waiting for bookmark to be removed");
|
||||
yield bookmarkRemovedPromise;
|
||||
info("Waiting for prefs to be reset");
|
||||
yield prefResetPromise;
|
||||
|
||||
info("Removing spare tabs");
|
||||
yield BrowserTestUtils.removeTab(firstTab);
|
||||
yield BrowserTestUtils.removeTab(secondTab);
|
||||
});
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
let scope = {};
|
||||
Cu.import("resource:///modules/AutoMigrate.jsm", scope);
|
||||
let oldCanUndo = scope.AutoMigrate.canUndo;
|
||||
registerCleanupFunction(function() {
|
||||
scope.AutoMigrate.canUndo = oldCanUndo;
|
||||
});
|
||||
|
||||
const kExpectedNotificationId = "automigration-undo";
|
||||
|
||||
add_task(function* autoMigrationUndoNotificationShows() {
|
||||
let getNotification = browser =>
|
||||
gBrowser.getNotificationBox(browser).getNotificationWithValue(kExpectedNotificationId);
|
||||
let localizedVersionOf = str => {
|
||||
if (str == "logins") {
|
||||
return "passwords";
|
||||
}
|
||||
if (str == "visits") {
|
||||
return "history";
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
scope.AutoMigrate.canUndo = () => true;
|
||||
let url = "about:newtab";
|
||||
Services.prefs.setCharPref("browser.migrate.automigrate.browser", "someunknownbrowser");
|
||||
const kSubsets = [
|
||||
["bookmarks", "logins", "visits"],
|
||||
["bookmarks", "logins"],
|
||||
["bookmarks", "visits"],
|
||||
["logins", "visits"],
|
||||
["bookmarks"],
|
||||
["logins"],
|
||||
["visits"],
|
||||
];
|
||||
const kAllItems = ["bookmarks", "logins", "visits"];
|
||||
for (let subset of kSubsets) {
|
||||
let state = new Map(subset.map(item => [item, [{}]]));
|
||||
scope.AutoMigrate._setImportedItemPrefFromState(state);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
if (!getNotification(browser)) {
|
||||
info(`Notification for ${url} not immediately present, waiting for it.`);
|
||||
yield BrowserTestUtils.waitForNotificationBar(gBrowser, browser, kExpectedNotificationId);
|
||||
}
|
||||
|
||||
ok(true, `Got notification for ${url}`);
|
||||
let notification = getNotification(browser);
|
||||
let notificationText = document.getAnonymousElementByAttribute(notification, "class", "messageText");
|
||||
notificationText = notificationText.textContent;
|
||||
for (let potentiallyImported of kAllItems) {
|
||||
let localizedImportItem = localizedVersionOf(potentiallyImported);
|
||||
if (subset.includes(potentiallyImported)) {
|
||||
ok(notificationText.includes(localizedImportItem),
|
||||
"Expected notification to contain " + localizedImportItem);
|
||||
} else {
|
||||
ok(!notificationText.includes(localizedImportItem),
|
||||
"Expected notification not to contain " + localizedImportItem);
|
||||
}
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[DEFAULT]
|
||||
run-if = buildapp == 'browser'
|
||||
|
||||
[test_refresh_firefox.py]
|
||||
|
||||
|
|
@ -1,416 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
|
||||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
|
||||
class TestFirefoxRefresh(MarionetteTestCase):
|
||||
_username = "marionette-test-login"
|
||||
_password = "marionette-test-password"
|
||||
_bookmarkURL = "about:mozilla"
|
||||
_bookmarkText = "Some bookmark from Marionette"
|
||||
|
||||
_cookieHost = "firefox-refresh.marionette-test.mozilla.org"
|
||||
_cookiePath = "some/cookie/path"
|
||||
_cookieName = "somecookie"
|
||||
_cookieValue = "some cookie value"
|
||||
|
||||
_historyURL = "http://firefox-refresh.marionette-test.mozilla.org/"
|
||||
_historyTitle = "Test visit for Firefox Reset"
|
||||
|
||||
_formHistoryFieldName = "some-very-unique-marionette-only-firefox-reset-field"
|
||||
_formHistoryValue = "special-pumpkin-value"
|
||||
|
||||
_expectedURLs = ["about:robots", "about:mozilla"]
|
||||
|
||||
def savePassword(self):
|
||||
self.runCode("""
|
||||
let myLogin = new global.LoginInfo(
|
||||
"test.marionette.mozilla.com",
|
||||
"http://test.marionette.mozilla.com/some/form/",
|
||||
null,
|
||||
arguments[0],
|
||||
arguments[1],
|
||||
"username",
|
||||
"password"
|
||||
);
|
||||
Services.logins.addLogin(myLogin)
|
||||
""", script_args=[self._username, self._password])
|
||||
|
||||
def createBookmark(self):
|
||||
self.marionette.execute_script("""
|
||||
let url = arguments[0];
|
||||
let title = arguments[1];
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder,
|
||||
makeURI(url), 0, title);
|
||||
""", script_args=[self._bookmarkURL, self._bookmarkText])
|
||||
|
||||
def createHistory(self):
|
||||
error = self.runAsyncCode("""
|
||||
// Copied from PlacesTestUtils, which isn't available in Marionette tests.
|
||||
let didReturn;
|
||||
PlacesUtils.asyncHistory.updatePlaces(
|
||||
[{title: arguments[1], uri: makeURI(arguments[0]), visits: [{
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
visitDate: (Date.now() - 5000) * 1000,
|
||||
referrerURI: makeURI("about:mozilla"),
|
||||
}]
|
||||
}],
|
||||
{
|
||||
handleError(resultCode, place) {
|
||||
didReturn = true;
|
||||
marionetteScriptFinished("Unexpected error in adding visit: " + resultCode);
|
||||
},
|
||||
handleResult() {},
|
||||
handleCompletion() {
|
||||
if (!didReturn) {
|
||||
marionetteScriptFinished(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
""", script_args=[self._historyURL, self._historyTitle])
|
||||
if error:
|
||||
print error
|
||||
|
||||
def createFormHistory(self):
|
||||
error = self.runAsyncCode("""
|
||||
let updateDefinition = {
|
||||
op: "add",
|
||||
fieldname: arguments[0],
|
||||
value: arguments[1],
|
||||
firstUsed: (Date.now() - 5000) * 1000,
|
||||
};
|
||||
let finished = false;
|
||||
global.FormHistory.update(updateDefinition, {
|
||||
handleError(error) {
|
||||
finished = true;
|
||||
marionetteScriptFinished(error);
|
||||
},
|
||||
handleCompletion() {
|
||||
if (!finished) {
|
||||
marionetteScriptFinished(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
""", script_args=[self._formHistoryFieldName, self._formHistoryValue])
|
||||
if error:
|
||||
print error
|
||||
|
||||
def createCookie(self):
|
||||
self.runCode("""
|
||||
// Expire in 15 minutes:
|
||||
let expireTime = Math.floor(Date.now() / 1000) + 15 * 60;
|
||||
Services.cookies.add(arguments[0], arguments[1], arguments[2], arguments[3],
|
||||
true, false, false, expireTime);
|
||||
""", script_args=[self._cookieHost, self._cookiePath, self._cookieName, self._cookieValue])
|
||||
|
||||
def createSession(self):
|
||||
self.runAsyncCode("""
|
||||
const COMPLETE_STATE = Ci.nsIWebProgressListener.STATE_STOP +
|
||||
Ci.nsIWebProgressListener.STATE_IS_NETWORK;
|
||||
let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
|
||||
let expectedURLs = Array.from(arguments[0])
|
||||
gBrowser.addTabsProgressListener({
|
||||
onStateChange(browser, webprogress, request, flags, status) {
|
||||
try {
|
||||
request && request.QueryInterface(Ci.nsIChannel);
|
||||
} catch (ex) {}
|
||||
let uriLoaded = request.originalURI && request.originalURI.spec;
|
||||
if ((flags & COMPLETE_STATE == COMPLETE_STATE) && uriLoaded &&
|
||||
expectedURLs.includes(uriLoaded)) {
|
||||
TabStateFlusher.flush(browser).then(function() {
|
||||
expectedURLs.splice(expectedURLs.indexOf(uriLoaded), 1);
|
||||
if (!expectedURLs.length) {
|
||||
gBrowser.removeTabsProgressListener(this);
|
||||
marionetteScriptFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
for (let url of expectedURLs) {
|
||||
gBrowser.addTab(url);
|
||||
}
|
||||
""", script_args=[self._expectedURLs])
|
||||
|
||||
def checkPassword(self):
|
||||
loginInfo = self.marionette.execute_script("""
|
||||
let ary = Services.logins.findLogins({},
|
||||
"test.marionette.mozilla.com",
|
||||
"http://test.marionette.mozilla.com/some/form/",
|
||||
null, {});
|
||||
return ary.length ? ary : {username: "null", password: "null"};
|
||||
""")
|
||||
self.assertEqual(len(loginInfo), 1)
|
||||
self.assertEqual(loginInfo[0]['username'], self._username)
|
||||
self.assertEqual(loginInfo[0]['password'], self._password)
|
||||
|
||||
loginCount = self.marionette.execute_script("""
|
||||
return Services.logins.getAllLogins().length;
|
||||
""")
|
||||
self.assertEqual(loginCount, 1, "No other logins are present")
|
||||
|
||||
def checkBookmark(self):
|
||||
titleInBookmarks = self.marionette.execute_script("""
|
||||
let url = arguments[0];
|
||||
let bookmarkIds = PlacesUtils.bookmarks.getBookmarkIdsForURI(makeURI(url), {}, {});
|
||||
return bookmarkIds.length == 1 ? PlacesUtils.bookmarks.getItemTitle(bookmarkIds[0]) : "";
|
||||
""", script_args=[self._bookmarkURL])
|
||||
self.assertEqual(titleInBookmarks, self._bookmarkText)
|
||||
|
||||
def checkHistory(self):
|
||||
historyResults = self.runAsyncCode("""
|
||||
let placeInfos = [];
|
||||
PlacesUtils.asyncHistory.getPlacesInfo(makeURI(arguments[0]), {
|
||||
handleError(resultCode, place) {
|
||||
placeInfos = null;
|
||||
marionetteScriptFinished("Unexpected error in fetching visit: " + resultCode);
|
||||
},
|
||||
handleResult(placeInfo) {
|
||||
placeInfos.push(placeInfo);
|
||||
},
|
||||
handleCompletion() {
|
||||
if (placeInfos) {
|
||||
if (!placeInfos.length) {
|
||||
marionetteScriptFinished("No visits found");
|
||||
} else {
|
||||
marionetteScriptFinished(placeInfos);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
""", script_args=[self._historyURL])
|
||||
if type(historyResults) == str:
|
||||
self.fail(historyResults)
|
||||
return
|
||||
|
||||
historyCount = len(historyResults)
|
||||
self.assertEqual(historyCount, 1, "Should have exactly 1 entry for URI, got %d" % historyCount)
|
||||
if historyCount == 1:
|
||||
self.assertEqual(historyResults[0]['title'], self._historyTitle)
|
||||
|
||||
def checkFormHistory(self):
|
||||
formFieldResults = self.runAsyncCode("""
|
||||
let results = [];
|
||||
global.FormHistory.search(["value"], {fieldname: arguments[0]}, {
|
||||
handleError(error) {
|
||||
results = error;
|
||||
},
|
||||
handleResult(result) {
|
||||
results.push(result);
|
||||
},
|
||||
handleCompletion() {
|
||||
marionetteScriptFinished(results);
|
||||
},
|
||||
});
|
||||
""", script_args=[self._formHistoryFieldName])
|
||||
if type(formFieldResults) == str:
|
||||
self.fail(formFieldResults)
|
||||
return
|
||||
|
||||
formFieldResultCount = len(formFieldResults)
|
||||
self.assertEqual(formFieldResultCount, 1, "Should have exactly 1 entry for this field, got %d" % formFieldResultCount)
|
||||
if formFieldResultCount == 1:
|
||||
self.assertEqual(formFieldResults[0]['value'], self._formHistoryValue)
|
||||
|
||||
formHistoryCount = self.runAsyncCode("""
|
||||
let count;
|
||||
let callbacks = {
|
||||
handleResult: rv => count = rv,
|
||||
handleCompletion() {
|
||||
marionetteScriptFinished(count);
|
||||
},
|
||||
};
|
||||
global.FormHistory.count({}, callbacks);
|
||||
""")
|
||||
self.assertEqual(formHistoryCount, 1, "There should be only 1 entry in the form history")
|
||||
|
||||
def checkCookie(self):
|
||||
cookieInfo = self.runCode("""
|
||||
try {
|
||||
let cookieEnum = Services.cookies.getCookiesFromHost(arguments[0]);
|
||||
let cookie = null;
|
||||
while (cookieEnum.hasMoreElements()) {
|
||||
let hostCookie = cookieEnum.getNext();
|
||||
hostCookie.QueryInterface(Ci.nsICookie2);
|
||||
// getCookiesFromHost returns any cookie from the BASE host.
|
||||
if (hostCookie.rawHost != arguments[0])
|
||||
continue;
|
||||
if (cookie != null) {
|
||||
return "more than 1 cookie! That shouldn't happen!";
|
||||
}
|
||||
cookie = hostCookie;
|
||||
}
|
||||
return {path: cookie.path, name: cookie.name, value: cookie.value};
|
||||
} catch (ex) {
|
||||
return "got exception trying to fetch cookie: " + ex;
|
||||
}
|
||||
""", script_args=[self._cookieHost])
|
||||
if not isinstance(cookieInfo, dict):
|
||||
self.fail(cookieInfo)
|
||||
return
|
||||
self.assertEqual(cookieInfo['path'], self._cookiePath)
|
||||
self.assertEqual(cookieInfo['value'], self._cookieValue)
|
||||
self.assertEqual(cookieInfo['name'], self._cookieName)
|
||||
|
||||
def checkSession(self):
|
||||
tabURIs = self.runCode("""
|
||||
return [... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec)
|
||||
""")
|
||||
self.assertSequenceEqual(tabURIs, ["about:welcomeback"])
|
||||
|
||||
tabURIs = self.runAsyncCode("""
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let fs = function() {
|
||||
content.document.getElementById("errorTryAgain").click();
|
||||
};
|
||||
let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
|
||||
window.addEventListener("SSWindowStateReady", function testSSPostReset() {
|
||||
window.removeEventListener("SSWindowStateReady", testSSPostReset, false);
|
||||
Promise.all(gBrowser.browsers.map(b => TabStateFlusher.flush(b))).then(function() {
|
||||
marionetteScriptFinished([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
|
||||
});
|
||||
}, false);
|
||||
mm.loadFrameScript("data:application/javascript,(" + fs.toString() + ")()", true);
|
||||
""")
|
||||
self.assertSequenceEqual(tabURIs, ["about:blank"] + self._expectedURLs)
|
||||
pass
|
||||
|
||||
def checkProfile(self, hasMigrated=False):
|
||||
self.checkPassword()
|
||||
self.checkBookmark()
|
||||
self.checkHistory()
|
||||
self.checkFormHistory()
|
||||
self.checkCookie()
|
||||
if hasMigrated:
|
||||
self.checkSession()
|
||||
|
||||
def createProfileData(self):
|
||||
self.savePassword()
|
||||
self.createBookmark()
|
||||
self.createHistory()
|
||||
self.createFormHistory()
|
||||
self.createCookie()
|
||||
self.createSession()
|
||||
|
||||
def setUpScriptData(self):
|
||||
self.marionette.set_context(self.marionette.CONTEXT_CHROME)
|
||||
self.marionette.execute_script("""
|
||||
global.LoginInfo = Components.Constructor("@mozilla.org/login-manager/loginInfo;1", "nsILoginInfo", "init");
|
||||
global.profSvc = Cc["@mozilla.org/toolkit/profile-service;1"].getService(Ci.nsIToolkitProfileService);
|
||||
global.Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
|
||||
global.FormHistory = Cu.import("resource://gre/modules/FormHistory.jsm", {}).FormHistory;
|
||||
""", new_sandbox=False, sandbox='system')
|
||||
|
||||
def runCode(self, script, *args, **kwargs):
|
||||
return self.marionette.execute_script(script, new_sandbox=False, sandbox='system', *args, **kwargs)
|
||||
|
||||
def runAsyncCode(self, script, *args, **kwargs):
|
||||
return self.marionette.execute_async_script(script, new_sandbox=False, sandbox='system', *args, **kwargs)
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.setUpScriptData()
|
||||
|
||||
self.reset_profile_path = None
|
||||
self.desktop_backup_path = None
|
||||
|
||||
self.createProfileData()
|
||||
|
||||
def tearDown(self):
|
||||
# Force yet another restart with a clean profile to disconnect from the
|
||||
# profile and environment changes we've made, to leave a more or less
|
||||
# blank slate for the next person.
|
||||
self.marionette.restart(clean=True, in_app=False)
|
||||
self.setUpScriptData()
|
||||
|
||||
# Super
|
||||
MarionetteTestCase.tearDown(self)
|
||||
|
||||
# Some helpers to deal with removing a load of files
|
||||
import errno, stat
|
||||
def handleRemoveReadonly(func, path, exc):
|
||||
excvalue = exc[1]
|
||||
if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
|
||||
os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
|
||||
func(path)
|
||||
else:
|
||||
raise
|
||||
|
||||
if self.desktop_backup_path:
|
||||
shutil.rmtree(self.desktop_backup_path, ignore_errors=False, onerror=handleRemoveReadonly)
|
||||
|
||||
if self.reset_profile_path:
|
||||
# Remove ourselves from profiles.ini
|
||||
profileLeafName = os.path.basename(os.path.normpath(self.reset_profile_path))
|
||||
self.runCode("""
|
||||
let [salt, name] = arguments[0].split(".");
|
||||
let profile = global.profSvc.getProfileByName(name);
|
||||
profile.remove(false)
|
||||
global.profSvc.flush();
|
||||
""", script_args=[profileLeafName])
|
||||
# And delete all the files.
|
||||
shutil.rmtree(self.reset_profile_path, ignore_errors=False, onerror=handleRemoveReadonly)
|
||||
|
||||
def doReset(self):
|
||||
self.runCode("""
|
||||
// Ensure the current (temporary) profile is in profiles.ini:
|
||||
let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
let profileName = "marionette-test-profile-" + Date.now();
|
||||
let myProfile = global.profSvc.createProfile(profD, profileName);
|
||||
global.profSvc.flush()
|
||||
|
||||
// Now add the reset parameters:
|
||||
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
let allMarionettePrefs = Services.prefs.getChildList("marionette.");
|
||||
let prefObj = {};
|
||||
for (let pref of allMarionettePrefs) {
|
||||
let prefSuffix = pref.substr("marionette.".length);
|
||||
let prefVal = global.Preferences.get(pref);
|
||||
prefObj[prefSuffix] = prefVal;
|
||||
}
|
||||
let marionetteInfo = JSON.stringify(prefObj);
|
||||
env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", marionetteInfo);
|
||||
env.set("MOZ_RESET_PROFILE_RESTART", "1");
|
||||
env.set("XRE_PROFILE_PATH", arguments[0]);
|
||||
env.set("XRE_PROFILE_NAME", profileName);
|
||||
""", script_args=[self.marionette.instance.profile.profile])
|
||||
|
||||
profileLeafName = os.path.basename(os.path.normpath(self.marionette.instance.profile.profile))
|
||||
|
||||
# Now restart the browser to get it reset:
|
||||
self.marionette.restart(clean=False, in_app=True)
|
||||
self.setUpScriptData()
|
||||
|
||||
# Determine the new profile path (we'll need to remove it when we're done)
|
||||
self.reset_profile_path = self.runCode("""
|
||||
let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
return profD.path;
|
||||
""")
|
||||
|
||||
# Determine the backup path
|
||||
self.desktop_backup_path = self.runCode("""
|
||||
let container;
|
||||
try {
|
||||
container = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
} catch (ex) {
|
||||
container = Services.dirsvc.get("Home", Ci.nsIFile);
|
||||
}
|
||||
let bundle = Services.strings.createBundle("chrome://mozapps/locale/profile/profileSelection.properties");
|
||||
let dirName = bundle.formatStringFromName("resetBackupDirectory", [Services.appinfo.name], 1);
|
||||
container.append(dirName);
|
||||
container.append(arguments[0]);
|
||||
return container.path;
|
||||
""", script_args = [profileLeafName])
|
||||
|
||||
self.assertTrue(os.path.isdir(self.reset_profile_path), "Reset profile path should be present")
|
||||
self.assertTrue(os.path.isdir(self.desktop_backup_path), "Backup profile path should be present")
|
||||
|
||||
def testReset(self):
|
||||
self.checkProfile()
|
||||
|
||||
self.doReset()
|
||||
|
||||
# Now check that we're doing OK...
|
||||
self.checkProfile(hasMigrated=True)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = { // eslint-disable-line no-undef
|
||||
"extends": [
|
||||
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
|
||||
]
|
||||
};
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"profile" : {
|
||||
"info_cache" : {
|
||||
"Default" : {
|
||||
"active_time" : 1430950755.65137,
|
||||
"is_using_default_name" : true,
|
||||
"is_ephemeral" : false,
|
||||
"is_omitted_from_profile_list" : false,
|
||||
"user_name" : "",
|
||||
"background_apps" : false,
|
||||
"is_using_default_avatar" : true,
|
||||
"avatar_icon" : "chrome://theme/IDR_PROFILE_AVATAR_0",
|
||||
"name" : "Person 1"
|
||||
}
|
||||
},
|
||||
"profiles_created" : 1,
|
||||
"last_used" : "Default",
|
||||
"last_active_profiles" : [
|
||||
"Default"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,69 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
/* exported gProfD, promiseMigration, registerFakePath */
|
||||
|
||||
var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.importGlobalProperties([ "URL" ]);
|
||||
|
||||
Cu.import("resource:///modules/MigrationUtils.jsm");
|
||||
Cu.import("resource://gre/modules/LoginHelper.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/PromiseUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://testing-common/TestUtils.jsm");
|
||||
Cu.import("resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
// Initialize profile.
|
||||
var gProfD = do_get_profile();
|
||||
|
||||
Cu.import("resource://testing-common/AppInfo.jsm"); /* globals updateAppInfo */
|
||||
updateAppInfo();
|
||||
|
||||
/**
|
||||
* Migrates the requested resource and waits for the migration to be complete.
|
||||
*/
|
||||
function promiseMigration(migrator, resourceType, aProfile = null) {
|
||||
// Ensure resource migration is available.
|
||||
let availableSources = migrator.getMigrateData(aProfile, false);
|
||||
Assert.ok((availableSources & resourceType) > 0, "Resource supported by migrator");
|
||||
|
||||
return new Promise (resolve => {
|
||||
Services.obs.addObserver(function onMigrationEnded() {
|
||||
Services.obs.removeObserver(onMigrationEnded, "Migration:Ended");
|
||||
resolve();
|
||||
}, "Migration:Ended", false);
|
||||
|
||||
migrator.migrate(resourceType, null, aProfile);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a directory service entry with a given nsIFile.
|
||||
*/
|
||||
function registerFakePath(key, file) {
|
||||
// Register our own provider for the Library directory.
|
||||
let provider = {
|
||||
getFile(prop, persistent) {
|
||||
persistent.value = true;
|
||||
if (prop == key) {
|
||||
return file;
|
||||
}
|
||||
throw Cr.NS_ERROR_FAILURE;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([ Ci.nsIDirectoryServiceProvider ])
|
||||
};
|
||||
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
|
||||
.registerProvider(provider);
|
||||
do_register_cleanup(() => {
|
||||
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
|
||||
.unregisterProvider(provider);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
|
||||
add_task(function* () {
|
||||
registerFakePath("ULibDir", do_get_file("Library/"));
|
||||
let migrator = MigrationUtils.getMigrator("chrome");
|
||||
|
||||
Assert.ok(migrator.sourceExists, "Sanity check the source exists");
|
||||
|
||||
const COOKIE = {
|
||||
expiry: 2145934800,
|
||||
host: "unencryptedcookie.invalid",
|
||||
isHttpOnly: false,
|
||||
isSession: false,
|
||||
name: "testcookie",
|
||||
path: "/",
|
||||
value: "testvalue",
|
||||
};
|
||||
|
||||
// Sanity check.
|
||||
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
|
||||
"There are no cookies initially");
|
||||
|
||||
const PROFILE = {
|
||||
id: "Default",
|
||||
name: "Person 1",
|
||||
};
|
||||
|
||||
// Migrate unencrypted cookies.
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES, PROFILE);
|
||||
|
||||
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1,
|
||||
"Migrated the expected number of unencrypted cookies");
|
||||
Assert.equal(Services.cookies.countCookiesFromHost("encryptedcookie.invalid"), 0,
|
||||
"Migrated the expected number of encrypted cookies");
|
||||
|
||||
// Now check the cookie details.
|
||||
let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host, {});
|
||||
Assert.ok(enumerator.hasMoreElements(), "Cookies available");
|
||||
let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
|
||||
|
||||
for (let prop of Object.keys(COOKIE)) {
|
||||
Assert.equal(foundCookie[prop], COOKIE[prop], "Check cookie " + prop);
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
ForgetAboutSite.removeDataFromDomain(COOKIE.host);
|
||||
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
|
||||
"There are no cookies after cleanup");
|
||||
});
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/OSCrypto.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PROFILE = {
|
||||
id: "Default",
|
||||
name: "Person 1",
|
||||
};
|
||||
|
||||
const TEST_LOGINS = [
|
||||
{
|
||||
id: 1, // id of the row in the chrome login db
|
||||
username: "username",
|
||||
password: "password",
|
||||
hostname: "https://c9.io",
|
||||
formSubmitURL: "https://c9.io",
|
||||
httpRealm: null,
|
||||
usernameField: "inputEmail",
|
||||
passwordField: "inputPassword",
|
||||
timeCreated: 1437418416037,
|
||||
timePasswordChanged: 1437418416037,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
username: "username@gmail.com",
|
||||
password: "password2",
|
||||
hostname: "https://accounts.google.com",
|
||||
formSubmitURL: "https://accounts.google.com",
|
||||
httpRealm: null,
|
||||
usernameField: "Email",
|
||||
passwordField: "Passwd",
|
||||
timeCreated: 1437418446598,
|
||||
timePasswordChanged: 1437418446598,
|
||||
timesUsed: 6,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: "username",
|
||||
password: "password3",
|
||||
hostname: "https://www.facebook.com",
|
||||
formSubmitURL: "https://www.facebook.com",
|
||||
httpRealm: null,
|
||||
usernameField: "email",
|
||||
passwordField: "pass",
|
||||
timeCreated: 1437418478851,
|
||||
timePasswordChanged: 1437418478851,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
username: "user",
|
||||
password: "password",
|
||||
hostname: "http://httpbin.org",
|
||||
formSubmitURL: null,
|
||||
httpRealm: "me@kennethreitz.com", // Digest auth.
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1437787462368,
|
||||
timePasswordChanged: 1437787462368,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
username: "buser",
|
||||
password: "bpassword",
|
||||
hostname: "http://httpbin.org",
|
||||
formSubmitURL: null,
|
||||
httpRealm: "Fake Realm", // Basic auth.
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1437787539233,
|
||||
timePasswordChanged: 1437787539233,
|
||||
timesUsed: 1,
|
||||
},
|
||||
];
|
||||
|
||||
var crypto = new OSCrypto();
|
||||
var dbConn;
|
||||
|
||||
function promiseSetPassword(login) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stmt = dbConn.createAsyncStatement(`
|
||||
UPDATE logins
|
||||
SET password_value = :password_value
|
||||
WHERE rowid = :rowid
|
||||
`);
|
||||
let passwordValue = crypto.stringToArray(crypto.encryptData(login.password));
|
||||
stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
|
||||
stmt.params.rowid = login.id;
|
||||
|
||||
stmt.executeAsync({
|
||||
handleError(aError) {
|
||||
reject("Error with the query: " + aError.message);
|
||||
},
|
||||
|
||||
handleCompletion(aReason) {
|
||||
if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
resolve();
|
||||
} else {
|
||||
reject("Query has failed: " + aReason);
|
||||
}
|
||||
},
|
||||
});
|
||||
stmt.finalize();
|
||||
});
|
||||
}
|
||||
|
||||
function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
|
||||
passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
Assert.equal(passwordManagerLogin.username, chromeLogin.username,
|
||||
"The two logins ID " + id + " have the same username");
|
||||
Assert.equal(passwordManagerLogin.password, chromeLogin.password,
|
||||
"The two logins ID " + id + " have the same password");
|
||||
Assert.equal(passwordManagerLogin.hostname, chromeLogin.hostname,
|
||||
"The two logins ID " + id + " have the same hostname");
|
||||
Assert.equal(passwordManagerLogin.formSubmitURL, chromeLogin.formSubmitURL,
|
||||
"The two logins ID " + id + " have the same formSubmitURL");
|
||||
Assert.equal(passwordManagerLogin.httpRealm, chromeLogin.httpRealm,
|
||||
"The two logins ID " + id + " have the same httpRealm");
|
||||
Assert.equal(passwordManagerLogin.usernameField, chromeLogin.usernameField,
|
||||
"The two logins ID " + id + " have the same usernameElement");
|
||||
Assert.equal(passwordManagerLogin.passwordField, chromeLogin.passwordField,
|
||||
"The two logins ID " + id + " have the same passwordElement");
|
||||
Assert.equal(passwordManagerLogin.timeCreated, chromeLogin.timeCreated,
|
||||
"The two logins ID " + id + " have the same timeCreated");
|
||||
Assert.equal(passwordManagerLogin.timePasswordChanged, chromeLogin.timePasswordChanged,
|
||||
"The two logins ID " + id + " have the same timePasswordChanged");
|
||||
Assert.equal(passwordManagerLogin.timesUsed, chromeLogin.timesUsed,
|
||||
"The two logins ID " + id + " have the same timesUsed");
|
||||
}
|
||||
|
||||
function generateDifferentLogin(login) {
|
||||
let newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
|
||||
|
||||
newLogin.init(login.hostname, login.formSubmitURL, null,
|
||||
login.username, login.password + 1, login.usernameField + 1,
|
||||
login.passwordField + 1);
|
||||
newLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
newLogin.timeCreated = login.timeCreated + 1;
|
||||
newLogin.timePasswordChanged = login.timePasswordChanged + 1;
|
||||
newLogin.timesUsed = login.timesUsed + 1;
|
||||
return newLogin;
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
let loginDataFile = do_get_file("AppData/Local/Google/Chrome/User Data/Default/Login Data");
|
||||
dbConn = Services.storage.openUnsharedDatabase(loginDataFile);
|
||||
registerFakePath("LocalAppData", do_get_file("AppData/Local/"));
|
||||
|
||||
do_register_cleanup(() => {
|
||||
Services.logins.removeAllLogins();
|
||||
dbConn.asyncClose();
|
||||
crypto.finalize();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_importIntoEmptyDB() {
|
||||
for (let login of TEST_LOGINS) {
|
||||
yield promiseSetPassword(login);
|
||||
}
|
||||
|
||||
let migrator = MigrationUtils.getMigrator("chrome");
|
||||
Assert.ok(migrator.sourceExists, "Sanity check the source exists");
|
||||
|
||||
let logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0, "There are no logins initially");
|
||||
|
||||
// Migrate the logins.
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
|
||||
|
||||
logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, TEST_LOGINS.length, "Check login count after importing the data");
|
||||
Assert.equal(logins.length, MigrationUtils._importQuantities.logins,
|
||||
"Check telemetry matches the actual import.");
|
||||
|
||||
for (let i = 0; i < TEST_LOGINS.length; i++) {
|
||||
checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Test that existing logins for the same primary key don't get overwritten
|
||||
add_task(function* test_importExistingLogins() {
|
||||
let migrator = MigrationUtils.getMigrator("chrome");
|
||||
Assert.ok(migrator.sourceExists, "Sanity check the source exists");
|
||||
|
||||
Services.logins.removeAllLogins();
|
||||
let logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0, "There are no logins after removing all of them");
|
||||
|
||||
let newLogins = [];
|
||||
|
||||
// Create 3 new logins that are different but where the key properties are still the same.
|
||||
for (let i = 0; i < 3; i++) {
|
||||
newLogins.push(generateDifferentLogin(TEST_LOGINS[i]));
|
||||
Services.logins.addLogin(newLogins[i]);
|
||||
}
|
||||
|
||||
logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, newLogins.length, "Check login count after the insertion");
|
||||
|
||||
for (let i = 0; i < newLogins.length; i++) {
|
||||
checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
|
||||
}
|
||||
// Migrate the logins.
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
|
||||
|
||||
logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, TEST_LOGINS.length,
|
||||
"Check there are still the same number of logins after re-importing the data");
|
||||
Assert.equal(logins.length, MigrationUtils._importQuantities.logins,
|
||||
"Check telemetry matches the actual import.");
|
||||
|
||||
for (let i = 0; i < newLogins.length; i++) {
|
||||
checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const EDGE_AVAILABLE_MIGRATIONS =
|
||||
MigrationUtils.resourceTypes.COOKIES |
|
||||
MigrationUtils.resourceTypes.BOOKMARKS |
|
||||
MigrationUtils.resourceTypes.HISTORY |
|
||||
MigrationUtils.resourceTypes.PASSWORDS;
|
||||
|
||||
add_task(function* () {
|
||||
let migrator = MigrationUtils.getMigrator("edge");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Assert.equal(!!(migrator && migrator.sourceExists), AppConstants.isPlatformAndVersionAtLeast("win", "10"),
|
||||
"Edge should be available for migration if and only if we're on Win 10+");
|
||||
if (migrator) {
|
||||
let migratableData = migrator.getMigrateData(null, false);
|
||||
Assert.equal(migratableData, EDGE_AVAILABLE_MIGRATIONS,
|
||||
"All the data types we expect should be available");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,471 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let eseBackStage = Cu.import("resource:///modules/ESEDBReader.jsm");
|
||||
let ESE = eseBackStage.ESE;
|
||||
let KERNEL = eseBackStage.KERNEL;
|
||||
let gLibs = eseBackStage.gLibs;
|
||||
let COLUMN_TYPES = eseBackStage.COLUMN_TYPES;
|
||||
let declareESEFunction = eseBackStage.declareESEFunction;
|
||||
let loadLibraries = eseBackStage.loadLibraries;
|
||||
|
||||
let gESEInstanceCounter = 1;
|
||||
|
||||
ESE.JET_COLUMNCREATE_W = new ctypes.StructType("JET_COLUMNCREATE_W", [
|
||||
{"cbStruct": ctypes.unsigned_long},
|
||||
{"szColumnName": ESE.JET_PCWSTR},
|
||||
{"coltyp": ESE.JET_COLTYP },
|
||||
{"cbMax": ctypes.unsigned_long },
|
||||
{"grbit": ESE.JET_GRBIT },
|
||||
{"pvDefault": ctypes.voidptr_t},
|
||||
{"cbDefault": ctypes.unsigned_long },
|
||||
{"cp": ctypes.unsigned_long },
|
||||
{"columnid": ESE.JET_COLUMNID},
|
||||
{"err": ESE.JET_ERR},
|
||||
]);
|
||||
|
||||
function createColumnCreationWrapper({name, type, cbMax}) {
|
||||
// We use a wrapper object because we need to be sure the JS engine won't GC
|
||||
// data that we're "only" pointing to.
|
||||
let wrapper = {};
|
||||
wrapper.column = new ESE.JET_COLUMNCREATE_W();
|
||||
wrapper.column.cbStruct = ESE.JET_COLUMNCREATE_W.size;
|
||||
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||
wrapper.name = new wchar_tArray(name.length + 1);
|
||||
wrapper.name.value = String(name);
|
||||
wrapper.column.szColumnName = wrapper.name;
|
||||
wrapper.column.coltyp = type;
|
||||
let fallback = 0;
|
||||
switch (type) {
|
||||
case COLUMN_TYPES.JET_coltypText:
|
||||
fallback = 255;
|
||||
// Intentional fall-through
|
||||
case COLUMN_TYPES.JET_coltypLongText:
|
||||
wrapper.column.cbMax = cbMax || fallback || 64 * 1024;
|
||||
break;
|
||||
case COLUMN_TYPES.JET_coltypGUID:
|
||||
wrapper.column.cbMax = 16;
|
||||
break;
|
||||
case COLUMN_TYPES.JET_coltypBit:
|
||||
wrapper.column.cbMax = 1;
|
||||
break;
|
||||
case COLUMN_TYPES.JET_coltypLongLong:
|
||||
wrapper.column.cbMax = 8;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown column type!");
|
||||
}
|
||||
|
||||
wrapper.column.columnid = new ESE.JET_COLUMNID();
|
||||
wrapper.column.grbit = 0;
|
||||
wrapper.column.pvDefault = null;
|
||||
wrapper.column.cbDefault = 0;
|
||||
wrapper.column.cp = 0;
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// "forward declarations" of indexcreate and setinfo structs, which we don't use.
|
||||
ESE.JET_INDEXCREATE = new ctypes.StructType("JET_INDEXCREATE");
|
||||
ESE.JET_SETINFO = new ctypes.StructType("JET_SETINFO");
|
||||
|
||||
ESE.JET_TABLECREATE_W = new ctypes.StructType("JET_TABLECREATE_W", [
|
||||
{"cbStruct": ctypes.unsigned_long},
|
||||
{"szTableName": ESE.JET_PCWSTR},
|
||||
{"szTemplateTableName": ESE.JET_PCWSTR},
|
||||
{"ulPages": ctypes.unsigned_long},
|
||||
{"ulDensity": ctypes.unsigned_long},
|
||||
{"rgcolumncreate": ESE.JET_COLUMNCREATE_W.ptr},
|
||||
{"cColumns": ctypes.unsigned_long},
|
||||
{"rgindexcreate": ESE.JET_INDEXCREATE.ptr},
|
||||
{"cIndexes": ctypes.unsigned_long},
|
||||
{"grbit": ESE.JET_GRBIT},
|
||||
{"tableid": ESE.JET_TABLEID},
|
||||
{"cCreated": ctypes.unsigned_long},
|
||||
]);
|
||||
|
||||
function createTableCreationWrapper(tableName, columns) {
|
||||
let wrapper = {};
|
||||
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||
wrapper.name = new wchar_tArray(tableName.length + 1);
|
||||
wrapper.name.value = String(tableName);
|
||||
wrapper.table = new ESE.JET_TABLECREATE_W();
|
||||
wrapper.table.cbStruct = ESE.JET_TABLECREATE_W.size;
|
||||
wrapper.table.szTableName = wrapper.name;
|
||||
wrapper.table.szTemplateTableName = null;
|
||||
wrapper.table.ulPages = 1;
|
||||
wrapper.table.ulDensity = 0;
|
||||
let columnArrayType = ESE.JET_COLUMNCREATE_W.array(columns.length);
|
||||
wrapper.columnAry = new columnArrayType();
|
||||
wrapper.table.rgcolumncreate = wrapper.columnAry.addressOfElement(0);
|
||||
wrapper.table.cColumns = columns.length;
|
||||
wrapper.columns = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
let column = columns[i];
|
||||
let columnWrapper = createColumnCreationWrapper(column);
|
||||
wrapper.columnAry.addressOfElement(i).contents = columnWrapper.column;
|
||||
wrapper.columns.push(columnWrapper);
|
||||
}
|
||||
wrapper.table.rgindexcreate = null;
|
||||
wrapper.table.cIndexes = 0;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function convertValueForWriting(value, valueType) {
|
||||
let buffer;
|
||||
let valueOfValueType = ctypes.UInt64.lo(valueType);
|
||||
switch (valueOfValueType) {
|
||||
case COLUMN_TYPES.JET_coltypLongLong:
|
||||
if (value instanceof Date) {
|
||||
buffer = new KERNEL.FILETIME();
|
||||
let sysTime = new KERNEL.SYSTEMTIME();
|
||||
sysTime.wYear = value.getUTCFullYear();
|
||||
sysTime.wMonth = value.getUTCMonth() + 1;
|
||||
sysTime.wDay = value.getUTCDate();
|
||||
sysTime.wHour = value.getUTCHours();
|
||||
sysTime.wMinute = value.getUTCMinutes();
|
||||
sysTime.wSecond = value.getUTCSeconds();
|
||||
sysTime.wMilliseconds = value.getUTCMilliseconds();
|
||||
let rv = KERNEL.SystemTimeToFileTime(sysTime.address(), buffer.address());
|
||||
if (!rv) {
|
||||
throw new Error("Failed to get FileTime.");
|
||||
}
|
||||
return [buffer, KERNEL.FILETIME.size];
|
||||
}
|
||||
throw new Error("Unrecognized value for longlong column");
|
||||
case COLUMN_TYPES.JET_coltypLongText:
|
||||
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||
buffer = new wchar_tArray(value.length + 1);
|
||||
buffer.value = String(value);
|
||||
return [buffer, buffer.length * 2];
|
||||
case COLUMN_TYPES.JET_coltypBit:
|
||||
buffer = new ctypes.uint8_t();
|
||||
// Bizarre boolean values, but whatever:
|
||||
buffer.value = value ? 255 : 0;
|
||||
return [buffer, 1];
|
||||
case COLUMN_TYPES.JET_coltypGUID:
|
||||
let byteArray = ctypes.ArrayType(ctypes.uint8_t);
|
||||
buffer = new byteArray(16);
|
||||
let j = 0;
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (!(/[0-9a-f]/i).test(value[i])) {
|
||||
continue;
|
||||
}
|
||||
let byteAsHex = value.substr(i, 2);
|
||||
buffer[j++] = parseInt(byteAsHex, 16);
|
||||
i++;
|
||||
}
|
||||
return [buffer, 16];
|
||||
}
|
||||
|
||||
throw new Error("Unknown type " + valueType);
|
||||
}
|
||||
|
||||
let initializedESE = false;
|
||||
|
||||
let eseDBWritingHelpers = {
|
||||
setupDB(dbFile, tableName, columns, rows) {
|
||||
if (!initializedESE) {
|
||||
initializedESE = true;
|
||||
loadLibraries();
|
||||
|
||||
KERNEL.SystemTimeToFileTime = gLibs.kernel.declare("SystemTimeToFileTime",
|
||||
ctypes.default_abi, ctypes.bool, KERNEL.SYSTEMTIME.ptr, KERNEL.FILETIME.ptr);
|
||||
|
||||
declareESEFunction("CreateDatabaseW", ESE.JET_SESID, ESE.JET_PCWSTR,
|
||||
ESE.JET_PCWSTR, ESE.JET_DBID.ptr, ESE.JET_GRBIT);
|
||||
declareESEFunction("CreateTableColumnIndexW", ESE.JET_SESID, ESE.JET_DBID,
|
||||
ESE.JET_TABLECREATE_W.ptr);
|
||||
declareESEFunction("BeginTransaction", ESE.JET_SESID);
|
||||
declareESEFunction("CommitTransaction", ESE.JET_SESID, ESE.JET_GRBIT);
|
||||
declareESEFunction("PrepareUpdate", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||
ctypes.unsigned_long);
|
||||
declareESEFunction("Update", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||
ctypes.voidptr_t, ctypes.unsigned_long,
|
||||
ctypes.unsigned_long.ptr);
|
||||
declareESEFunction("SetColumn", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||
ESE.JET_COLUMNID, ctypes.voidptr_t,
|
||||
ctypes.unsigned_long, ESE.JET_GRBIT, ESE.JET_SETINFO.ptr);
|
||||
ESE.SetSystemParameterW(null, 0, 64 /* JET_paramDatabasePageSize*/,
|
||||
8192, null);
|
||||
}
|
||||
|
||||
let rootPath = dbFile.parent.path + "\\";
|
||||
let logPath = rootPath + "LogFiles\\";
|
||||
|
||||
try {
|
||||
this._instanceId = new ESE.JET_INSTANCE();
|
||||
ESE.CreateInstanceW(this._instanceId.address(),
|
||||
"firefox-dbwriter-" + (gESEInstanceCounter++));
|
||||
this._instanceCreated = true;
|
||||
|
||||
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||
0 /* JET_paramSystemPath*/, 0, rootPath);
|
||||
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||
1 /* JET_paramTempPath */, 0, rootPath);
|
||||
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||
2 /* JET_paramLogFilePath*/, 0, logPath);
|
||||
// Shouldn't try to call JetTerm if the following call fails.
|
||||
this._instanceCreated = false;
|
||||
ESE.Init(this._instanceId.address());
|
||||
this._instanceCreated = true;
|
||||
this._sessionId = new ESE.JET_SESID();
|
||||
ESE.BeginSessionW(this._instanceId, this._sessionId.address(), null,
|
||||
null);
|
||||
this._sessionCreated = true;
|
||||
|
||||
this._dbId = new ESE.JET_DBID();
|
||||
this._dbPath = rootPath + "spartan.edb";
|
||||
ESE.CreateDatabaseW(this._sessionId, this._dbPath, null,
|
||||
this._dbId.address(), 0);
|
||||
this._opened = this._attached = true;
|
||||
|
||||
let tableCreationWrapper = createTableCreationWrapper(tableName, columns);
|
||||
ESE.CreateTableColumnIndexW(this._sessionId, this._dbId,
|
||||
tableCreationWrapper.table.address());
|
||||
this._tableId = tableCreationWrapper.table.tableid;
|
||||
|
||||
let columnIdMap = new Map();
|
||||
if (rows.length) {
|
||||
// Iterate over the struct we passed into ESENT because they have the
|
||||
// created column ids.
|
||||
let columnCount = ctypes.UInt64.lo(tableCreationWrapper.table.cColumns);
|
||||
let columnsPassed = tableCreationWrapper.table.rgcolumncreate;
|
||||
for (let i = 0; i < columnCount; i++) {
|
||||
let column = columnsPassed.contents;
|
||||
columnIdMap.set(column.szColumnName.readString(), column);
|
||||
columnsPassed = columnsPassed.increment();
|
||||
}
|
||||
ESE.ManualMove(this._sessionId, this._tableId,
|
||||
-2147483648 /* JET_MoveFirst */, 0);
|
||||
ESE.BeginTransaction(this._sessionId);
|
||||
for (let row of rows) {
|
||||
ESE.PrepareUpdate(this._sessionId, this._tableId, 0 /* JET_prepInsert */);
|
||||
for (let columnName in row) {
|
||||
let col = columnIdMap.get(columnName);
|
||||
let colId = col.columnid;
|
||||
let [val, valSize] = convertValueForWriting(row[columnName], col.coltyp);
|
||||
/* JET_bitSetOverwriteLV */
|
||||
ESE.SetColumn(this._sessionId, this._tableId, colId, val.address(), valSize, 4, null);
|
||||
}
|
||||
let actualBookmarkSize = new ctypes.unsigned_long();
|
||||
ESE.Update(this._sessionId, this._tableId, null, 0, actualBookmarkSize.address());
|
||||
}
|
||||
ESE.CommitTransaction(this._sessionId, 0 /* JET_bitWaitLastLevel0Commit */);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
this._close();
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_close() {
|
||||
if (this._tableId) {
|
||||
ESE.FailSafeCloseTable(this._sessionId, this._tableId);
|
||||
delete this._tableId;
|
||||
}
|
||||
if (this._opened) {
|
||||
ESE.FailSafeCloseDatabase(this._sessionId, this._dbId, 0);
|
||||
this._opened = false;
|
||||
}
|
||||
if (this._attached) {
|
||||
ESE.FailSafeDetachDatabaseW(this._sessionId, this._dbPath);
|
||||
this._attached = false;
|
||||
}
|
||||
if (this._sessionCreated) {
|
||||
ESE.FailSafeEndSession(this._sessionId, 0);
|
||||
this._sessionCreated = false;
|
||||
}
|
||||
if (this._instanceCreated) {
|
||||
ESE.FailSafeTerm(this._instanceId);
|
||||
this._instanceCreated = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
add_task(function*() {
|
||||
let tempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
tempFile.append("fx-xpcshell-edge-db");
|
||||
tempFile.createUnique(tempFile.DIRECTORY_TYPE, 0o600);
|
||||
|
||||
let db = tempFile.clone();
|
||||
db.append("spartan.edb");
|
||||
|
||||
let logs = tempFile.clone();
|
||||
logs.append("LogFiles");
|
||||
logs.create(tempFile.DIRECTORY_TYPE, 0o600);
|
||||
|
||||
let creationDate = new Date(Date.now() - 5000);
|
||||
const kEdgeMenuParent = "62d07e2b-5f0d-4e41-8426-5f5ec9717beb";
|
||||
let itemsInDB = [
|
||||
{
|
||||
URL: "http://www.mozilla.org/",
|
||||
Title: "Mozilla",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 100),
|
||||
ItemId: "1c00c10a-15f6-4618-92dd-22575102a4da",
|
||||
ParentId: kEdgeMenuParent,
|
||||
IsFolder: false,
|
||||
IsDeleted: false,
|
||||
},
|
||||
{
|
||||
Title: "Folder",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 200),
|
||||
ItemId: "564b21f2-05d6-4f7d-8499-304d00ccc3aa",
|
||||
ParentId: kEdgeMenuParent,
|
||||
IsFolder: true,
|
||||
IsDeleted: false,
|
||||
},
|
||||
{
|
||||
Title: "Item in folder",
|
||||
URL: "http://www.iteminfolder.org/",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 300),
|
||||
ItemId: "c295ddaf-04a1-424a-866c-0ebde011e7c8",
|
||||
ParentId: "564b21f2-05d6-4f7d-8499-304d00ccc3aa",
|
||||
IsFolder: false,
|
||||
IsDeleted: false,
|
||||
},
|
||||
{
|
||||
Title: "Deleted folder",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 400),
|
||||
ItemId: "a547573c-4d4d-4406-a736-5b5462d93bca",
|
||||
ParentId: kEdgeMenuParent,
|
||||
IsFolder: true,
|
||||
IsDeleted: true,
|
||||
},
|
||||
{
|
||||
Title: "Deleted item",
|
||||
URL: "http://www.deleteditem.org/",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 500),
|
||||
ItemId: "37a574bb-b44b-4bbc-a414-908615536435",
|
||||
ParentId: kEdgeMenuParent,
|
||||
IsFolder: false,
|
||||
IsDeleted: true,
|
||||
},
|
||||
{
|
||||
Title: "Item in deleted folder (should be in root)",
|
||||
URL: "http://www.itemindeletedfolder.org/",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 600),
|
||||
ItemId: "74dd1cc3-4c5d-471f-bccc-7bc7c72fa621",
|
||||
ParentId: "a547573c-4d4d-4406-a736-5b5462d93bca",
|
||||
IsFolder: false,
|
||||
IsDeleted: false,
|
||||
},
|
||||
{
|
||||
Title: "_Favorites_Bar_",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 700),
|
||||
ItemId: "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf",
|
||||
ParentId: kEdgeMenuParent,
|
||||
IsFolder: true,
|
||||
IsDeleted: false,
|
||||
},
|
||||
{
|
||||
Title: "Item in favorites bar",
|
||||
URL: "http://www.iteminfavoritesbar.org/",
|
||||
DateUpdated: new Date(creationDate.valueOf() + 800),
|
||||
ItemId: "9f2b1ff8-b651-46cf-8f41-16da8bcb6791",
|
||||
ParentId: "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf",
|
||||
IsFolder: false,
|
||||
IsDeleted: false,
|
||||
},
|
||||
];
|
||||
eseDBWritingHelpers.setupDB(db, "Favorites", [
|
||||
{type: COLUMN_TYPES.JET_coltypLongText, name: "URL", cbMax: 4096},
|
||||
{type: COLUMN_TYPES.JET_coltypLongText, name: "Title", cbMax: 4096},
|
||||
{type: COLUMN_TYPES.JET_coltypLongLong, name: "DateUpdated"},
|
||||
{type: COLUMN_TYPES.JET_coltypGUID, name: "ItemId"},
|
||||
{type: COLUMN_TYPES.JET_coltypBit, name: "IsDeleted"},
|
||||
{type: COLUMN_TYPES.JET_coltypBit, name: "IsFolder"},
|
||||
{type: COLUMN_TYPES.JET_coltypGUID, name: "ParentId"},
|
||||
], itemsInDB);
|
||||
|
||||
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=edge"]
|
||||
.createInstance(Ci.nsIBrowserProfileMigrator);
|
||||
let bookmarksMigrator = migrator.wrappedJSObject.getESEMigratorForTesting(db);
|
||||
Assert.ok(bookmarksMigrator.exists, "Should recognize table we just created");
|
||||
|
||||
let source = MigrationUtils.getLocalizedString("sourceNameEdge");
|
||||
let sourceLabel = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
|
||||
|
||||
let seenBookmarks = [];
|
||||
let bookmarkObserver = {
|
||||
onItemAdded(itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid) {
|
||||
if (title.startsWith("Deleted")) {
|
||||
ok(false, "Should not see deleted items being bookmarked!");
|
||||
}
|
||||
seenBookmarks.push({itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid});
|
||||
},
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onItemRemoved() {},
|
||||
onItemChanged() {},
|
||||
onItemVisited() {},
|
||||
onItemMoved() {},
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(bookmarkObserver, false);
|
||||
|
||||
let migrateResult = yield new Promise(resolve => bookmarksMigrator.migrate(resolve)).catch(ex => {
|
||||
Cu.reportError(ex);
|
||||
Assert.ok(false, "Got an exception trying to migrate data! " + ex);
|
||||
return false;
|
||||
});
|
||||
PlacesUtils.bookmarks.removeObserver(bookmarkObserver);
|
||||
Assert.ok(migrateResult, "Migration should succeed");
|
||||
Assert.equal(seenBookmarks.length, 7, "Should have seen 7 items being bookmarked.");
|
||||
Assert.equal(seenBookmarks.filter(bm => bm.title != sourceLabel).length,
|
||||
MigrationUtils._importQuantities.bookmarks,
|
||||
"Telemetry should have items except for 'From Microsoft Edge' folders");
|
||||
|
||||
let menuParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.menuGuid);
|
||||
Assert.equal(menuParents.length, 1, "Should have a single folder added to the menu");
|
||||
let toolbarParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.toolbarGuid);
|
||||
Assert.equal(toolbarParents.length, 1, "Should have a single item added to the toolbar");
|
||||
let menuParentGuid = menuParents[0].itemGuid;
|
||||
let toolbarParentGuid = toolbarParents[0].itemGuid;
|
||||
|
||||
let expectedTitlesInMenu = itemsInDB.filter(item => item.ParentId == kEdgeMenuParent).map(item => item.Title);
|
||||
// Hacky, but seems like much the simplest way:
|
||||
expectedTitlesInMenu.push("Item in deleted folder (should be in root)");
|
||||
let expectedTitlesInToolbar = itemsInDB.filter(item => item.ParentId == "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf").map(item => item.Title);
|
||||
|
||||
let edgeNameStr = MigrationUtils.getLocalizedString("sourceNameEdge");
|
||||
let importParentFolderName = MigrationUtils.getLocalizedString("importedBookmarksFolder", [edgeNameStr]);
|
||||
|
||||
for (let bookmark of seenBookmarks) {
|
||||
let shouldBeInMenu = expectedTitlesInMenu.includes(bookmark.title);
|
||||
let shouldBeInToolbar = expectedTitlesInToolbar.includes(bookmark.title);
|
||||
if (bookmark.title == "Folder" || bookmark.title == importParentFolderName) {
|
||||
Assert.equal(bookmark.itemType, PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
"Bookmark " + bookmark.title + " should be a folder");
|
||||
} else {
|
||||
Assert.notEqual(bookmark.itemType, PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||
"Bookmark " + bookmark.title + " should not be a folder");
|
||||
}
|
||||
|
||||
if (shouldBeInMenu) {
|
||||
Assert.equal(bookmark.parentGuid, menuParentGuid, "Item '" + bookmark.title + "' should be in menu");
|
||||
} else if (shouldBeInToolbar) {
|
||||
Assert.equal(bookmark.parentGuid, toolbarParentGuid, "Item '" + bookmark.title + "' should be in toolbar");
|
||||
} else if (bookmark.itemGuid == menuParentGuid || bookmark.itemGuid == toolbarParentGuid) {
|
||||
Assert.ok(true, "Expect toolbar and menu folders to not be in menu or toolbar");
|
||||
} else {
|
||||
// Bit hacky, but we do need to check this.
|
||||
Assert.equal(bookmark.title, "Item in folder", "Subfoldered item shouldn't be in menu or toolbar");
|
||||
let parent = seenBookmarks.find(maybeParent => maybeParent.itemGuid == bookmark.parentGuid);
|
||||
Assert.equal(parent && parent.title, "Folder", "Subfoldered item should be in subfolder labeled 'Folder'");
|
||||
}
|
||||
|
||||
let dbItem = itemsInDB.find(someItem => bookmark.title == someItem.Title);
|
||||
if (!dbItem) {
|
||||
Assert.equal(bookmark.title, importParentFolderName, "Only the extra layer of folders isn't in the input we stuck in the DB.");
|
||||
Assert.ok([menuParentGuid, toolbarParentGuid].includes(bookmark.itemGuid), "This item should be one of the containers");
|
||||
} else {
|
||||
Assert.equal(dbItem.URL || null, bookmark.url && bookmark.url.spec, "URL is correct");
|
||||
Assert.equal(dbItem.DateUpdated.valueOf(), (new Date(bookmark.dateAdded / 1000)).valueOf(), "Date added is correct");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,397 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
|
||||
"resource://gre/modules/WindowsRegistry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
|
||||
"resource://gre/modules/OSCrypto.jsm");
|
||||
|
||||
const IE7_FORM_PASSWORDS_MIGRATOR_NAME = "IE7FormPasswords";
|
||||
const LOGINS_KEY = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
|
||||
const EXTENSION = "-backup";
|
||||
const TESTED_WEBSITES = {
|
||||
twitter: {
|
||||
uri: makeURI("https://twitter.com"),
|
||||
hash: "A89D42BC6406E27265B1AD0782B6F376375764A301",
|
||||
data: [12, 0, 0, 0, 56, 0, 0, 0, 38, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 8, 0, 0, 0, 18, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 9, 0, 0, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 0, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, 0, 0, 0],
|
||||
logins: [
|
||||
{
|
||||
username: "abcdefgh",
|
||||
password: "123456789",
|
||||
hostname: "https://twitter.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439325854000,
|
||||
timeLastUsed: 1439325854000,
|
||||
timePasswordChanged: 1439325854000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
facebook: {
|
||||
uri: makeURI("https://www.facebook.com/"),
|
||||
hash: "EF44D3E034009CB0FD1B1D81A1FF3F3335213BD796",
|
||||
data: [12, 0, 0, 0, 152, 0, 0, 0, 160, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 182, 125, 18, 121, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 88, 182, 125, 18, 121, 212, 208, 1, 9, 0, 0, 0, 40, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0, 0, 0, 60, 0, 0, 0, 134, 65, 33, 37, 121, 212, 208, 1, 9, 0, 0, 0, 80, 0, 0, 0, 45, 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 100, 0, 0, 0, 45, 242, 246, 62, 121, 212, 208, 1, 9, 0, 0, 0, 120, 0, 0, 0, 28, 10, 193, 80, 121, 212, 208, 1, 9, 0, 0, 0, 140, 0, 0, 0, 28, 10, 193, 80, 121, 212, 208, 1, 9, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 48, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 48, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 49, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 49, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 50, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 50, 0, 0, 0, 117, 0, 115, 0, 101, 0, 114, 0, 110, 0, 97, 0, 109, 0, 101, 0, 51, 0, 0, 0, 112, 0, 97, 0, 115, 0, 115, 0, 119, 0, 111, 0, 114, 0, 100, 0, 51, 0, 0, 0],
|
||||
logins: [
|
||||
{
|
||||
username: "username0",
|
||||
password: "password0",
|
||||
hostname: "https://www.facebook.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439326966000,
|
||||
timeLastUsed: 1439326966000,
|
||||
timePasswordChanged: 1439326966000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
username: "username1",
|
||||
password: "password1",
|
||||
hostname: "https://www.facebook.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439326997000,
|
||||
timeLastUsed: 1439326997000,
|
||||
timePasswordChanged: 1439326997000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
username: "username2",
|
||||
password: "password2",
|
||||
hostname: "https://www.facebook.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439327040000,
|
||||
timeLastUsed: 1439327040000,
|
||||
timePasswordChanged: 1439327040000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
username: "username3",
|
||||
password: "password3",
|
||||
hostname: "https://www.facebook.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439327070000,
|
||||
timeLastUsed: 1439327070000,
|
||||
timePasswordChanged: 1439327070000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
live: {
|
||||
uri: makeURI("https://login.live.com/"),
|
||||
hash: "7B506F2D6B81D939A8E0456F036EE8970856FF705E",
|
||||
data: [12, 0, 0, 0, 56, 0, 0, 0, 44, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 17, 219, 140, 148, 212, 208, 1, 9, 0, 0, 0, 20, 0, 0, 0, 212, 17, 219, 140, 148, 212, 208, 1, 11, 0, 0, 0, 114, 0, 105, 0, 97, 0, 100, 0, 104, 0, 49, 6, 74, 6, 39, 6, 54, 6, 0, 0, 39, 6, 66, 6, 49, 6, 35, 6, 80, 0, 192, 0, 223, 0, 119, 0, 246, 0, 114, 0, 100, 0, 0, 0],
|
||||
logins: [
|
||||
{
|
||||
username: "riadhرياض",
|
||||
password: "اقرأPÀßwörd",
|
||||
hostname: "https://login.live.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439338767000,
|
||||
timeLastUsed: 1439338767000,
|
||||
timePasswordChanged: 1439338767000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
reddit: {
|
||||
uri: makeURI("http://www.reddit.com/"),
|
||||
hash: "B644028D1C109A91EC2C4B9D1F145E55A1FAE42065",
|
||||
data: [12, 0, 0, 0, 152, 0, 0, 0, 212, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 8, 234, 114, 153, 212, 208, 1, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 97, 93, 131, 116, 153, 212, 208, 1, 3, 0, 0, 0, 14, 0, 0, 0, 97, 93, 131, 116, 153, 212, 208, 1, 16, 0, 0, 0, 48, 0, 0, 0, 88, 150, 78, 174, 153, 212, 208, 1, 4, 0, 0, 0, 58, 0, 0, 0, 88, 150, 78, 174, 153, 212, 208, 1, 29, 0, 0, 0, 118, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208, 1, 15, 0, 0, 0, 150, 0, 0, 0, 79, 102, 137, 34, 154, 212, 208, 1, 30, 0, 0, 0, 97, 0, 0, 0, 0, 0, 252, 140, 173, 138, 146, 48, 0, 0, 66, 0, 105, 0, 116, 0, 116, 0, 101, 0, 32, 0, 98, 0, 101, 0, 115, 0, 116, 0, 228, 0, 116, 0, 105, 0, 103, 0, 101, 0, 110, 0, 0, 0, 205, 145, 110, 127, 198, 91, 1, 120, 0, 0, 31, 4, 48, 4, 64, 4, 62, 4, 59, 4, 76, 4, 32, 0, 67, 4, 65, 4, 63, 4, 53, 4, 72, 4, 61, 4, 62, 4, 32, 0, 65, 4, 49, 4, 64, 4, 62, 4, 72, 4, 53, 4, 61, 4, 46, 0, 32, 0, 18, 4, 62, 4, 57, 4, 66, 4, 56, 4, 0, 0, 40, 6, 51, 6, 69, 6, 32, 0, 39, 6, 68, 6, 68, 6, 71, 6, 32, 0, 39, 6, 68, 6, 49, 6, 45, 6, 69, 6, 70, 6, 0, 0, 118, 0, 101, 0, 117, 0, 105, 0, 108, 0, 108, 0, 101, 0, 122, 0, 32, 0, 108, 0, 101, 0, 32, 0, 118, 0, 233, 0, 114, 0, 105, 0, 102, 0, 105, 0, 101, 0, 114, 0, 32, 0, 224, 0, 32, 0, 110, 0, 111, 0, 117, 0, 118, 0, 101, 0, 97, 0, 117, 0, 0, 0],
|
||||
logins: [
|
||||
{
|
||||
username: "購読を",
|
||||
password: "Bitte bestätigen",
|
||||
hostname: "http://www.reddit.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439340874000,
|
||||
timeLastUsed: 1439340874000,
|
||||
timePasswordChanged: 1439340874000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
username: "重置密码",
|
||||
password: "Пароль успешно сброшен. Войти",
|
||||
hostname: "http://www.reddit.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439340971000,
|
||||
timeLastUsed: 1439340971000,
|
||||
timePasswordChanged: 1439340971000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
username: "بسم الله الرحمن",
|
||||
password: "veuillez le vérifier à nouveau",
|
||||
hostname: "http://www.reddit.com",
|
||||
formSubmitURL: "",
|
||||
httpRealm: null,
|
||||
usernameField: "",
|
||||
passwordField: "",
|
||||
timeCreated: 1439341166000,
|
||||
timeLastUsed: 1439341166000,
|
||||
timePasswordChanged: 1439341166000,
|
||||
timesUsed: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const TESTED_URLS = [
|
||||
"http://a.foo.com",
|
||||
"http://b.foo.com",
|
||||
"http://c.foo.com",
|
||||
"http://www.test.net",
|
||||
"http://www.test.net/home",
|
||||
"http://www.test.net/index",
|
||||
"https://a.bar.com",
|
||||
"https://b.bar.com",
|
||||
"https://c.bar.com",
|
||||
];
|
||||
|
||||
var nsIWindowsRegKey = Ci.nsIWindowsRegKey;
|
||||
var Storage2Key;
|
||||
|
||||
/*
|
||||
* If the key value exists, it's going to be backed up and replaced, so the value could be restored.
|
||||
* Otherwise a new value is going to be created.
|
||||
*/
|
||||
function backupAndStore(key, name, value) {
|
||||
if (key.hasValue(name)) {
|
||||
// backup the the current value
|
||||
let type = key.getValueType(name);
|
||||
// create a new value using use the current value name followed by EXTENSION as its new name
|
||||
switch (type) {
|
||||
case nsIWindowsRegKey.TYPE_STRING:
|
||||
key.writeStringValue(name + EXTENSION, key.readStringValue(name));
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_BINARY:
|
||||
key.writeBinaryValue(name + EXTENSION, key.readBinaryValue(name));
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_INT:
|
||||
key.writeIntValue(name + EXTENSION, key.readIntValue(name));
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_INT64:
|
||||
key.writeInt64Value(name + EXTENSION, key.readInt64Value(name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
key.writeBinaryValue(name, value);
|
||||
}
|
||||
|
||||
// Remove all values where their names are members of the names array from the key of registry
|
||||
function removeAllValues(key, names) {
|
||||
for (let name of names) {
|
||||
key.removeValue(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore all the backed up values
|
||||
function restore(key) {
|
||||
let count = key.valueCount;
|
||||
let names = []; // the names of the key values
|
||||
for (let i = 0; i < count; ++i) {
|
||||
names.push(key.getValueName(i));
|
||||
}
|
||||
|
||||
for (let name of names) {
|
||||
// backed up values have EXTENSION at the end of their names
|
||||
if (name.lastIndexOf(EXTENSION) == name.length - EXTENSION.length) {
|
||||
let valueName = name.substr(0, name.length - EXTENSION.length);
|
||||
let type = key.getValueType(name);
|
||||
// create a new value using the name before the backup and removed the backed up one
|
||||
switch (type) {
|
||||
case nsIWindowsRegKey.TYPE_STRING:
|
||||
key.writeStringValue(valueName, key.readStringValue(name));
|
||||
key.removeValue(name);
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_BINARY:
|
||||
key.writeBinaryValue(valueName, key.readBinaryValue(name));
|
||||
key.removeValue(name);
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_INT:
|
||||
key.writeIntValue(valueName, key.readIntValue(name));
|
||||
key.removeValue(name);
|
||||
break;
|
||||
case nsIWindowsRegKey.TYPE_INT64:
|
||||
key.writeInt64Value(valueName, key.readInt64Value(name));
|
||||
key.removeValue(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkLoginsAreEqual(passwordManagerLogin, IELogin, id) {
|
||||
passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
for (let attribute in IELogin) {
|
||||
Assert.equal(passwordManagerLogin[attribute], IELogin[attribute],
|
||||
"The two logins ID " + id + " have the same " + attribute);
|
||||
}
|
||||
}
|
||||
|
||||
function createRegistryPath(path) {
|
||||
let loginPath = path.split("\\");
|
||||
let parentKey = Cc["@mozilla.org/windows-registry-key;1"].
|
||||
createInstance(nsIWindowsRegKey);
|
||||
let currentPath = [];
|
||||
for (let currentKey of loginPath) {
|
||||
parentKey.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, currentPath.join("\\"),
|
||||
nsIWindowsRegKey.ACCESS_ALL);
|
||||
|
||||
if (!parentKey.hasChild(currentKey)) {
|
||||
parentKey.createChild(currentKey, 0);
|
||||
}
|
||||
currentPath.push(currentKey);
|
||||
parentKey.close();
|
||||
}
|
||||
}
|
||||
|
||||
function getFirstResourceOfType(type) {
|
||||
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=ie"]
|
||||
.createInstance(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
let migrators = migrator.getResources();
|
||||
for (let m of migrators) {
|
||||
if (m.name == IE7_FORM_PASSWORDS_MIGRATOR_NAME && m.type == type) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
throw new Error("failed to find the " + type + " migrator");
|
||||
}
|
||||
|
||||
function makeURI(aURL) {
|
||||
return Services.io.newURI(aURL, null, null);
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
|
||||
Assert.throws(() => getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS),
|
||||
"The migrator doesn't exist for win8+");
|
||||
return;
|
||||
}
|
||||
// create the path to Storage2 in the registry if it doest exist.
|
||||
createRegistryPath(LOGINS_KEY);
|
||||
Storage2Key = Cc["@mozilla.org/windows-registry-key;1"].
|
||||
createInstance(nsIWindowsRegKey);
|
||||
Storage2Key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, LOGINS_KEY,
|
||||
nsIWindowsRegKey.ACCESS_ALL);
|
||||
|
||||
// create a dummy value otherwise the migrator doesn't exist
|
||||
if (!Storage2Key.hasValue("dummy")) {
|
||||
Storage2Key.writeBinaryValue("dummy", "dummy");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_passwordsNotAvailable() {
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let migrator = getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS);
|
||||
Assert.ok(migrator.exists, "The migrator has to exist");
|
||||
let logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0, "There are no logins at the beginning of the test");
|
||||
|
||||
let uris = []; // the uris of the migrated logins
|
||||
for (let url of TESTED_URLS) {
|
||||
uris.push(makeURI(url));
|
||||
// in this test, there is no IE login data in the registry, so after the migration, the number
|
||||
// of logins in the store should be 0
|
||||
migrator._migrateURIs(uris);
|
||||
logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0,
|
||||
"There are no logins after doing the migration without adding values to the registry");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_passwordsAvailable() {
|
||||
if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let crypto = new OSCrypto();
|
||||
let hashes = []; // the hashes of all migrator websites, this is going to be used for the clean up
|
||||
|
||||
do_register_cleanup(() => {
|
||||
Services.logins.removeAllLogins();
|
||||
logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0, "There are no logins after the cleanup");
|
||||
// remove all the values created in this test from the registry
|
||||
removeAllValues(Storage2Key, hashes);
|
||||
// restore all backed up values
|
||||
restore(Storage2Key);
|
||||
|
||||
// clean the dummy value
|
||||
if (Storage2Key.hasValue("dummy")) {
|
||||
Storage2Key.removeValue("dummy");
|
||||
}
|
||||
Storage2Key.close();
|
||||
crypto.finalize();
|
||||
});
|
||||
|
||||
let migrator = getFirstResourceOfType(MigrationUtils.resourceTypes.PASSWORDS);
|
||||
Assert.ok(migrator.exists, "The migrator has to exist");
|
||||
let logins = Services.logins.getAllLogins({});
|
||||
Assert.equal(logins.length, 0, "There are no logins at the beginning of the test");
|
||||
|
||||
let uris = []; // the uris of the migrated logins
|
||||
|
||||
let loginCount = 0;
|
||||
for (let current in TESTED_WEBSITES) {
|
||||
let website = TESTED_WEBSITES[current];
|
||||
// backup the current the registry value if it exists and replace the existing value/create a
|
||||
// new value with the encrypted data
|
||||
backupAndStore(Storage2Key, website.hash,
|
||||
crypto.encryptData(crypto.arrayToString(website.data),
|
||||
website.uri.spec, true));
|
||||
Assert.ok(migrator.exists, "The migrator has to exist");
|
||||
uris.push(website.uri);
|
||||
hashes.push(website.hash);
|
||||
|
||||
migrator._migrateURIs(uris);
|
||||
logins = Services.logins.getAllLogins({});
|
||||
// check that the number of logins in the password manager has increased as expected which means
|
||||
// that all the values for the current website were imported
|
||||
loginCount += website.logins.length;
|
||||
Assert.equal(logins.length, loginCount,
|
||||
"The number of logins has increased after the migration");
|
||||
// NB: because telemetry records any login data passed to the login manager, it
|
||||
// also gets told about logins that are duplicates or invalid (for one reason
|
||||
// or another) and so its counts might exceed those of the login manager itself.
|
||||
Assert.greaterOrEqual(MigrationUtils._importQuantities.logins, loginCount,
|
||||
"Telemetry quantities equal or exceed the actual import.");
|
||||
// Reset - this normally happens at the start of a new migration, but we're calling
|
||||
// the migrator directly so can't rely on that:
|
||||
MigrationUtils._importQuantities.logins = 0;
|
||||
|
||||
let startIndex = loginCount - website.logins.length;
|
||||
// compares the imported password manager logins with their expected logins
|
||||
for (let i = 0; i < website.logins.length; i++) {
|
||||
checkLoginsAreEqual(logins[startIndex + i], website.logins[i],
|
||||
" " + current + " - " + i + " ");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
let migrator = MigrationUtils.getMigrator("ie");
|
||||
// Sanity check for the source.
|
||||
Assert.ok(migrator.sourceExists);
|
||||
|
||||
// Wait for the imported bookmarks. Check that "From Internet Explorer"
|
||||
// folders are created in the menu and on the toolbar.
|
||||
let source = MigrationUtils.getLocalizedString("sourceNameIE");
|
||||
let label = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
|
||||
|
||||
let expectedParents = [ PlacesUtils.bookmarksMenuFolderId,
|
||||
PlacesUtils.toolbarFolderId ];
|
||||
|
||||
let itemCount = 0;
|
||||
let bmObserver = {
|
||||
onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
|
||||
if (aTitle != label) {
|
||||
itemCount++;
|
||||
}
|
||||
if (expectedParents.length > 0 && aTitle == label) {
|
||||
let index = expectedParents.indexOf(aParentId);
|
||||
Assert.notEqual(index, -1);
|
||||
expectedParents.splice(index, 1);
|
||||
}
|
||||
},
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onItemRemoved() {},
|
||||
onItemChanged() {},
|
||||
onItemVisited() {},
|
||||
onItemMoved() {},
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(bmObserver, false);
|
||||
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS);
|
||||
PlacesUtils.bookmarks.removeObserver(bmObserver);
|
||||
Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount,
|
||||
"Ensure telemetry matches actual number of imported items.");
|
||||
|
||||
// Check the bookmarks have been imported to all the expected parents.
|
||||
Assert.equal(expectedParents.length, 0, "Got all the expected parents");
|
||||
});
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
|
||||
"resource://gre/modules/ctypes.jsm");
|
||||
|
||||
add_task(function* () {
|
||||
let migrator = MigrationUtils.getMigrator("ie");
|
||||
// Sanity check for the source.
|
||||
Assert.ok(migrator.sourceExists);
|
||||
|
||||
const BOOL = ctypes.bool;
|
||||
const LPCTSTR = ctypes.char16_t.ptr;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const LPDWORD = DWORD.ptr;
|
||||
|
||||
let wininet = ctypes.open("Wininet");
|
||||
|
||||
/*
|
||||
BOOL InternetSetCookieW(
|
||||
_In_ LPCTSTR lpszUrl,
|
||||
_In_ LPCTSTR lpszCookieName,
|
||||
_In_ LPCTSTR lpszCookieData
|
||||
);
|
||||
*/
|
||||
let setIECookie = wininet.declare("InternetSetCookieW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
LPCTSTR,
|
||||
LPCTSTR,
|
||||
LPCTSTR);
|
||||
|
||||
/*
|
||||
BOOL InternetGetCookieW(
|
||||
_In_ LPCTSTR lpszUrl,
|
||||
_In_ LPCTSTR lpszCookieName,
|
||||
_Out_ LPCTSTR lpszCookieData,
|
||||
_Inout_ LPDWORD lpdwSize
|
||||
);
|
||||
*/
|
||||
let getIECookie = wininet.declare("InternetGetCookieW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
LPCTSTR,
|
||||
LPCTSTR,
|
||||
LPCTSTR,
|
||||
LPDWORD);
|
||||
|
||||
// We need to randomize the cookie to avoid clashing with other cookies
|
||||
// that might have been set by previous tests and not properly cleared.
|
||||
let date = (new Date()).getDate();
|
||||
const COOKIE = {
|
||||
get host() {
|
||||
return new URL(this.href).host;
|
||||
},
|
||||
href: `http://mycookietest.${Math.random()}.com`,
|
||||
name: "testcookie",
|
||||
value: "testvalue",
|
||||
expiry: new Date(new Date().setDate(date + 2))
|
||||
};
|
||||
let data = ctypes.char16_t.array()(256);
|
||||
let sizeRef = DWORD(256).address();
|
||||
|
||||
do_register_cleanup(() => {
|
||||
// Remove the cookie.
|
||||
try {
|
||||
let expired = new Date(new Date().setDate(date - 2));
|
||||
let rv = setIECookie(COOKIE.href, COOKIE.name,
|
||||
`; expires=${expired.toUTCString()}`);
|
||||
Assert.ok(rv, "Expired the IE cookie");
|
||||
Assert.ok(!getIECookie(COOKIE.href, COOKIE.name, data, sizeRef),
|
||||
"The cookie has been properly removed");
|
||||
} catch (ex) {}
|
||||
|
||||
// Close the library.
|
||||
try {
|
||||
wininet.close();
|
||||
} catch (ex) {}
|
||||
});
|
||||
|
||||
// Create the persistent cookie in IE.
|
||||
let value = `${COOKIE.value}; expires=${COOKIE.expiry.toUTCString()}`;
|
||||
let rv = setIECookie(COOKIE.href, COOKIE.name, value);
|
||||
Assert.ok(rv, "Added a persistent IE cookie: " + value);
|
||||
|
||||
// Sanity check the cookie has been created.
|
||||
Assert.ok(getIECookie(COOKIE.href, COOKIE.name, data, sizeRef),
|
||||
"Found the added persistent IE cookie");
|
||||
do_print("Found cookie: " + data.readString());
|
||||
Assert.equal(data.readString(), `${COOKIE.name}=${COOKIE.value}`,
|
||||
"Found the expected cookie");
|
||||
|
||||
// Sanity check that there are no cookies.
|
||||
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 0,
|
||||
"There are no cookies initially");
|
||||
|
||||
// Migrate cookies.
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES);
|
||||
|
||||
Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1,
|
||||
"Migrated the expected number of cookies");
|
||||
|
||||
// Now check the cookie details.
|
||||
let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host, {});
|
||||
Assert.ok(enumerator.hasMoreElements());
|
||||
let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
|
||||
|
||||
Assert.equal(foundCookie.name, COOKIE.name);
|
||||
Assert.equal(foundCookie.value, COOKIE.value);
|
||||
Assert.equal(foundCookie.host, "." + COOKIE.host);
|
||||
Assert.equal(foundCookie.expiry, Math.floor(COOKIE.expiry / 1000));
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
registerFakePath("ULibDir", do_get_file("Library/"));
|
||||
|
||||
let migrator = MigrationUtils.getMigrator("safari");
|
||||
// Sanity check for the source.
|
||||
Assert.ok(migrator.sourceExists);
|
||||
|
||||
// Wait for the imported bookmarks. Check that "From Safari"
|
||||
// folders are created on the toolbar.
|
||||
let source = MigrationUtils.getLocalizedString("sourceNameSafari");
|
||||
let label = MigrationUtils.getLocalizedString("importedBookmarksFolder", [source]);
|
||||
|
||||
let expectedParents = [ PlacesUtils.toolbarFolderId ];
|
||||
let itemCount = 0;
|
||||
|
||||
let bmObserver = {
|
||||
onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
|
||||
if (aTitle != label) {
|
||||
itemCount++;
|
||||
}
|
||||
if (expectedParents.length > 0 && aTitle == label) {
|
||||
let index = expectedParents.indexOf(aParentId);
|
||||
Assert.ok(index != -1, "Found expected parent");
|
||||
expectedParents.splice(index, 1);
|
||||
}
|
||||
},
|
||||
onBeginUpdateBatch() {},
|
||||
onEndUpdateBatch() {},
|
||||
onItemRemoved() {},
|
||||
onItemChanged() {},
|
||||
onItemVisited() {},
|
||||
onItemMoved() {},
|
||||
};
|
||||
PlacesUtils.bookmarks.addObserver(bmObserver, false);
|
||||
|
||||
yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS);
|
||||
PlacesUtils.bookmarks.removeObserver(bmObserver);
|
||||
|
||||
// Check the bookmarks have been imported to all the expected parents.
|
||||
Assert.ok(!expectedParents.length, "No more expected parents");
|
||||
Assert.equal(itemCount, 13, "Should import all 13 items.");
|
||||
// Check that the telemetry matches:
|
||||
Assert.equal(MigrationUtils._importQuantities.bookmarks, itemCount, "Telemetry reporting correct.");
|
||||
});
|
||||
|
|
@ -1,695 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
let AutoMigrateBackstage = Cu.import("resource:///modules/AutoMigrate.jsm"); /* globals AutoMigrate */
|
||||
|
||||
let gShimmedMigratorKeyPicker = null;
|
||||
let gShimmedMigrator = null;
|
||||
|
||||
const kUsecPerMin = 60 * 1000000;
|
||||
|
||||
// This is really a proxy on MigrationUtils, but if we specify that directly,
|
||||
// we get in trouble because the object itself is frozen, and Proxies can't
|
||||
// return a different value to an object when directly proxying a frozen
|
||||
// object.
|
||||
AutoMigrateBackstage.MigrationUtils = new Proxy({}, {
|
||||
get(target, name) {
|
||||
if (name == "getMigratorKeyForDefaultBrowser" && gShimmedMigratorKeyPicker) {
|
||||
return gShimmedMigratorKeyPicker;
|
||||
}
|
||||
if (name == "getMigrator" && gShimmedMigrator) {
|
||||
return function() { return gShimmedMigrator };
|
||||
}
|
||||
return MigrationUtils[name];
|
||||
},
|
||||
});
|
||||
|
||||
do_register_cleanup(function() {
|
||||
AutoMigrateBackstage.MigrationUtils = MigrationUtils;
|
||||
});
|
||||
|
||||
// This should be replaced by using History.fetch with a fetchVisits option,
|
||||
// once that becomes available
|
||||
function* visitsForURL(url)
|
||||
{
|
||||
let visitCount = 0;
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
visitCount = yield db.execute(
|
||||
`SELECT count(*) FROM moz_historyvisits v
|
||||
JOIN moz_places h ON h.id = v.place_id
|
||||
WHERE url_hash = hash(:url) AND url = :url`,
|
||||
{url});
|
||||
visitCount = visitCount[0].getInt64(0);
|
||||
return visitCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test automatically picking a browser to migrate from
|
||||
*/
|
||||
add_task(function* checkMigratorPicking() {
|
||||
Assert.throws(() => AutoMigrate.pickMigrator("firefox"),
|
||||
/Can't automatically migrate from Firefox/,
|
||||
"Should throw when explicitly picking Firefox.");
|
||||
|
||||
Assert.throws(() => AutoMigrate.pickMigrator("gobbledygook"),
|
||||
/migrator object is not available/,
|
||||
"Should throw when passing unknown migrator key");
|
||||
gShimmedMigratorKeyPicker = function() {
|
||||
return "firefox";
|
||||
};
|
||||
Assert.throws(() => AutoMigrate.pickMigrator(),
|
||||
/Can't automatically migrate from Firefox/,
|
||||
"Should throw when implicitly picking Firefox.");
|
||||
gShimmedMigratorKeyPicker = function() {
|
||||
return "gobbledygook";
|
||||
};
|
||||
Assert.throws(() => AutoMigrate.pickMigrator(),
|
||||
/migrator object is not available/,
|
||||
"Should throw when an unknown migrator is the default");
|
||||
gShimmedMigratorKeyPicker = function() {
|
||||
return "";
|
||||
};
|
||||
Assert.throws(() => AutoMigrate.pickMigrator(),
|
||||
/Could not determine default browser key/,
|
||||
"Should throw when an unknown migrator is the default");
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Test automatically picking a profile to migrate from
|
||||
*/
|
||||
add_task(function* checkProfilePicking() {
|
||||
let fakeMigrator = {sourceProfiles: [{id: "a"}, {id: "b"}]};
|
||||
let profB = fakeMigrator.sourceProfiles[1];
|
||||
Assert.throws(() => AutoMigrate.pickProfile(fakeMigrator),
|
||||
/Don't know how to pick a profile when more/,
|
||||
"Should throw when there are multiple profiles.");
|
||||
Assert.throws(() => AutoMigrate.pickProfile(fakeMigrator, "c"),
|
||||
/Profile specified was not found/,
|
||||
"Should throw when the profile supplied doesn't exist.");
|
||||
let profileToMigrate = AutoMigrate.pickProfile(fakeMigrator, "b");
|
||||
Assert.equal(profileToMigrate, profB, "Should return profile supplied");
|
||||
|
||||
fakeMigrator.sourceProfiles = null;
|
||||
Assert.throws(() => AutoMigrate.pickProfile(fakeMigrator, "c"),
|
||||
/Profile specified but only a default profile found./,
|
||||
"Should throw when the profile supplied doesn't exist.");
|
||||
profileToMigrate = AutoMigrate.pickProfile(fakeMigrator);
|
||||
Assert.equal(profileToMigrate, null, "Should return default profile when that's the only one.");
|
||||
|
||||
fakeMigrator.sourceProfiles = [];
|
||||
Assert.throws(() => AutoMigrate.pickProfile(fakeMigrator),
|
||||
/No profile data found/,
|
||||
"Should throw when no profile data is present.");
|
||||
|
||||
fakeMigrator.sourceProfiles = [{id: "a"}];
|
||||
let profA = fakeMigrator.sourceProfiles[0];
|
||||
profileToMigrate = AutoMigrate.pickProfile(fakeMigrator);
|
||||
Assert.equal(profileToMigrate, profA, "Should return the only profile if only one is present.");
|
||||
});
|
||||
|
||||
/**
|
||||
* Test the complete automatic process including browser and profile selection,
|
||||
* and actual migration (which implies startup)
|
||||
*/
|
||||
add_task(function* checkIntegration() {
|
||||
gShimmedMigrator = {
|
||||
get sourceProfiles() {
|
||||
do_print("Read sourceProfiles");
|
||||
return null;
|
||||
},
|
||||
getMigrateData(profileToMigrate) {
|
||||
this._getMigrateDataArgs = profileToMigrate;
|
||||
return Ci.nsIBrowserProfileMigrator.BOOKMARKS;
|
||||
},
|
||||
migrate(types, startup, profileToMigrate) {
|
||||
this._migrateArgs = [types, startup, profileToMigrate];
|
||||
},
|
||||
};
|
||||
gShimmedMigratorKeyPicker = function() {
|
||||
return "gobbledygook";
|
||||
};
|
||||
AutoMigrate.migrate("startup");
|
||||
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
|
||||
"getMigrateData called with 'null' as a profile");
|
||||
|
||||
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
|
||||
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
|
||||
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
|
||||
"migrate called with 'null' as a profile");
|
||||
});
|
||||
|
||||
/**
|
||||
* Test the undo preconditions and a no-op undo in the automigrator.
|
||||
*/
|
||||
add_task(function* checkUndoPreconditions() {
|
||||
let shouldAddData = false;
|
||||
gShimmedMigrator = {
|
||||
get sourceProfiles() {
|
||||
do_print("Read sourceProfiles");
|
||||
return null;
|
||||
},
|
||||
getMigrateData(profileToMigrate) {
|
||||
this._getMigrateDataArgs = profileToMigrate;
|
||||
return Ci.nsIBrowserProfileMigrator.BOOKMARKS;
|
||||
},
|
||||
migrate(types, startup, profileToMigrate) {
|
||||
this._migrateArgs = [types, startup, profileToMigrate];
|
||||
if (shouldAddData) {
|
||||
// Insert a login and check that that worked.
|
||||
MigrationUtils.insertLoginWrapper({
|
||||
hostname: "www.mozilla.org",
|
||||
formSubmitURL: "http://www.mozilla.org",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
});
|
||||
}
|
||||
TestUtils.executeSoon(function() {
|
||||
Services.obs.notifyObservers(null, "Migration:Ended", undefined);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
gShimmedMigratorKeyPicker = function() {
|
||||
return "gobbledygook";
|
||||
};
|
||||
AutoMigrate.migrate("startup");
|
||||
let migrationFinishedPromise = TestUtils.topicObserved("Migration:Ended");
|
||||
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
|
||||
"getMigrateData called with 'null' as a profile");
|
||||
|
||||
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
|
||||
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
|
||||
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
|
||||
"migrate called with 'null' as a profile");
|
||||
|
||||
yield migrationFinishedPromise;
|
||||
Assert.ok(Preferences.has("browser.migrate.automigrate.browser"),
|
||||
"Should have set browser pref");
|
||||
Assert.ok(!(yield AutoMigrate.canUndo()), "Should not be able to undo migration, as there's no data");
|
||||
gShimmedMigrator._migrateArgs = null;
|
||||
gShimmedMigrator._getMigrateDataArgs = null;
|
||||
Preferences.reset("browser.migrate.automigrate.browser");
|
||||
shouldAddData = true;
|
||||
|
||||
AutoMigrate.migrate("startup");
|
||||
migrationFinishedPromise = TestUtils.topicObserved("Migration:Ended");
|
||||
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
|
||||
"getMigrateData called with 'null' as a profile");
|
||||
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
|
||||
"migrate called with 'null' as a profile");
|
||||
|
||||
yield migrationFinishedPromise;
|
||||
let storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
|
||||
"http://www.mozilla.org", null);
|
||||
Assert.equal(storedLogins.length, 1, "Should have 1 login");
|
||||
|
||||
Assert.ok(Preferences.has("browser.migrate.automigrate.browser"),
|
||||
"Should have set browser pref");
|
||||
Assert.ok((yield AutoMigrate.canUndo()), "Should be able to undo migration, as now there's data");
|
||||
|
||||
yield AutoMigrate.undo();
|
||||
Assert.ok(true, "Should be able to finish an undo cycle.");
|
||||
|
||||
// Check that the undo removed the passwords:
|
||||
storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
|
||||
"http://www.mozilla.org", null);
|
||||
Assert.equal(storedLogins.length, 0, "Should have no logins");
|
||||
});
|
||||
|
||||
/**
|
||||
* Fake a migration and then try to undo it to verify all data gets removed.
|
||||
*/
|
||||
add_task(function* checkUndoRemoval() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
Preferences.set("browser.migrate.automigrate.browser", "automationbrowser");
|
||||
// Insert a login and check that that worked.
|
||||
MigrationUtils.insertLoginWrapper({
|
||||
hostname: "www.mozilla.org",
|
||||
formSubmitURL: "http://www.mozilla.org",
|
||||
username: "user",
|
||||
password: "pass",
|
||||
});
|
||||
let storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
|
||||
"http://www.mozilla.org", null);
|
||||
Assert.equal(storedLogins.length, 1, "Should have 1 login");
|
||||
|
||||
// Insert a bookmark and check that we have exactly 1 bookmark for that URI.
|
||||
yield MigrationUtils.insertBookmarkWrapper({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
url: "http://www.example.org/",
|
||||
title: "Some example bookmark",
|
||||
});
|
||||
|
||||
let bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
|
||||
Assert.ok(bookmark, "Should have a bookmark before undo");
|
||||
Assert.equal(bookmark.title, "Some example bookmark", "Should have correct bookmark before undo.");
|
||||
|
||||
// Insert 2 history visits
|
||||
let now_uSec = Date.now() * 1000;
|
||||
let visitedURI = Services.io.newURI("http://www.example.com/", null, null);
|
||||
let frecencyUpdatePromise = new Promise(resolve => {
|
||||
let expectedChanges = 2;
|
||||
let observer = {
|
||||
onFrecencyChanged: function() {
|
||||
if (!--expectedChanges) {
|
||||
PlacesUtils.history.removeObserver(observer);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
};
|
||||
PlacesUtils.history.addObserver(observer, false);
|
||||
});
|
||||
yield MigrationUtils.insertVisitsWrapper([{
|
||||
uri: visitedURI,
|
||||
visits: [
|
||||
{
|
||||
transitionType: PlacesUtils.history.TRANSITION_LINK,
|
||||
visitDate: now_uSec,
|
||||
},
|
||||
{
|
||||
transitionType: PlacesUtils.history.TRANSITION_LINK,
|
||||
visitDate: now_uSec - 100 * kUsecPerMin,
|
||||
},
|
||||
]
|
||||
}]);
|
||||
yield frecencyUpdatePromise;
|
||||
|
||||
// Verify that both visits get reported.
|
||||
let opts = PlacesUtils.history.getNewQueryOptions();
|
||||
opts.resultType = opts.RESULTS_AS_VISIT;
|
||||
let query = PlacesUtils.history.getNewQuery();
|
||||
query.uri = visitedURI;
|
||||
let visits = PlacesUtils.history.executeQuery(query, opts);
|
||||
visits.root.containerOpen = true;
|
||||
Assert.equal(visits.root.childCount, 2, "Should have 2 visits");
|
||||
// Clean up:
|
||||
visits.root.containerOpen = false;
|
||||
|
||||
yield AutoMigrate.saveUndoState();
|
||||
|
||||
// Verify that we can undo, then undo:
|
||||
Assert.ok(AutoMigrate.canUndo(), "Should be possible to undo migration");
|
||||
yield AutoMigrate.undo();
|
||||
|
||||
let histograms = [
|
||||
"FX_STARTUP_MIGRATION_UNDO_BOOKMARKS_ERRORCOUNT",
|
||||
"FX_STARTUP_MIGRATION_UNDO_LOGINS_ERRORCOUNT",
|
||||
"FX_STARTUP_MIGRATION_UNDO_VISITS_ERRORCOUNT",
|
||||
];
|
||||
for (let histogramId of histograms) {
|
||||
let keyedHistogram = Services.telemetry.getKeyedHistogramById(histogramId);
|
||||
let histogramData = keyedHistogram.snapshot().automationbrowser;
|
||||
Assert.equal(histogramData.sum, 0, `Should have reported 0 errors to ${histogramId}.`);
|
||||
Assert.greaterOrEqual(histogramData.counts[0], 1, `Should have reported value of 0 one time to ${histogramId}.`);
|
||||
}
|
||||
histograms = [
|
||||
"FX_STARTUP_MIGRATION_UNDO_BOOKMARKS_MS",
|
||||
"FX_STARTUP_MIGRATION_UNDO_LOGINS_MS",
|
||||
"FX_STARTUP_MIGRATION_UNDO_VISITS_MS",
|
||||
"FX_STARTUP_MIGRATION_UNDO_TOTAL_MS",
|
||||
];
|
||||
for (let histogramId of histograms) {
|
||||
Assert.greater(Services.telemetry.getKeyedHistogramById(histogramId).snapshot().automationbrowser.sum, 0,
|
||||
`Should have reported non-zero time spent using undo for ${histogramId}`);
|
||||
}
|
||||
|
||||
// Check that the undo removed the history visits:
|
||||
visits = PlacesUtils.history.executeQuery(query, opts);
|
||||
visits.root.containerOpen = true;
|
||||
Assert.equal(visits.root.childCount, 0, "Should have no more visits");
|
||||
visits.root.containerOpen = false;
|
||||
|
||||
// Check that the undo removed the bookmarks:
|
||||
bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
|
||||
Assert.ok(!bookmark, "Should have no bookmarks after undo");
|
||||
|
||||
// Check that the undo removed the passwords:
|
||||
storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
|
||||
"http://www.mozilla.org", null);
|
||||
Assert.equal(storedLogins.length, 0, "Should have no logins");
|
||||
});
|
||||
|
||||
add_task(function* checkUndoBookmarksState() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
const {TYPE_FOLDER, TYPE_BOOKMARK} = PlacesUtils.bookmarks;
|
||||
let title = "Some example bookmark";
|
||||
let url = "http://www.example.com";
|
||||
let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
|
||||
let {guid, lastModified} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title, url, parentGuid
|
||||
});
|
||||
Assert.deepEqual((yield MigrationUtils.stopAndRetrieveUndoData()).get("bookmarks"),
|
||||
[{lastModified, parentGuid, guid, type: TYPE_BOOKMARK}]);
|
||||
|
||||
MigrationUtils.initializeUndoData();
|
||||
({guid, lastModified} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title, parentGuid, type: TYPE_FOLDER
|
||||
}));
|
||||
let folder = {guid, lastModified, parentGuid, type: TYPE_FOLDER};
|
||||
let folderGuid = folder.guid;
|
||||
({guid, lastModified} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title, url, parentGuid: folderGuid
|
||||
}));
|
||||
let kid1 = {guid, lastModified, parentGuid: folderGuid, type: TYPE_BOOKMARK};
|
||||
({guid, lastModified} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title, url, parentGuid: folderGuid
|
||||
}));
|
||||
let kid2 = {guid, lastModified, parentGuid: folderGuid, type: TYPE_BOOKMARK};
|
||||
|
||||
let bookmarksUndo = (yield MigrationUtils.stopAndRetrieveUndoData()).get("bookmarks");
|
||||
Assert.equal(bookmarksUndo.length, 3);
|
||||
// We expect that the last modified time from first kid #1 and then kid #2
|
||||
// has been propagated to the folder:
|
||||
folder.lastModified = kid2.lastModified;
|
||||
// Not just using deepEqual on the entire array (which should work) because
|
||||
// the failure messages get truncated by xpcshell which is unhelpful.
|
||||
Assert.deepEqual(bookmarksUndo[0], folder);
|
||||
Assert.deepEqual(bookmarksUndo[1], kid1);
|
||||
Assert.deepEqual(bookmarksUndo[2], kid2);
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
});
|
||||
|
||||
add_task(function* testBookmarkRemovalByUndo() {
|
||||
const {TYPE_FOLDER} = PlacesUtils.bookmarks;
|
||||
MigrationUtils.initializeUndoData();
|
||||
let title = "Some example bookmark";
|
||||
let url = "http://www.mymagicaluniqueurl.com";
|
||||
let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
|
||||
let {guid} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title: "Some folder", parentGuid, type: TYPE_FOLDER
|
||||
});
|
||||
let folderGuid = guid;
|
||||
let itemsToRemove = [];
|
||||
({guid} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title: "Inner folder", parentGuid: folderGuid, type: TYPE_FOLDER
|
||||
}));
|
||||
let innerFolderGuid = guid;
|
||||
itemsToRemove.push(innerFolderGuid);
|
||||
|
||||
({guid} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title: "Inner inner folder", parentGuid: innerFolderGuid, type: TYPE_FOLDER
|
||||
}));
|
||||
itemsToRemove.push(guid);
|
||||
|
||||
({guid} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title: "Inner nested item", url: "http://inner-nested-example.com", parentGuid: guid
|
||||
}));
|
||||
itemsToRemove.push(guid);
|
||||
|
||||
({guid} = yield MigrationUtils.insertBookmarkWrapper({
|
||||
title, url, parentGuid: folderGuid
|
||||
}));
|
||||
itemsToRemove.push(guid);
|
||||
|
||||
for (let toBeRemovedGuid of itemsToRemove) {
|
||||
let dbResultForGuid = yield PlacesUtils.bookmarks.fetch(toBeRemovedGuid);
|
||||
Assert.ok(dbResultForGuid, "Should be able to find items that will be removed.");
|
||||
}
|
||||
let bookmarkUndoState = (yield MigrationUtils.stopAndRetrieveUndoData()).get("bookmarks");
|
||||
// Now insert a separate item into this folder, not related to the migration.
|
||||
let newItem = yield PlacesUtils.bookmarks.insert(
|
||||
{title: "Not imported", parentGuid: folderGuid, url: "http://www.example.com"}
|
||||
);
|
||||
|
||||
yield AutoMigrate._removeUnchangedBookmarks(bookmarkUndoState);
|
||||
Assert.ok(true, "Successfully removed imported items.");
|
||||
|
||||
let itemFromDB = yield PlacesUtils.bookmarks.fetch(newItem.guid);
|
||||
Assert.ok(itemFromDB, "Item we inserted outside of migration is still there.");
|
||||
itemFromDB = yield PlacesUtils.bookmarks.fetch(folderGuid);
|
||||
Assert.ok(itemFromDB, "Folder we inserted in migration is still there because of new kids.");
|
||||
for (let removedGuid of itemsToRemove) {
|
||||
let dbResultForGuid = yield PlacesUtils.bookmarks.fetch(removedGuid);
|
||||
let dbgStr = dbResultForGuid && dbResultForGuid.title;
|
||||
Assert.equal(null, dbResultForGuid, "Should not be able to find items that should have been removed, but found " + dbgStr);
|
||||
}
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
});
|
||||
|
||||
add_task(function* checkUndoLoginsState() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
MigrationUtils.insertLoginWrapper({
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com/",
|
||||
timeCreated: new Date(),
|
||||
});
|
||||
let storedLogins = Services.logins.findLogins({}, "https://example.com", "", "");
|
||||
let storedLogin = storedLogins[0];
|
||||
storedLogin.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
let {guid, timePasswordChanged} = storedLogin;
|
||||
let undoLoginData = (yield MigrationUtils.stopAndRetrieveUndoData()).get("logins");
|
||||
Assert.deepEqual([{guid, timePasswordChanged}], undoLoginData);
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
add_task(function* testLoginsRemovalByUndo() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
MigrationUtils.insertLoginWrapper({
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
hostname: "https://example.com",
|
||||
formSubmitURL: "https://example.com/",
|
||||
timeCreated: new Date(),
|
||||
});
|
||||
MigrationUtils.insertLoginWrapper({
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
hostname: "https://example.org",
|
||||
formSubmitURL: "https://example.org/",
|
||||
timeCreated: new Date(new Date().getTime() - 10000),
|
||||
});
|
||||
// This should update the existing login
|
||||
LoginHelper.maybeImportLogin({
|
||||
username: "foo",
|
||||
password: "bazzy",
|
||||
hostname: "https://example.org",
|
||||
formSubmitURL: "https://example.org/",
|
||||
timePasswordChanged: new Date(),
|
||||
});
|
||||
Assert.equal(1, LoginHelper.searchLoginsWithObject({hostname: "https://example.org", formSubmitURL: "https://example.org/"}).length,
|
||||
"Should be only 1 login for example.org (that was updated)");
|
||||
let undoLoginData = (yield MigrationUtils.stopAndRetrieveUndoData()).get("logins");
|
||||
|
||||
yield AutoMigrate._removeUnchangedLogins(undoLoginData);
|
||||
Assert.equal(0, LoginHelper.searchLoginsWithObject({hostname: "https://example.com", formSubmitURL: "https://example.com/"}).length,
|
||||
"unchanged example.com entry should have been removed.");
|
||||
Assert.equal(1, LoginHelper.searchLoginsWithObject({hostname: "https://example.org", formSubmitURL: "https://example.org/"}).length,
|
||||
"changed example.org entry should have persisted.");
|
||||
Services.logins.removeAllLogins();
|
||||
});
|
||||
|
||||
add_task(function* checkUndoVisitsState() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
yield MigrationUtils.insertVisitsWrapper([{
|
||||
uri: NetUtil.newURI("http://www.example.com/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2015-07-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-09-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-08-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}, {
|
||||
uri: NetUtil.newURI("http://www.example.org/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2016-04-03").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-08-03").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}, {
|
||||
uri: NetUtil.newURI("http://www.example.com/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2015-10-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}]);
|
||||
let undoVisitData = (yield MigrationUtils.stopAndRetrieveUndoData()).get("visits");
|
||||
Assert.deepEqual(Array.from(undoVisitData.map(v => v.url)).sort(),
|
||||
["http://www.example.com/", "http://www.example.org/"]);
|
||||
Assert.deepEqual(undoVisitData.find(v => v.url == "http://www.example.com/"), {
|
||||
url: "http://www.example.com/",
|
||||
visitCount: 4,
|
||||
first: new Date("2015-07-10").getTime() * 1000,
|
||||
last: new Date("2015-10-10").getTime() * 1000,
|
||||
});
|
||||
Assert.deepEqual(undoVisitData.find(v => v.url == "http://www.example.org/"), {
|
||||
url: "http://www.example.org/",
|
||||
visitCount: 2,
|
||||
first: new Date("2015-08-03").getTime() * 1000,
|
||||
last: new Date("2016-04-03").getTime() * 1000,
|
||||
});
|
||||
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
add_task(function* checkUndoVisitsState() {
|
||||
MigrationUtils.initializeUndoData();
|
||||
yield MigrationUtils.insertVisitsWrapper([{
|
||||
uri: NetUtil.newURI("http://www.example.com/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2015-07-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-09-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-08-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}, {
|
||||
uri: NetUtil.newURI("http://www.example.org/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2016-04-03").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}, {
|
||||
visitDate: new Date("2015-08-03").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}, {
|
||||
uri: NetUtil.newURI("http://www.example.com/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2015-10-10").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}, {
|
||||
uri: NetUtil.newURI("http://www.mozilla.org/"),
|
||||
title: "Example",
|
||||
visits: [{
|
||||
visitDate: new Date("2015-01-01").getTime() * 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
|
||||
}],
|
||||
}]);
|
||||
|
||||
// We have to wait until frecency updates have been handled in order
|
||||
// to accurately determine whether we're doing the right thing.
|
||||
let frecencyUpdatesHandled = new Promise(resolve => {
|
||||
PlacesUtils.history.addObserver({
|
||||
onFrecencyChanged(aURI) {
|
||||
if (aURI.spec == "http://www.unrelated.org/") {
|
||||
PlacesUtils.history.removeObserver(this);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
yield PlacesUtils.history.insertMany([{
|
||||
url: "http://www.example.com/",
|
||||
title: "Example",
|
||||
visits: [{
|
||||
date: new Date("2015-08-16"),
|
||||
}],
|
||||
}, {
|
||||
url: "http://www.example.org/",
|
||||
title: "Example",
|
||||
visits: [{
|
||||
date: new Date("2016-01-03"),
|
||||
}, {
|
||||
date: new Date("2015-05-03"),
|
||||
}],
|
||||
}, {
|
||||
url: "http://www.unrelated.org/",
|
||||
title: "Unrelated",
|
||||
visits: [{
|
||||
date: new Date("2015-09-01"),
|
||||
}],
|
||||
}]);
|
||||
yield frecencyUpdatesHandled;
|
||||
let undoVisitData = (yield MigrationUtils.stopAndRetrieveUndoData()).get("visits");
|
||||
|
||||
let frecencyChangesExpected = new Map([
|
||||
["http://www.example.com/", PromiseUtils.defer()],
|
||||
["http://www.example.org/", PromiseUtils.defer()]
|
||||
]);
|
||||
let uriDeletedExpected = new Map([
|
||||
["http://www.mozilla.org/", PromiseUtils.defer()],
|
||||
]);
|
||||
let wrongMethodDeferred = PromiseUtils.defer();
|
||||
let observer = {
|
||||
onBeginUpdateBatch: function() {},
|
||||
onEndUpdateBatch: function() {},
|
||||
onVisit: function(uri) {
|
||||
wrongMethodDeferred.reject(new Error("Unexpected call to onVisit " + uri.spec));
|
||||
},
|
||||
onTitleChanged: function(uri) {
|
||||
wrongMethodDeferred.reject(new Error("Unexpected call to onTitleChanged " + uri.spec));
|
||||
},
|
||||
onClearHistory: function() {
|
||||
wrongMethodDeferred.reject("Unexpected call to onClearHistory");
|
||||
},
|
||||
onPageChanged: function(uri) {
|
||||
wrongMethodDeferred.reject(new Error("Unexpected call to onPageChanged " + uri.spec));
|
||||
},
|
||||
onFrecencyChanged: function(aURI) {
|
||||
do_print("frecency change");
|
||||
Assert.ok(frecencyChangesExpected.has(aURI.spec),
|
||||
"Should be expecting frecency change for " + aURI.spec);
|
||||
frecencyChangesExpected.get(aURI.spec).resolve();
|
||||
},
|
||||
onManyFrecenciesChanged: function() {
|
||||
do_print("Many frecencies changed");
|
||||
wrongMethodDeferred.reject(new Error("This test can't deal with onManyFrecenciesChanged to be called"));
|
||||
},
|
||||
onDeleteURI: function(aURI) {
|
||||
do_print("delete uri");
|
||||
Assert.ok(uriDeletedExpected.has(aURI.spec),
|
||||
"Should be expecting uri deletion for " + aURI.spec);
|
||||
uriDeletedExpected.get(aURI.spec).resolve();
|
||||
},
|
||||
};
|
||||
PlacesUtils.history.addObserver(observer, false);
|
||||
|
||||
yield AutoMigrate._removeSomeVisits(undoVisitData);
|
||||
PlacesUtils.history.removeObserver(observer);
|
||||
yield Promise.all(uriDeletedExpected.values());
|
||||
yield Promise.all(frecencyChangesExpected.values());
|
||||
|
||||
Assert.equal(yield visitsForURL("http://www.example.com/"), 1,
|
||||
"1 example.com visit (out of 5) should have persisted despite being within the range, due to limiting");
|
||||
Assert.equal(yield visitsForURL("http://www.mozilla.org/"), 0,
|
||||
"0 mozilla.org visits should have persisted (out of 1).");
|
||||
Assert.equal(yield visitsForURL("http://www.example.org/"), 2,
|
||||
"2 example.org visits should have persisted (out of 4).");
|
||||
Assert.equal(yield visitsForURL("http://www.unrelated.org/"), 1,
|
||||
"1 unrelated.org visits should have persisted as it's not involved in the import.");
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
add_task(function* checkHistoryRemovalCompletion() {
|
||||
AutoMigrate._errorMap = {bookmarks: 0, visits: 0, logins: 0};
|
||||
yield AutoMigrate._removeSomeVisits([{url: "http://www.example.com/", limit: -1}]);
|
||||
ok(true, "Removing visits should complete even if removing some visits failed.");
|
||||
Assert.equal(AutoMigrate._errorMap.visits, 1, "Should have logged the error for visits.");
|
||||
|
||||
// Unfortunately there's not a reliable way to make removing bookmarks be
|
||||
// unhappy unless the DB is messed up (e.g. contains children but has
|
||||
// parents removed already).
|
||||
yield AutoMigrate._removeUnchangedBookmarks([
|
||||
{guid: PlacesUtils.bookmarks, lastModified: new Date(0), parentGuid: 0},
|
||||
{guid: "gobbledygook", lastModified: new Date(0), parentGuid: 0},
|
||||
]);
|
||||
ok(true, "Removing bookmarks should complete even if some items are gone or bogus.");
|
||||
Assert.equal(AutoMigrate._errorMap.bookmarks, 0,
|
||||
"Should have ignored removing non-existing (or builtin) bookmark.");
|
||||
|
||||
|
||||
yield AutoMigrate._removeUnchangedLogins([
|
||||
{guid: "gobbledygook", timePasswordChanged: new Date(0)},
|
||||
]);
|
||||
ok(true, "Removing logins should complete even if logins don't exist.");
|
||||
Assert.equal(AutoMigrate._errorMap.logins, 0,
|
||||
"Should have ignored removing non-existing logins.");
|
||||
});
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* globals do_get_tempdir */
|
||||
|
||||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function readFile(file) {
|
||||
let stream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
stream.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
||||
|
||||
let sis = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
sis.init(stream);
|
||||
let contents = sis.read(file.fileSize);
|
||||
sis.close();
|
||||
return contents;
|
||||
}
|
||||
|
||||
function checkDirectoryContains(dir, files) {
|
||||
print("checking " + dir.path + " - should contain " + Object.keys(files));
|
||||
let seen = new Set();
|
||||
let enumerator = dir.directoryEntries;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
|
||||
print("found file: " + file.path);
|
||||
Assert.ok(file.leafName in files, file.leafName + " exists, but shouldn't");
|
||||
|
||||
let expectedContents = files[file.leafName];
|
||||
if (typeof expectedContents != "string") {
|
||||
// it's a subdir - recurse!
|
||||
Assert.ok(file.isDirectory(), "should be a subdir");
|
||||
let newDir = dir.clone();
|
||||
newDir.append(file.leafName);
|
||||
checkDirectoryContains(newDir, expectedContents);
|
||||
} else {
|
||||
Assert.ok(!file.isDirectory(), "should be a regular file");
|
||||
let contents = readFile(file);
|
||||
Assert.equal(contents, expectedContents);
|
||||
}
|
||||
seen.add(file.leafName);
|
||||
}
|
||||
let missing = [];
|
||||
for (let x in files) {
|
||||
if (!seen.has(x)) {
|
||||
missing.push(x);
|
||||
}
|
||||
}
|
||||
Assert.deepEqual(missing, [], "no missing files in " + dir.path);
|
||||
}
|
||||
|
||||
function getTestDirs() {
|
||||
// we make a directory structure in a temp dir which mirrors what we are
|
||||
// testing.
|
||||
let tempDir = do_get_tempdir();
|
||||
let srcDir = tempDir.clone();
|
||||
srcDir.append("test_source_dir");
|
||||
srcDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
let targetDir = tempDir.clone();
|
||||
targetDir.append("test_target_dir");
|
||||
targetDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
// no need to cleanup these dirs - the xpcshell harness will do it for us.
|
||||
return [srcDir, targetDir];
|
||||
}
|
||||
|
||||
function writeToFile(dir, leafName, contents) {
|
||||
let file = dir.clone();
|
||||
file.append(leafName);
|
||||
|
||||
let outputStream = FileUtils.openFileOutputStream(file);
|
||||
outputStream.write(contents, contents.length);
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
function createSubDir(dir, subDirName) {
|
||||
let subDir = dir.clone();
|
||||
subDir.append(subDirName);
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
return subDir;
|
||||
}
|
||||
|
||||
function promiseMigrator(name, srcDir, targetDir) {
|
||||
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=firefox"]
|
||||
.createInstance(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
let migrators = migrator._getResourcesInternal(srcDir, targetDir);
|
||||
for (let m of migrators) {
|
||||
if (m.name == name) {
|
||||
return new Promise(resolve => m.migrate(resolve));
|
||||
}
|
||||
}
|
||||
throw new Error("failed to find the " + name + " migrator");
|
||||
}
|
||||
|
||||
function promiseTelemetryMigrator(srcDir, targetDir) {
|
||||
return promiseMigrator("telemetry", srcDir, targetDir);
|
||||
}
|
||||
|
||||
add_task(function* test_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with empty directories");
|
||||
// check both are empty
|
||||
checkDirectoryContains(srcDir, {});
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_migrate_files() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Set up datareporting files, some to copy, some not.
|
||||
let stateContent = JSON.stringify({
|
||||
clientId: "68d5474e-19dc-45c1-8e9a-81fca592707c",
|
||||
});
|
||||
let sessionStateContent = "foobar 5432";
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
writeToFile(subDir, "session-state.json", sessionStateContent);
|
||||
writeToFile(subDir, "other.file", "do not copy");
|
||||
|
||||
let archived = createSubDir(subDir, "archived");
|
||||
writeToFile(archived, "other.file", "do not copy");
|
||||
|
||||
// Set up FHR files, they should not be copied.
|
||||
writeToFile(srcDir, "healthreport.sqlite", "do not copy");
|
||||
writeToFile(srcDir, "healthreport.sqlite-wal", "do not copy");
|
||||
subDir = createSubDir(srcDir, "healthreport");
|
||||
writeToFile(subDir, "state.json", "do not copy");
|
||||
writeToFile(subDir, "other.file", "do not copy");
|
||||
|
||||
// Perform migration.
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with important telemetry files copied");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting": {
|
||||
"state.json": stateContent,
|
||||
"session-state.json": sessionStateContent,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_fallback_fhr_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Test that we fall back to migrating FHR state if the datareporting
|
||||
// state file does not exist.
|
||||
let stateContent = JSON.stringify({
|
||||
clientId: "68d5474e-19dc-45c1-8e9a-81fca592707c",
|
||||
});
|
||||
let subDir = createSubDir(srcDir, "healthreport");
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
|
||||
// Perform migration.
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport": {
|
||||
"state.json": stateContent,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_datareporting_not_dir() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
writeToFile(srcDir, "datareporting", "I'm a file but should be a directory");
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true even though the directory was a file");
|
||||
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_datareporting_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Migrate with an empty 'datareporting' subdir.
|
||||
createSubDir(srcDir, "datareporting");
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// We should end up with no migrated files.
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting": {},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_healthreport_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Migrate with no 'datareporting' and an empty 'healthreport' subdir.
|
||||
createSubDir(srcDir, "healthreport");
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// We should end up with no migrated files.
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_datareporting_many() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Create some datareporting files.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let shouldBeCopied = "should be copied";
|
||||
writeToFile(subDir, "state.json", shouldBeCopied);
|
||||
writeToFile(subDir, "session-state.json", shouldBeCopied);
|
||||
writeToFile(subDir, "something.else", "should not");
|
||||
createSubDir(subDir, "emptyDir");
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"state.json": shouldBeCopied,
|
||||
"session-state.json": shouldBeCopied,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_no_session_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Check that migration still works properly if we only have state.json.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let stateContent = "abcd984";
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"state.json": stateContent,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_no_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Check that migration still works properly if we only have session-state.json.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let sessionStateContent = "abcd512";
|
||||
writeToFile(subDir, "session-state.json", sessionStateContent);
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"session-state.json": sessionStateContent,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_times_migration() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// create a times.json in the source directory.
|
||||
let contents = JSON.stringify({created: 1234});
|
||||
writeToFile(srcDir, "times.json", contents);
|
||||
|
||||
let earliest = Date.now();
|
||||
let ok = yield promiseMigrator("times", srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
let latest = Date.now();
|
||||
|
||||
let timesFile = targetDir.clone();
|
||||
timesFile.append("times.json");
|
||||
|
||||
let raw = readFile(timesFile);
|
||||
let times = JSON.parse(raw);
|
||||
Assert.ok(times.reset >= earliest && times.reset <= latest);
|
||||
// and it should have left the creation time alone.
|
||||
Assert.equal(times.created, 1234);
|
||||
});
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
[DEFAULT]
|
||||
head = head_migration.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android'
|
||||
support-files =
|
||||
Library/**
|
||||
AppData/**
|
||||
|
||||
[test_automigration.js]
|
||||
[test_Chrome_cookies.js]
|
||||
skip-if = os != "mac" # Relies on ULibDir
|
||||
[test_Chrome_passwords.js]
|
||||
skip-if = os != "win"
|
||||
[test_Edge_availability.js]
|
||||
[test_Edge_db_migration.js]
|
||||
skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
|
||||
[test_fx_telemetry.js]
|
||||
[test_IE_bookmarks.js]
|
||||
skip-if = os != "win"
|
||||
[test_IE_cookies.js]
|
||||
skip-if = os != "win"
|
||||
[test_IE7_passwords.js]
|
||||
skip-if = os != "win"
|
||||
[test_Safari_bookmarks.js]
|
||||
skip-if = os != "mac"
|
||||
Loading…
Add table
Add a link
Reference in a new issue