merge fx-team to mozilla-central a=merge
|
@ -354,6 +354,10 @@ pref("browser.urlbar.suggest.searches", true);
|
|||
pref("browser.urlbar.suggest.searches", false);
|
||||
#endif
|
||||
|
||||
// Limit the number of characters sent to the current search engine to fetch
|
||||
// suggestions.
|
||||
pref("browser.urlbar.maxCharsForSearchSuggestions", 20);
|
||||
|
||||
// Restrictions to current suggestions can also be applied (intersection).
|
||||
// Typed suggestion works only if history is set to true.
|
||||
pref("browser.urlbar.suggest.history.onlyTyped", false);
|
||||
|
|
|
@ -134,6 +134,7 @@ skip-if = e10s # Bug 1093153 - no about:home support yet
|
|||
[browser_autocomplete_a11y_label.js]
|
||||
skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
|
||||
[browser_autocomplete_cursor.js]
|
||||
[browser_autocomplete_edit_completed.js]
|
||||
[browser_autocomplete_enter_race.js]
|
||||
[browser_autocomplete_no_title.js]
|
||||
[browser_autocomplete_autoselect.js]
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
add_task(function*() {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
|
||||
yield PlacesTestUtils.addVisits([
|
||||
{ uri: makeURI("http://example.com/foo") },
|
||||
{ uri: makeURI("http://example.com/foo/bar") },
|
||||
]);
|
||||
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
yield* do_test();
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", false);
|
||||
yield* do_test();
|
||||
});
|
||||
|
||||
registerCleanupFunction(function* () {
|
||||
Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
function* do_test() {
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
gURLBar.focus();
|
||||
|
||||
yield promiseAutocompleteResultPopup("http://example.com");
|
||||
|
||||
let popup = gURLBar.popup;
|
||||
let list = popup.richlistbox;
|
||||
let initialIndex = list.selectedIndex;
|
||||
|
||||
info("Key Down to select the next item.");
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
let nextIndex = initialIndex + 1;
|
||||
let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex);
|
||||
is(list.selectedIndex, nextIndex, "The next item is selected.");
|
||||
is(gURLBar.value, nextValue, "The selected URL is completed.");
|
||||
|
||||
info("Press backspace");
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
yield promiseSearchComplete();
|
||||
|
||||
let editedValue = gURLBar.value;
|
||||
is(list.selectedIndex, initialIndex, "The initial index is selected again.");
|
||||
isnot(editedValue, nextValue, "The URL has changed.");
|
||||
|
||||
info("Press return to load edited URL.");
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
yield Promise.all([
|
||||
promisePopupHidden(gURLBar.popup),
|
||||
waitForDocLoadAndStopIt("http://" + editedValue)]);
|
||||
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
}
|
|
@ -6,16 +6,19 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
|
||||
|
||||
const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
|
||||
const S100NS_PER_MS = 10;
|
||||
|
||||
const AUTH_TYPE = {
|
||||
SCHEME_HTML: 0,
|
||||
SCHEME_BASIC: 1,
|
||||
SCHEME_DIGEST: 2
|
||||
};
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
@ -25,6 +28,8 @@ Cu.import("resource:///modules/MigrationUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
|
||||
"resource://gre/modules/OSCrypto.jsm");
|
||||
|
||||
/**
|
||||
* Convert Chrome time format to Date object
|
||||
|
@ -93,7 +98,11 @@ ChromeProfileMigrator.prototype.getResources =
|
|||
if (profileFolder.exists()) {
|
||||
let possibleResources = [GetBookmarksResource(profileFolder),
|
||||
GetHistoryResource(profileFolder),
|
||||
GetCookiesResource(profileFolder)];
|
||||
GetCookiesResource(profileFolder),
|
||||
#ifdef XP_WIN
|
||||
GetWindowsPasswordsResource(profileFolder)
|
||||
#endif
|
||||
];
|
||||
return [r for each (r in possibleResources) if (r != null)];
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +362,101 @@ function GetCookiesResource(aProfileFolder) {
|
|||
}
|
||||
}
|
||||
|
||||
function GetWindowsPasswordsResource(aProfileFolder) {
|
||||
let loginFile = aProfileFolder.clone();
|
||||
loginFile.append("Login Data");
|
||||
if (!loginFile.exists())
|
||||
return null;
|
||||
|
||||
return {
|
||||
type: MigrationUtils.resourceTypes.PASSWORDS,
|
||||
|
||||
migrate(aCallback) {
|
||||
let dbConn = Services.storage.openUnsharedDatabase(loginFile);
|
||||
let stmt = dbConn.createAsyncStatement(`
|
||||
SELECT origin_url, action_url, username_element, username_value,
|
||||
password_element, password_value, signon_realm, scheme, date_created,
|
||||
times_used FROM logins WHERE blacklisted_by_user = 0`);
|
||||
let crypto = new OSCrypto();
|
||||
|
||||
stmt.executeAsync({
|
||||
_rowToLoginInfo(row) {
|
||||
let loginInfo = {
|
||||
username: row.getResultByName("username_value"),
|
||||
password: crypto.decryptData(row.getResultByName("password_value")),
|
||||
hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
|
||||
submitURL: null,
|
||||
httpRealm: null,
|
||||
usernameElement: row.getResultByName("username_element"),
|
||||
passwordElement: row.getResultByName("password_element"),
|
||||
timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
|
||||
timesUsed: row.getResultByName("times_used") + 0,
|
||||
};
|
||||
|
||||
switch (row.getResultByName("scheme")) {
|
||||
case AUTH_TYPE.SCHEME_HTML:
|
||||
loginInfo.submitURL = NetUtil.newURI(row.getResultByName("action_url")).prePath;
|
||||
break;
|
||||
case AUTH_TYPE.SCHEME_BASIC:
|
||||
case AUTH_TYPE.SCHEME_DIGEST:
|
||||
// signon_realm format is URIrealm, so we need remove URI
|
||||
loginInfo.httpRealm = row.getResultByName("signon_realm")
|
||||
.substring(loginInfo.hostName.length + 1);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Login data scheme type not supported: " +
|
||||
row.getResultByName("scheme"));
|
||||
}
|
||||
|
||||
return loginInfo;
|
||||
},
|
||||
|
||||
handleResult(aResults) {
|
||||
for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
|
||||
try {
|
||||
let loginInfo = this._rowToLoginInfo(row);
|
||||
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
|
||||
|
||||
login.init(loginInfo.hostName, loginInfo.submitURL, loginInfo.httpRealm,
|
||||
loginInfo.username, loginInfo.password, loginInfo.usernameElement,
|
||||
loginInfo.passwordElement);
|
||||
login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
login.timeCreated = loginInfo.timeCreated;
|
||||
login.timeLastUsed = loginInfo.timeCreated;
|
||||
login.timePasswordChanged = loginInfo.timeCreated;
|
||||
login.timesUsed = loginInfo.timesUsed;
|
||||
|
||||
// Add the login only if there's not an existing entry
|
||||
let logins = Services.logins.findLogins({}, login.hostname,
|
||||
login.formSubmitURL,
|
||||
login.httpRealm);
|
||||
|
||||
// Bug 1187190: Password changes should be propagated depending on timestamps.
|
||||
if (!logins.some(l => login.matches(l, true))) {
|
||||
Services.logins.addLogin(login);
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleError(aError) {
|
||||
Cu.reportError("Async statement execution returned with '" +
|
||||
aError.result + "', '" + aError.message + "'");
|
||||
},
|
||||
|
||||
handleCompletion(aReason) {
|
||||
dbConn.asyncClose();
|
||||
aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
|
||||
crypto.finalize();
|
||||
},
|
||||
});
|
||||
stmt.finalize();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
|
||||
ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
|
||||
ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
|
||||
|
|
Двоичные данные
browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data
Normal file
|
@ -0,0 +1,213 @@
|
|||
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,
|
||||
},
|
||||
];
|
||||
|
||||
let crypto = new OSCrypto();
|
||||
let 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.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");
|
||||
|
||||
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");
|
||||
|
||||
for (let i = 0; i < newLogins.length; i++) {
|
||||
checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
|
||||
}
|
||||
});
|
|
@ -5,9 +5,12 @@ firefox-appdir = browser
|
|||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
support-files =
|
||||
Library/**
|
||||
AppData/**
|
||||
|
||||
[test_Chrome_cookies.js]
|
||||
skip-if = os != "mac" # Relies on ULibDir
|
||||
[test_Chrome_passwords.js]
|
||||
skip-if = os != "win"
|
||||
[test_fx_fhr.js]
|
||||
[test_IE_bookmarks.js]
|
||||
skip-if = os != "win"
|
||||
|
|
|
@ -2854,6 +2854,13 @@ TextPropertyEditor.prototype = {
|
|||
this.ruleView.tooltips.isEditing) || this.popup.isOpen;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the rule to the current text property
|
||||
*/
|
||||
get rule() {
|
||||
return this.prop.rule;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the property editor's DOM.
|
||||
*/
|
||||
|
@ -3004,7 +3011,7 @@ TextPropertyEditor.prototype = {
|
|||
* @return {string} the stylesheet's href.
|
||||
*/
|
||||
get sheetHref() {
|
||||
let domRule = this.prop.rule.domRule;
|
||||
let domRule = this.rule.domRule;
|
||||
if (domRule) {
|
||||
return domRule.href || domRule.nodeHref;
|
||||
}
|
||||
|
@ -3068,14 +3075,14 @@ TextPropertyEditor.prototype = {
|
|||
|
||||
// Combine the property's value and priority into one string for
|
||||
// the value.
|
||||
let store = this.prop.rule.elementStyle.store;
|
||||
let val = store.userProperties.getProperty(this.prop.rule.style, name,
|
||||
let store = this.rule.elementStyle.store;
|
||||
let val = store.userProperties.getProperty(this.rule.style, name,
|
||||
this.prop.value);
|
||||
if (this.prop.priority) {
|
||||
val += " !" + this.prop.priority;
|
||||
}
|
||||
|
||||
let propDirty = store.userProperties.contains(this.prop.rule.style, name);
|
||||
let propDirty = store.userProperties.contains(this.rule.style, name);
|
||||
|
||||
if (propDirty) {
|
||||
this.element.setAttribute("dirty", "");
|
||||
|
@ -3156,7 +3163,6 @@ TextPropertyEditor.prototype = {
|
|||
},
|
||||
|
||||
_onStartEditing: function() {
|
||||
this.element.classList.remove("ruleview-overridden");
|
||||
this._previewValue(this.prop.value);
|
||||
},
|
||||
|
||||
|
@ -3294,23 +3300,38 @@ TextPropertyEditor.prototype = {
|
|||
* True if the change should be applied.
|
||||
*/
|
||||
_onNameDone: function(aValue, aCommit) {
|
||||
if (aCommit && !this.ruleEditor.isEditing) {
|
||||
// Unlike the value editor, if a name is empty the entire property
|
||||
// should always be removed.
|
||||
if (aValue.trim() === "") {
|
||||
this.remove();
|
||||
} else {
|
||||
// Adding multiple rules inside of name field overwrites the current
|
||||
// property with the first, then adds any more onto the property list.
|
||||
let properties = parseDeclarations(aValue);
|
||||
if ((!aCommit && this.ruleEditor.isEditing) ||
|
||||
this.committed.name == aValue) {
|
||||
// Disable the property if the property was originally disabled.
|
||||
if (!this.prop.enabled) {
|
||||
this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
|
||||
}
|
||||
|
||||
if (properties.length) {
|
||||
this.prop.setName(properties[0].name);
|
||||
if (properties.length > 1) {
|
||||
this.prop.setValue(properties[0].value, properties[0].priority);
|
||||
this.ruleEditor.addProperties(properties.slice(1), this.prop);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlike the value editor, if a name is empty the entire property
|
||||
// should always be removed.
|
||||
if (aValue.trim() === "") {
|
||||
this.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
// Adding multiple rules inside of name field overwrites the current
|
||||
// property with the first, then adds any more onto the property list.
|
||||
let properties = parseDeclarations(aValue);
|
||||
|
||||
if (properties.length) {
|
||||
this.prop.setName(properties[0].name);
|
||||
this.committed.name = this.prop.name;
|
||||
|
||||
if (!this.prop.enabled) {
|
||||
this.prop.setEnabled(true);
|
||||
}
|
||||
|
||||
if (properties.length > 1) {
|
||||
this.prop.setValue(properties[0].value, properties[0].priority);
|
||||
this.ruleEditor.addProperties(properties.slice(1), this.prop);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3342,35 +3363,38 @@ TextPropertyEditor.prototype = {
|
|||
* @param {bool} aCommit
|
||||
* True if the change should be applied.
|
||||
*/
|
||||
_onValueDone: function(aValue, aCommit) {
|
||||
if (!aCommit && !this.ruleEditor.isEditing) {
|
||||
_onValueDone: function(aValue="", aCommit) {
|
||||
let parsedProperties = this._getValueAndExtraProperties(aValue);
|
||||
let val = parseSingleValue(parsedProperties.firstValue);
|
||||
let isValueUnchanged =
|
||||
!parsedProperties.propertiesToAdd.length &&
|
||||
this.committed.value == val.value &&
|
||||
this.committed.priority == val.priority;
|
||||
|
||||
if ((!aCommit && !this.ruleEditor.isEditing) || isValueUnchanged) {
|
||||
// A new property should be removed when escape is pressed.
|
||||
if (this.removeOnRevert) {
|
||||
this.remove();
|
||||
} else {
|
||||
// update the editor back to committed value
|
||||
this.update();
|
||||
|
||||
// undo the preview in content style
|
||||
this.ruleEditor.rule.previewPropertyValue(this.prop,
|
||||
this.prop.value, this.prop.priority);
|
||||
// Disable the property if the property was originally disabled.
|
||||
this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let {propertiesToAdd, firstValue} =
|
||||
this._getValueAndExtraProperties(aValue);
|
||||
|
||||
// First, set this property value (common case, only modified a property)
|
||||
let val = parseSingleValue(firstValue);
|
||||
|
||||
this.prop.setValue(val.value, val.priority);
|
||||
|
||||
if (!this.prop.enabled) {
|
||||
this.prop.setEnabled(true);
|
||||
}
|
||||
|
||||
this.removeOnRevert = false;
|
||||
this.committed.value = this.prop.value;
|
||||
this.committed.priority = this.prop.priority;
|
||||
|
||||
// If needed, add any new properties after this.prop.
|
||||
this.ruleEditor.addProperties(propertiesToAdd, this.prop);
|
||||
this.ruleEditor.addProperties(parsedProperties.propertiesToAdd, this.prop);
|
||||
|
||||
// If the name or value is not actively being edited, and the value is
|
||||
// empty, then remove the whole property.
|
||||
|
@ -3444,6 +3468,9 @@ TextPropertyEditor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
this.element.classList.remove("ruleview-overridden");
|
||||
this.enable.style.visibility = "hidden";
|
||||
|
||||
let val = parseSingleValue(aValue);
|
||||
this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
|
||||
val.priority);
|
||||
|
|
|
@ -92,6 +92,10 @@ skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work wit
|
|||
[browser_ruleview_edit-property_01.js]
|
||||
[browser_ruleview_edit-property_02.js]
|
||||
[browser_ruleview_edit-property_03.js]
|
||||
[browser_ruleview_edit-property_04.js]
|
||||
[browser_ruleview_edit-property_05.js]
|
||||
[browser_ruleview_edit-property_06.js]
|
||||
[browser_ruleview_edit-property_07.js]
|
||||
[browser_ruleview_edit-selector-commit.js]
|
||||
[browser_ruleview_edit-selector_01.js]
|
||||
[browser_ruleview_edit-selector_02.js]
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that a disabled property is previewed when the property name or value
|
||||
// editor is focused and the property remains disabled when the escaping out of
|
||||
// the property editor.
|
||||
|
||||
let TEST_URI = [
|
||||
"<style type='text/css'>",
|
||||
"#testid {",
|
||||
" background-color: blue;",
|
||||
"}",
|
||||
"</style>",
|
||||
"<div id='testid'>Styled Node</div>",
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("#testid", inspector);
|
||||
yield testDisableProperty(inspector, view);
|
||||
});
|
||||
|
||||
function* testDisableProperty(inspector, view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
|
||||
info("Disabling a property");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
let newValue = yield executeInContent("Test:GetRulePropertyValue", {
|
||||
styleSheetIndex: 0,
|
||||
ruleIndex: 0,
|
||||
name: "background-color"
|
||||
});
|
||||
is(newValue, "", "background-color should have been unset.");
|
||||
|
||||
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
|
||||
propEditor.nameSpan, "VK_ESCAPE");
|
||||
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
|
||||
propEditor.valueSpan, "VK_ESCAPE");
|
||||
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
|
||||
propEditor.valueSpan, "VK_TAB");
|
||||
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
|
||||
propEditor.valueSpan, "VK_RETURN");
|
||||
}
|
||||
|
||||
function* testPreviewDisableProperty(view, ruleEditor, propEditor,
|
||||
editableField, commitKey) {
|
||||
let editor = yield focusEditableField(view, editableField);
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
ok(!propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"property is not overridden.");
|
||||
is(propEditor.enable.style.visibility, "hidden",
|
||||
"property enable checkbox is hidden.");
|
||||
|
||||
let newValue = yield executeInContent("Test:GetRulePropertyValue", {
|
||||
styleSheetIndex: 0,
|
||||
ruleIndex: 0,
|
||||
name: "background-color"
|
||||
});
|
||||
is(newValue, "blue", "background-color should have been previewed.");
|
||||
|
||||
let onBlur = once(editor.input, "blur");
|
||||
EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
|
||||
yield onBlur;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
ok(!propEditor.prop.enabled, "property is disabled.");
|
||||
ok(propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"property is overridden.");
|
||||
is(propEditor.enable.style.visibility, "visible",
|
||||
"property enable checkbox is visible.");
|
||||
ok(!propEditor.enable.getAttribute("checked"),
|
||||
"property enable checkbox is not checked.");
|
||||
|
||||
newValue = yield executeInContent("Test:GetRulePropertyValue", {
|
||||
styleSheetIndex: 0,
|
||||
ruleIndex: 0,
|
||||
name: "background-color"
|
||||
});
|
||||
is(newValue, "", "background-color should have been unset.");
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that a disabled property is re-enabled if the property name or value is
|
||||
// modified
|
||||
|
||||
let TEST_URI = [
|
||||
"<style type='text/css'>",
|
||||
"#testid {",
|
||||
" background-color: blue;",
|
||||
"}",
|
||||
"</style>",
|
||||
"<div id='testid'>Styled Node</div>",
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("#testid", inspector);
|
||||
yield testEditingDisableProperty(inspector, view);
|
||||
});
|
||||
|
||||
function* testEditingDisableProperty(inspector, view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
|
||||
info("Disabling background-color property");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
let newValue = yield getRulePropertyValue("background-color");
|
||||
is(newValue, "", "background-color should have been unset.");
|
||||
|
||||
yield focusEditableField(view, propEditor.nameSpan);
|
||||
|
||||
info("Entering a new property name, including : to commit and " +
|
||||
"focus the value");
|
||||
let onValueFocus = once(ruleEditor.element, "focus", true);
|
||||
EventUtils.sendString("border-color:", view.styleWindow);
|
||||
yield onValueFocus;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
info("Escape editing the property value");
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
newValue = yield getRulePropertyValue("border-color");
|
||||
is(newValue, "blue", "border-color should have been set.");
|
||||
|
||||
ok(propEditor.prop.enabled, "border-color property is enabled.");
|
||||
ok(!propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"border-color is not overridden");
|
||||
|
||||
info("Disabling border-color property");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
newValue = yield getRulePropertyValue("border-color");
|
||||
is(newValue, "", "border-color should have been unset.");
|
||||
|
||||
info("Enter a new property value for the border-color property");
|
||||
let editor = yield focusEditableField(view, propEditor.valueSpan);
|
||||
let onBlur = once(editor.input, "blur");
|
||||
EventUtils.sendString("red;", view.styleWindow);
|
||||
yield onBlur;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
newValue = yield getRulePropertyValue("border-color");
|
||||
is(newValue, "red", "new border-color should have been set.");
|
||||
|
||||
ok(propEditor.prop.enabled, "border-color property is enabled.");
|
||||
ok(!propEditor.element.classList.contains("ruleview-overridden"),
|
||||
"border-color is not overridden");
|
||||
}
|
||||
|
||||
function* getRulePropertyValue(name) {
|
||||
let propValue = yield executeInContent("Test:GetRulePropertyValue", {
|
||||
styleSheetIndex: 0,
|
||||
ruleIndex: 0,
|
||||
name: name
|
||||
});
|
||||
return propValue;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that editing a property's priority is behaving correctly, and disabling
|
||||
// and editing the property will re-enable the property.
|
||||
|
||||
let TEST_URI = [
|
||||
"<style type='text/css'>",
|
||||
"body {",
|
||||
" background-color: green !important;",
|
||||
"}",
|
||||
"body {",
|
||||
" background-color: red;",
|
||||
"}",
|
||||
"</style>",
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("body", inspector);
|
||||
yield testEditPropertyPriorityAndDisable(inspector, view);
|
||||
});
|
||||
|
||||
function* testEditPropertyPriorityAndDisable(inspector, view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
|
||||
is((yield getComputedStyleProperty("body", null, "background-color")),
|
||||
"rgb(0, 128, 0)", "green background color is set.");
|
||||
|
||||
let editor = yield focusEditableField(view, propEditor.valueSpan);
|
||||
let onBlur = once(editor.input, "blur");
|
||||
EventUtils.sendString("red !important;", view.styleWindow);
|
||||
yield onBlur;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
is(propEditor.valueSpan.textContent, "red !important",
|
||||
"'red !important' property value is correctly set.");
|
||||
is((yield getComputedStyleProperty("body", null, "background-color")),
|
||||
"rgb(255, 0, 0)", "red background color is set.");
|
||||
|
||||
info("Disabling red background color property");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
is((yield getComputedStyleProperty("body", null, "background-color")),
|
||||
"rgb(0, 128, 0)", "green background color is set.");
|
||||
|
||||
editor = yield focusEditableField(view, propEditor.valueSpan);
|
||||
onBlur = once(editor.input, "blur");
|
||||
EventUtils.sendString("red;", view.styleWindow);
|
||||
yield onBlur;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
is(propEditor.valueSpan.textContent, "red",
|
||||
"'red' property value is correctly set.");
|
||||
ok(propEditor.prop.enabled, "red background-color property is enabled.");
|
||||
is((yield getComputedStyleProperty("body", null, "background-color")),
|
||||
"rgb(0, 128, 0)", "green background color is set.");
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that adding multiple values will enable the property even if the
|
||||
// property does not change, and that the extra values are added correctly.
|
||||
|
||||
let TEST_URI = [
|
||||
"<style type='text/css'>",
|
||||
"#testid {",
|
||||
" background-color: red;",
|
||||
"}",
|
||||
"</style>",
|
||||
"<div id='testid'>Styled Node</div>",
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("#testid", inspector);
|
||||
yield testEditDisableProperty(inspector, view);
|
||||
});
|
||||
|
||||
function* testEditDisableProperty(inspector, view) {
|
||||
let ruleEditor = getRuleViewRuleEditor(view, 1);
|
||||
let propEditor = ruleEditor.rule.textProps[0].editor;
|
||||
|
||||
info("Disabling red background color property");
|
||||
propEditor.enable.click();
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
ok(!propEditor.prop.enabled, "red background-color property is disabled.");
|
||||
|
||||
let editor = yield focusEditableField(view, propEditor.valueSpan);
|
||||
let onBlur = once(editor.input, "blur");
|
||||
EventUtils.sendString("red; color: red;", view.styleWindow);
|
||||
yield onBlur;
|
||||
yield ruleEditor.rule._applyingModifications;
|
||||
|
||||
is(propEditor.valueSpan.textContent, "red",
|
||||
"'red' property value is correctly set.");
|
||||
ok(propEditor.prop.enabled, "red background-color property is enabled.");
|
||||
is((yield getComputedStyleProperty("#testid", null, "background-color")),
|
||||
"rgb(255, 0, 0)", "red background color is set.");
|
||||
|
||||
propEditor = ruleEditor.rule.textProps[1].editor;
|
||||
is(propEditor.nameSpan.textContent, "color",
|
||||
"new 'color' property name is correctly set.");
|
||||
is(propEditor.valueSpan.textContent, "red",
|
||||
"new 'red' property value is correctly set.");
|
||||
is((yield getComputedStyleProperty("#testid", null, "color")),
|
||||
"rgb(255, 0, 0)", "red color is set.");
|
||||
}
|
|
@ -2028,6 +2028,14 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||
.tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
|
||||
background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
|
||||
}
|
||||
|
||||
.tab-throbber[busy] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
|
||||
}
|
||||
|
||||
.tab-throbber[progress] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove border between tab strip and navigation toolbar on Windows 10+ */
|
||||
|
|
|
@ -330,8 +330,10 @@ browser.jar:
|
|||
skin/classic/browser/tabbrowser/newtab-inverted-XPVista7.png (tabbrowser/newtab-inverted-XPVista7.png)
|
||||
skin/classic/browser/tabbrowser/newtab-inverted-XPVista7@2x.png (tabbrowser/newtab-inverted-XPVista7@2x.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/connecting@2x.png (tabbrowser/connecting@2x.png)
|
||||
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
|
||||
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/loading@2x.png (tabbrowser/loading@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)
|
||||
|
@ -346,6 +348,12 @@ browser.jar:
|
|||
skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end@2x.png (tabbrowser/tab-background-end@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start-preWin10.png (tabbrowser/tab-background-start-preWin10.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start-preWin10@2x.png (tabbrowser/tab-background-start-preWin10@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle-preWin10.png (tabbrowser/tab-background-middle-preWin10.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle-preWin10@2x.png (tabbrowser/tab-background-middle-preWin10@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end-preWin10.png (tabbrowser/tab-background-end-preWin10.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end-preWin10@2x.png (tabbrowser/tab-background-end-preWin10@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-overflow-indicator.png (../shared/tabbrowser/tab-overflow-indicator.png)
|
||||
skin/classic/browser/tabbrowser/pendingpaint.png (../shared/tabbrowser/pendingpaint.png)
|
||||
|
||||
|
@ -704,3 +712,9 @@ browser.jar:
|
|||
% override chrome://browser/skin/urlbar-history-dropmarker.png chrome://browser/skin/urlbar-history-dropmarker-preWin10.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/urlbar-history-dropmarker@2x.png chrome://browser/skin/urlbar-history-dropmarker-preWin10@2x.png os=WINNT osversion<=6.3
|
||||
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-start.png chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-start@2x.png chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-middle.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-end.png chrome://browser/skin/tabbrowser/tab-background-end-preWin10.png os=WINNT osversion<=6.3
|
||||
% override chrome://browser/skin/tabbrowser/tab-background-end@2x.png chrome://browser/skin/tabbrowser/tab-background-end-preWin10@2x.png os=WINNT osversion<=6.3
|
||||
|
|
После Ширина: | Высота: | Размер: 29 KiB |
Двоичные данные
browser/themes/windows/tabbrowser/loading.png
До Ширина: | Высота: | Размер: 10 KiB После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 39 KiB |
После Ширина: | Высота: | Размер: 802 B |
После Ширина: | Высота: | Размер: 2.8 KiB |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-end.png
До Ширина: | Высота: | Размер: 802 B После Ширина: | Высота: | Размер: 256 B |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-end@2x.png
До Ширина: | Высота: | Размер: 2.8 KiB После Ширина: | Высота: | Размер: 400 B |
После Ширина: | Высота: | Размер: 122 B |
После Ширина: | Высота: | Размер: 782 B |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-middle.png
До Ширина: | Высота: | Размер: 122 B После Ширина: | Высота: | Размер: 75 B |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-middle@2x.png
До Ширина: | Высота: | Размер: 782 B После Ширина: | Высота: | Размер: 86 B |
После Ширина: | Высота: | Размер: 814 B |
После Ширина: | Высота: | Размер: 2.9 KiB |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-start.png
До Ширина: | Высота: | Размер: 814 B После Ширина: | Высота: | Размер: 257 B |
Двоичные данные
browser/themes/windows/tabbrowser/tab-background-start@2x.png
До Ширина: | Высота: | Размер: 2.9 KiB После Ширина: | Высота: | Размер: 417 B |
|
@ -7,6 +7,7 @@ let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm")
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(window, "gChromeWin", function()
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -53,7 +54,9 @@ let Logins = {
|
|||
emptyBody.classList.add("hidden");
|
||||
|
||||
try {
|
||||
TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
|
||||
logins = Services.logins.getAllLogins();
|
||||
TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
|
||||
} catch(e) {
|
||||
// Master password was not entered
|
||||
debug("Master password permissions error: " + e);
|
||||
|
|
|
@ -4730,11 +4730,11 @@ pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,tes
|
|||
pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
|
||||
pref("urlclassifier.downloadBlockTable", "");
|
||||
pref("urlclassifier.downloadAllowTable", "");
|
||||
pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
|
||||
pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
|
||||
|
||||
// The table and update/gethash URLs for Safebrowsing phishing and malware
|
||||
// checks.
|
||||
pref("urlclassifier.trackingTable", "mozpub-track-digest256");
|
||||
pref("urlclassifier.trackingTable", "test-track-simple,mozpub-track-digest256");
|
||||
pref("browser.trackingprotection.updateURL", "https://tracking.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
pref("browser.trackingprotection.gethashURL", "https://tracking.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
|
||||
|
||||
|
|
|
@ -1344,7 +1344,9 @@ nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
|
|||
GetResultValueAt(mCompletedSelectionIndex, true, finalValue);
|
||||
nsAutoString inputValue;
|
||||
input->GetTextValue(inputValue);
|
||||
if (!finalValue.Equals(inputValue)) {
|
||||
nsAutoString completedValue;
|
||||
GetResultValueAt(mCompletedSelectionIndex, false, completedValue);
|
||||
if (completedValue.Equals(inputValue) && !finalValue.Equals(inputValue)) {
|
||||
value = finalValue;
|
||||
}
|
||||
// Note that if the user opens the popup, mouses over entries without
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Common front for various implementations of OSCrypto
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["OSCrypto"];
|
||||
|
||||
let OSCrypto = {};
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
Services.scriptloader.loadSubScript("resource://gre/modules/OSCrypto_win.js", this);
|
||||
} else {
|
||||
throw new Error("OSCrypto.jsm isn't supported on this platform");
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ctypes", "resource://gre/modules/ctypes.jsm");
|
||||
|
||||
function OSCrypto() {
|
||||
this._structs = {};
|
||||
this._functions = new Map();
|
||||
this._libs = new Map();
|
||||
this._structs.DATA_BLOB = new ctypes.StructType("DATA_BLOB",
|
||||
[
|
||||
{cbData: ctypes.uint32_t},
|
||||
{pbData: ctypes.uint8_t.ptr}
|
||||
]);
|
||||
|
||||
try {
|
||||
|
||||
this._libs.set("crypt32", ctypes.open("Crypt32"));
|
||||
this._libs.set("kernel32", ctypes.open("Kernel32"));
|
||||
|
||||
this._functions.set("CryptProtectData",
|
||||
this._libs.get("crypt32").declare("CryptProtectData",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uint32_t,
|
||||
this._structs.DATA_BLOB.ptr,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.uint32_t,
|
||||
this._structs.DATA_BLOB.ptr));
|
||||
|
||||
this._functions.set("CryptUnprotectData",
|
||||
this._libs.get("crypt32").declare("CryptUnprotectData",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uint32_t,
|
||||
this._structs.DATA_BLOB.ptr,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.voidptr_t,
|
||||
ctypes.uint32_t,
|
||||
this._structs.DATA_BLOB.ptr));
|
||||
this._functions.set("LocalFree",
|
||||
this._libs.get("kernel32").declare("LocalFree",
|
||||
ctypes.winapi_abi,
|
||||
ctypes.uint32_t,
|
||||
ctypes.voidptr_t));
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
this.finalize();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
OSCrypto.prototype = {
|
||||
/**
|
||||
* Decrypt an array of numbers using the windows CryptUnprotectData API.
|
||||
* @param {number[]} array - the encrypted array that needs to be decrypted.
|
||||
* @returns {string} the decryption of the array.
|
||||
*/
|
||||
decryptData(array) {
|
||||
let decryptedData = "";
|
||||
let encryptedData = ctypes.uint8_t.array(array.length)(array);
|
||||
let inData = new this._structs.DATA_BLOB(encryptedData.length, encryptedData);
|
||||
let outData = new this._structs.DATA_BLOB();
|
||||
let status = this._functions.get("CryptUnprotectData")(inData.address(), null,
|
||||
null, null, null, 0,
|
||||
outData.address());
|
||||
if (status === 0) {
|
||||
throw new Error("decryptData failed: " + status);
|
||||
}
|
||||
|
||||
// convert byte array to JS string.
|
||||
let len = outData.cbData;
|
||||
let decrypted = ctypes.cast(outData.pbData,
|
||||
ctypes.uint8_t.array(len).ptr).contents;
|
||||
for (let i = 0; i < decrypted.length; i++) {
|
||||
decryptedData += String.fromCharCode(decrypted[i]);
|
||||
}
|
||||
|
||||
this._functions.get("LocalFree")(outData.pbData);
|
||||
return decryptedData;
|
||||
},
|
||||
|
||||
/**
|
||||
* Encrypt a string using the windows CryptProtectData API.
|
||||
* @param {string} string - the string that is going to be encrypted.
|
||||
* @returns {number[]} the encrypted string encoded as an array of numbers.
|
||||
*/
|
||||
encryptData(string) {
|
||||
let encryptedData = [];
|
||||
let decryptedData = ctypes.uint8_t.array(string.length)();
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
decryptedData[i] = string.charCodeAt(i);
|
||||
}
|
||||
|
||||
let inData = new this._structs.DATA_BLOB(string.length, decryptedData);
|
||||
let outData = new this._structs.DATA_BLOB();
|
||||
let status = this._functions.get("CryptProtectData")(inData.address(), null,
|
||||
null, null, null, 0,
|
||||
outData.address());
|
||||
if (status === 0) {
|
||||
throw new Error("encryptData failed: " + status);
|
||||
}
|
||||
|
||||
// convert byte array to JS string.
|
||||
let len = outData.cbData;
|
||||
let encrypted = ctypes.cast(outData.pbData,
|
||||
ctypes.uint8_t.array(len).ptr).contents;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
encryptedData.push(encrypted[i]);
|
||||
}
|
||||
|
||||
this._functions.get("LocalFree")(outData.pbData);
|
||||
return encryptedData;
|
||||
},
|
||||
|
||||
/**
|
||||
* Must be invoked once after last use of any of the provided helpers.
|
||||
*/
|
||||
finalize() {
|
||||
this._structs = {};
|
||||
this._functions.clear();
|
||||
for (let lib of this._libs.values()) {
|
||||
try {
|
||||
lib.close();
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
this._libs.clear();
|
||||
},
|
||||
};
|
|
@ -41,10 +41,10 @@ EXTRA_PP_JS_MODULES += [
|
|||
|
||||
EXTRA_JS_MODULES += [
|
||||
'InsecurePasswordUtils.jsm',
|
||||
'LoginDoorhangers.jsm',
|
||||
'LoginHelper.jsm',
|
||||
'LoginManagerContent.jsm',
|
||||
'LoginRecipes.jsm',
|
||||
'OSCrypto.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
|
@ -56,10 +56,16 @@ else:
|
|||
'storage-json.js',
|
||||
]
|
||||
EXTRA_JS_MODULES += [
|
||||
'LoginDoorhangers.jsm',
|
||||
'LoginImport.jsm',
|
||||
'LoginStore.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'WINNT':
|
||||
EXTRA_JS_MODULES += [
|
||||
'OSCrypto_win.js',
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('**'):
|
||||
|
|
|
@ -133,7 +133,9 @@ function SearchSuggestionControllerWrapper(engine, searchToken,
|
|||
this._controller.maxRemoteResults = maxResults;
|
||||
let promise = this._controller.fetch(searchToken, inPrivateContext, engine);
|
||||
this._suggestions = [];
|
||||
this._success = false;
|
||||
this._promise = promise.then(results => {
|
||||
this._success = true;
|
||||
this._suggestions = (results ? results.remote : null) || [];
|
||||
}).catch(err => {
|
||||
// fetch() rejects its promise if there's a pending request.
|
||||
|
@ -164,6 +166,14 @@ SearchSuggestionControllerWrapper.prototype = {
|
|||
this._suggestions.shift()];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the number of fetched suggestions, or -1 if the fetching was
|
||||
* incomplete or failed.
|
||||
*/
|
||||
get resultsCount() {
|
||||
return this._success ? this._suggestions.length : -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops the fetch.
|
||||
*/
|
||||
|
|
|
@ -38,6 +38,8 @@ const PREF_SUGGEST_OPENPAGE = [ "suggest.openpage", true ];
|
|||
const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
|
||||
const PREF_SUGGEST_SEARCHES = [ "suggest.searches", true ];
|
||||
|
||||
const PREF_MAX_CHARS_FOR_SUGGEST = [ "maxCharsForSearchSuggestions", 20];
|
||||
|
||||
// Match type constants.
|
||||
// These indicate what type of search function we should be using.
|
||||
const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE;
|
||||
|
@ -416,6 +418,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
|
|||
store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
|
||||
store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
|
||||
store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
|
||||
store.maxCharsForSearchSuggestions = prefs.get(...PREF_MAX_CHARS_FOR_SUGGEST);
|
||||
|
||||
// If history is not set, onlyTyped value should be ignored.
|
||||
if (!store.suggestHistory) {
|
||||
|
@ -598,9 +601,11 @@ function makeActionURL(action, params) {
|
|||
* An nsIAutoCompleteSimpleResultListener.
|
||||
* @param autocompleteSearch
|
||||
* An nsIAutoCompleteSearch.
|
||||
* @param prohibitSearchSuggestions
|
||||
* Whether search suggestions are allowed for this search.
|
||||
*/
|
||||
function Search(searchString, searchParam, autocompleteListener,
|
||||
resultListener, autocompleteSearch) {
|
||||
resultListener, autocompleteSearch, prohibitSearchSuggestions) {
|
||||
// We want to store the original string for case sensitive searches.
|
||||
this._originalSearchString = searchString;
|
||||
this._trimmedOriginalSearchString = searchString.trim();
|
||||
|
@ -632,6 +637,8 @@ function Search(searchString, searchParam, autocompleteListener,
|
|||
this._trimmedOriginalSearchString.slice(pathIndex)
|
||||
);
|
||||
|
||||
this._prohibitSearchSuggestions = prohibitSearchSuggestions;
|
||||
|
||||
this._listener = autocompleteListener;
|
||||
this._autocompleteSearch = autocompleteSearch;
|
||||
|
||||
|
@ -909,18 +916,29 @@ Search.prototype = {
|
|||
}),
|
||||
|
||||
*_matchSearchSuggestions() {
|
||||
if (!this.hasBehavior("searches") || this._inPrivateWindow) {
|
||||
// Limit the string sent for search suggestions to a maximum length.
|
||||
let searchString = this._searchTokens.join(" ")
|
||||
.substr(0, Prefs.maxCharsForSearchSuggestions);
|
||||
// Avoid fetching suggestions if they are not required, private browsing
|
||||
// mode is enabled, or the search string may expose sensitive information.
|
||||
if (!this.hasBehavior("searches") || this._inPrivateWindow ||
|
||||
this._prohibitSearchSuggestionsFor(searchString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._searchSuggestionController =
|
||||
PlacesSearchAutocompleteProvider.getSuggestionController(
|
||||
this._searchTokens.join(" "),
|
||||
searchString,
|
||||
this._inPrivateWindow,
|
||||
Prefs.maxRichResults
|
||||
);
|
||||
let promise = this._searchSuggestionController.fetchCompletePromise
|
||||
.then(() => {
|
||||
if (this._searchSuggestionController.resultsCount >= 0 &&
|
||||
this._searchSuggestionController.resultsCount < 2) {
|
||||
// The original string is used to properly compare with the next search.
|
||||
this._lastLowResultsSearchSuggestion = this._originalSearchString;
|
||||
}
|
||||
while (this.pending && this._remoteMatchesCount < Prefs.maxRichResults) {
|
||||
let [match, suggestion] = this._searchSuggestionController.consume();
|
||||
if (!suggestion)
|
||||
|
@ -940,6 +958,28 @@ Search.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_prohibitSearchSuggestionsFor(searchString) {
|
||||
if (this._prohibitSearchSuggestions)
|
||||
return true;
|
||||
|
||||
// Suggestions for a single letter are unlikely to be useful.
|
||||
if (searchString.length < 2)
|
||||
return true;
|
||||
|
||||
let tokens = searchString.split(/\s/);
|
||||
|
||||
// The first token may be a whitelisted host.
|
||||
if (REGEXP_SINGLEWORD_HOST.test(tokens[0]) &&
|
||||
Services.uriFixup.isDomainWhitelisted(tokens[0], -1))
|
||||
return true;
|
||||
|
||||
// Disallow fetching search suggestions for strings looking like URLs, to
|
||||
// avoid disclosing information about networks or passwords.
|
||||
return tokens.some(token => {
|
||||
return ["/", "@", ":", "."].some(c => token.includes(c));
|
||||
});
|
||||
},
|
||||
|
||||
_matchKnownUrl: function* (conn) {
|
||||
// Hosts have no "/" in them.
|
||||
let lastSlashIndex = this._searchString.lastIndexOf("/");
|
||||
|
@ -1701,8 +1741,15 @@ UnifiedComplete.prototype = {
|
|||
// Note: We don't use previousResult to make sure ordering of results are
|
||||
// consistent. See bug 412730 for more details.
|
||||
|
||||
// If the previous search didn't fetch enough search suggestions, it's
|
||||
// unlikely a longer text would do.
|
||||
let prohibitSearchSuggestions =
|
||||
this._lastLowResultsSearchSuggestion &&
|
||||
searchString.length > this._lastLowResultsSearchSuggestion.length &&
|
||||
searchString.startsWith(this._lastLowResultsSearchSuggestion);
|
||||
|
||||
this._currentSearch = new Search(searchString, searchParam, listener,
|
||||
this, this);
|
||||
this, this, prohibitSearchSuggestions);
|
||||
|
||||
// If we are not enabled, we need to return now. Notice we need an empty
|
||||
// result regardless, so we still create the Search object.
|
||||
|
@ -1745,6 +1792,7 @@ UnifiedComplete.prototype = {
|
|||
TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, this);
|
||||
// Clear state now to avoid race conditions, see below.
|
||||
let search = this._currentSearch;
|
||||
this._lastLowResultsSearchSuggestion = search._lastLowResultsSearchSuggestion;
|
||||
delete this._currentSearch;
|
||||
|
||||
if (!notify)
|
||||
|
|
|
@ -414,3 +414,71 @@ add_task(function* mixup_frecency() {
|
|||
|
||||
yield cleanUpSuggestions();
|
||||
});
|
||||
|
||||
add_task(function* prohibit_suggestions() {
|
||||
Services.prefs.setBoolPref(SUGGEST_PREF, true);
|
||||
|
||||
yield check_autocomplete({
|
||||
search: "localhost",
|
||||
matches: [
|
||||
{
|
||||
uri: makeActionURI(("searchengine"), {
|
||||
engineName: ENGINE_NAME,
|
||||
input: "localhost foo",
|
||||
searchQuery: "localhost",
|
||||
searchSuggestion: "localhost foo",
|
||||
}),
|
||||
title: ENGINE_NAME,
|
||||
style: ["action", "searchengine"],
|
||||
icon: "",
|
||||
},
|
||||
{
|
||||
uri: makeActionURI(("searchengine"), {
|
||||
engineName: ENGINE_NAME,
|
||||
input: "localhost bar",
|
||||
searchQuery: "localhost",
|
||||
searchSuggestion: "localhost bar",
|
||||
}),
|
||||
title: ENGINE_NAME,
|
||||
style: ["action", "searchengine"],
|
||||
icon: "",
|
||||
},
|
||||
],
|
||||
});
|
||||
Services.prefs.setBoolPref("browser.fixup.domainwhitelist.localhost", true);
|
||||
do_register_cleanup(() => {
|
||||
Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
|
||||
});
|
||||
yield check_autocomplete({
|
||||
search: "localhost",
|
||||
matches: [],
|
||||
});
|
||||
|
||||
yield check_autocomplete({
|
||||
search: "1.2.3.4",
|
||||
matches: [],
|
||||
});
|
||||
yield check_autocomplete({
|
||||
search: "[2001::1]:30",
|
||||
matches: [],
|
||||
});
|
||||
yield check_autocomplete({
|
||||
search: "user:pass@test",
|
||||
matches: [],
|
||||
});
|
||||
yield check_autocomplete({
|
||||
search: "test/test",
|
||||
matches: [],
|
||||
});
|
||||
yield check_autocomplete({
|
||||
search: "data:text/plain,Content",
|
||||
matches: [],
|
||||
});
|
||||
|
||||
yield check_autocomplete({
|
||||
search: "a",
|
||||
matches: [],
|
||||
});
|
||||
|
||||
yield cleanUpSuggestions();
|
||||
});
|
||||
|
|
|
@ -8223,6 +8223,14 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "Sanitize: Time it takes to sanitize the open windows list (ms)"
|
||||
},
|
||||
"PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS": {
|
||||
"expires_in_version": "55",
|
||||
"kind": "exponential",
|
||||
"high": 60000,
|
||||
"n_buckets": 30,
|
||||
"extended_statistics_ok": true,
|
||||
"description": "How long getAllLogins() on about:logins takes for mobile users"
|
||||
},
|
||||
"PWMGR_BLOCKLIST_NUM_SITES": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
|
|
|
@ -214,6 +214,10 @@ this.SafeBrowsing = {
|
|||
const phishURL = "itisatrap.org/firefox/its-a-trap.html";
|
||||
const malwareURL = "itisatrap.org/firefox/its-an-attack.html";
|
||||
const unwantedURL = "itisatrap.org/firefox/unwanted.html";
|
||||
const trackerURLs = [
|
||||
"trackertest.org/",
|
||||
"itisatracker.org/",
|
||||
];
|
||||
|
||||
let update = "n:1000\ni:test-malware-simple\nad:1\n" +
|
||||
"a:1:32:" + malwareURL.length + "\n" +
|
||||
|
@ -224,6 +228,11 @@ this.SafeBrowsing = {
|
|||
update += "n:1000\ni:test-unwanted-simple\nad:1\n" +
|
||||
"a:1:32:" + unwantedURL.length + "\n" +
|
||||
unwantedURL;
|
||||
trackerURLs.forEach((trackerURL, i) => {
|
||||
update += "n:1000\ni:test-track-simple\nad:1\n" +
|
||||
"a:" + (i + 1) + ":32:" + trackerURL.length + "\n" +
|
||||
trackerURL;
|
||||
});
|
||||
log("addMozEntries:", update);
|
||||
|
||||
let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
|
||||
|
@ -238,7 +247,8 @@ this.SafeBrowsing = {
|
|||
};
|
||||
|
||||
try {
|
||||
db.beginUpdate(dummyListener, "test-malware-simple,test-phish-simple,test-unwanted-simple", "");
|
||||
let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple";
|
||||
db.beginUpdate(dummyListener, tables, "");
|
||||
db.beginStream("", "");
|
||||
db.updateStream(update);
|
||||
db.finishStream();
|
||||
|
|
|
@ -54,17 +54,20 @@ function cleanUp() {
|
|||
delFile("safebrowsing/test-phish-simple.sbstore");
|
||||
delFile("safebrowsing/test-malware-simple.sbstore");
|
||||
delFile("safebrowsing/test-unwanted-simple.sbstore");
|
||||
delFile("safebrowsing/test-track-simple.sbstore");
|
||||
delFile("safebrowsing/test-phish-simple.cache");
|
||||
delFile("safebrowsing/test-malware-simple.cache");
|
||||
delFile("safebrowsing/test-unwanted-simple.cache");
|
||||
delFile("safebrowsing/test-track-simple.cache");
|
||||
delFile("safebrowsing/test-phish-simple.pset");
|
||||
delFile("safebrowsing/test-malware-simple.pset");
|
||||
delFile("safebrowsing/test-unwanted-simple.pset");
|
||||
delFile("safebrowsing/test-track-simple.pset");
|
||||
delFile("testLarge.pset");
|
||||
delFile("testNoDelta.pset");
|
||||
}
|
||||
|
||||
var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple";
|
||||
var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple";
|
||||
|
||||
var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
|
||||
var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
|
||||
|
@ -144,8 +147,7 @@ function doSimpleUpdate(updateText, success, failure) {
|
|||
updateSuccess: function(requestedTimeout) { success(requestedTimeout); }
|
||||
};
|
||||
|
||||
dbservice.beginUpdate(listener,
|
||||
"test-phish-simple,test-malware-simple,test-unwanted-simple");
|
||||
dbservice.beginUpdate(listener, allTables);
|
||||
dbservice.beginStream("", "");
|
||||
dbservice.updateStream(updateText);
|
||||
dbservice.finishStream();
|
||||
|
@ -187,7 +189,7 @@ function doStreamUpdate(updateText, success, failure, downloadFailure) {
|
|||
downloadFailure = failure;
|
||||
}
|
||||
|
||||
streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple,test-unwanted-simple", "",
|
||||
streamUpdater.downloadUpdates(allTables, "",
|
||||
dataUpdate, success, failure, downloadFailure);
|
||||
}
|
||||
|
||||
|
|
|
@ -213,6 +213,20 @@ Sensitive information such as your Bugzilla credentials could be
|
|||
stolen if others have access to this file/machine.
|
||||
'''.strip()
|
||||
|
||||
MULTIPLE_VCT = '''
|
||||
*** WARNING ***
|
||||
|
||||
Multiple version-control-tools repositories are referenced in your
|
||||
Mercurial config. Extensions and other code within the
|
||||
version-control-tools repository could run with inconsistent results.
|
||||
|
||||
Please manually edit the following file to reference a single
|
||||
version-control-tools repository:
|
||||
|
||||
%s
|
||||
'''.lstrip()
|
||||
|
||||
|
||||
class MercurialSetupWizard(object):
|
||||
"""Command-line wizard to help users configure Mercurial."""
|
||||
|
||||
|
@ -404,6 +418,23 @@ class MercurialSetupWizard(object):
|
|||
|
||||
c.add_mozilla_host_fingerprints()
|
||||
|
||||
# References to multiple version-control-tools checkouts can confuse
|
||||
# version-control-tools, since various Mercurial extensions resolve
|
||||
# dependencies via __file__ and repos could reference another copy.
|
||||
seen_vct = set()
|
||||
for k, v in c.config.get('extensions', {}).items():
|
||||
if 'version-control-tools' not in v:
|
||||
continue
|
||||
|
||||
i = v.index('version-control-tools')
|
||||
vct = v[0:i + len('version-control-tools')]
|
||||
seen_vct.add(os.path.realpath(os.path.expanduser(vct)))
|
||||
|
||||
if len(seen_vct) > 1:
|
||||
print(MULTIPLE_VCT % c.config_path)
|
||||
|
||||
# At this point the config should be finalized.
|
||||
|
||||
b = StringIO()
|
||||
c.write(b)
|
||||
new_lines = [line.rstrip() for line in b.getvalue().splitlines()]
|
||||
|
|