merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-07-28 16:11:49 +02:00
Родитель 768abb5b11 9e192ff73a
Коммит 815a154fae
43 изменённых файлов: 1130 добавлений и 53 удалений

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

@ -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}");

Двоичный файл не отображается.

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

@ -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

Двоичные данные
browser/themes/windows/tabbrowser/connecting@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 29 KiB

Двоичные данные
browser/themes/windows/tabbrowser/loading.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 10 KiB

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Двоичные данные
browser/themes/windows/tabbrowser/loading@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 39 KiB

Двоичные данные
browser/themes/windows/tabbrowser/tab-background-end-preWin10.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 802 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 802 B

После

Ширина:  |  Высота:  |  Размер: 256 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 2.8 KiB

После

Ширина:  |  Высота:  |  Размер: 400 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 122 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 782 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 122 B

После

Ширина:  |  Высота:  |  Размер: 75 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 782 B

После

Ширина:  |  Высота:  |  Размер: 86 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 814 B

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.9 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 814 B

После

Ширина:  |  Высота:  |  Размер: 257 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 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()]