diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml index c7160b0ccb23a58c5761b9723401f3b0037fc1f5..44f6ffead4c9d8dbf8959ef5629306d7b8514b2e 100644 --- a/b2g/config/aries/sources.xml +++ b/b2g/config/aries/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 8060d4d57d5e1d92627b34436e5e7e457fe70eb6..881d3c78a816157ebdc68aa58a05ccd55a33c02c 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 886d615791315c9ef63e4af4c3183e5eab29a8c4..98ff122f0466bd1da576e89abddc5f4c30ae9ab7 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/> diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 84e6706c9c896dc9b108925beaede65f47309a6e..d63adc161f5ee866f4b3a9124b835d577923dca1 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/> diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 0a33e6d3f4767f831786438278c8b83cb49e0207..15ec68bdad744cf19803deb7a9b3546f7482f75c 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml index 0b74b64cb2ae9201ab9b9cfd2cdcfd9023c16e59..e0ec32a104c14b881176955670ca391615d06139 100644 --- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 886d615791315c9ef63e4af4c3183e5eab29a8c4..98ff122f0466bd1da576e89abddc5f4c30ae9ab7 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/> diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index d5a1768478fc78ce9f814e73536456efdb57a075..8f678a4a410840daa026862b544ad4780b8cabb7 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index b8fe2d31cb3e62cd2a60fb273817e17bff2caada..520838b84ec0500035010580e4b8a254f7db9047 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "14e32276025b0310d3e89027320cf4b2a24cedfb", + "git_revision": "a8319543833b08e986fa3ed590faeb2624bf1683", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "8b7476489ad4a91eb41856b43934364fe8ef68be", + "revision": "4fc562ca01a3e8e17dd042af9076f1fe7bc8f91d", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 9e868f943d372d6b4275bfb6606db592219f56f4..2b96ad640746ae8c1587619f37725725ac1ce5fb 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/> diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml index 16cb7adb690671182cb35d3b6cc0e1d249898af8..b0f6a16d6f496bafdd6347baf3794e73d3bf5377 100644 --- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -15,7 +15,7 @@ <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 5d3abf9a5f6aba08711e41c362af0c81e8b097cb..ebd6eae7430d6fec6b1134c0f5fc735c9f48f3e3 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -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); diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 33805be1aedd4c519f762153f329bd51da3dc58e..3f185c5f31ea4a4e175eda29be0fb4d1ac90f4f4 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -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] diff --git a/browser/base/content/test/general/browser_autocomplete_edit_completed.js b/browser/base/content/test/general/browser_autocomplete_edit_completed.js new file mode 100644 index 0000000000000000000000000000000000000000..40179d3284f9285f03764abf6f6db91f4fe1fd0c --- /dev/null +++ b/browser/base/content/test/general/browser_autocomplete_edit_completed.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); +} diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js index 767489cf41ca8b716b93d74f0edf3260f4920e77..ccb69bfad00ced108ccbf66eee1cb48c03dcc99e 100644 --- a/browser/components/migration/ChromeProfileMigrator.js +++ b/browser/components/migration/ChromeProfileMigrator.js @@ -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}"); diff --git a/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data b/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data new file mode 100644 index 0000000000000000000000000000000000000000..914149c710a8c748b97543c541aaedfee74aa1f6 Binary files /dev/null and b/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data differ diff --git a/browser/components/migration/tests/unit/test_Chrome_passwords.js b/browser/components/migration/tests/unit/test_Chrome_passwords.js new file mode 100644 index 0000000000000000000000000000000000000000..dcd9a3aefd1f5e362bce38006ff53a1a44be21ea --- /dev/null +++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js @@ -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); + } +}); diff --git a/browser/components/migration/tests/unit/xpcshell.ini b/browser/components/migration/tests/unit/xpcshell.ini index eaef34cbad2a0473b481c6e3d5aff65d6185393a..036562b5e590948644ae9348be8ce8b2feed47a5 100644 --- a/browser/components/migration/tests/unit/xpcshell.ini +++ b/browser/components/migration/tests/unit/xpcshell.ini @@ -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" diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js index 34dbe7bc80fe469e93243ba27d547db79f23c70d..c148ad1dcd57b4edd662370cfeacfbd36f281b44 100644 --- a/browser/devtools/styleinspector/rule-view.js +++ b/browser/devtools/styleinspector/rule-view.js @@ -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 (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); - } - } + 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); + } + + 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); diff --git a/browser/devtools/styleinspector/test/browser.ini b/browser/devtools/styleinspector/test/browser.ini index e1e29c01da3af1d8615070a6be5a156657380876..fd6625ab68ce5247a1c02d120777771ddb5328d6 100644 --- a/browser/devtools/styleinspector/test/browser.ini +++ b/browser/devtools/styleinspector/test/browser.ini @@ -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] diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js new file mode 100644 index 0000000000000000000000000000000000000000..886d32490a9020d1c350c422b75af96de681a8fe --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.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."); +} diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js new file mode 100644 index 0000000000000000000000000000000000000000..583704906f403bcfaa0ce2bb0e7b77faccecc894 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js @@ -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; +} diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js new file mode 100644 index 0000000000000000000000000000000000000000..f15cf518ff0f1ce82fcef0171ee1e7bc8bfe1927 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js @@ -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."); +} diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js new file mode 100644 index 0000000000000000000000000000000000000000..72364c5d45573a6de9eeb526197264c45b874fbd --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js @@ -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."); +} diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index a805935a4ab0d7accd4a4601349d7e3fd8ff5117..1df1947100969ce0af5874adaed988106b9cec8d 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -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+ */ diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn index cece366c69aeb307cfff22960f5bff596c52533f..96b55fb892b1076d0b3cfdc917f29dea04146ec0 100644 --- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -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 diff --git a/browser/themes/windows/tabbrowser/connecting@2x.png b/browser/themes/windows/tabbrowser/connecting@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..97e2b2eb67ecc58e98dd839187143ab6870699e5 Binary files /dev/null and b/browser/themes/windows/tabbrowser/connecting@2x.png differ diff --git a/browser/themes/windows/tabbrowser/loading.png b/browser/themes/windows/tabbrowser/loading.png index ba54836e9834f2c495bcfb05738b56481b2dbed0..b886c73eff9239196fbe3db01116c220f726469b 100644 Binary files a/browser/themes/windows/tabbrowser/loading.png and b/browser/themes/windows/tabbrowser/loading.png differ diff --git a/browser/themes/windows/tabbrowser/loading@2x.png b/browser/themes/windows/tabbrowser/loading@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ac928c585372bd72707d895c37c9e11931b327bd Binary files /dev/null and b/browser/themes/windows/tabbrowser/loading@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png new file mode 100644 index 0000000000000000000000000000000000000000..fb353b17e70657d85e501a30ca6e2e6249e83a76 Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eefb6ac47cd820af31556f1d8c6631ad36396baa Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-end.png b/browser/themes/windows/tabbrowser/tab-background-end.png index fb353b17e70657d85e501a30ca6e2e6249e83a76..d68ea6da6a9834dfed10a88e73afa10ba1933099 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-end.png and b/browser/themes/windows/tabbrowser/tab-background-end.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-end@2x.png b/browser/themes/windows/tabbrowser/tab-background-end@2x.png index eefb6ac47cd820af31556f1d8c6631ad36396baa..8ed84ab37969668dcee44c1f5915cde2a48e59a0 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-end@2x.png and b/browser/themes/windows/tabbrowser/tab-background-end@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png new file mode 100644 index 0000000000000000000000000000000000000000..51e066c2e9c6d71f5ce4846b1b030d2f111127ef Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b26cb95de6ab3231f676a4c939a2bbf210557771 Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-middle.png b/browser/themes/windows/tabbrowser/tab-background-middle.png index 51e066c2e9c6d71f5ce4846b1b030d2f111127ef..faaf7e38e5d9726af460735604497ee0e8eed164 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-middle.png and b/browser/themes/windows/tabbrowser/tab-background-middle.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-middle@2x.png b/browser/themes/windows/tabbrowser/tab-background-middle@2x.png index b26cb95de6ab3231f676a4c939a2bbf210557771..c9d245f4f4459fe4e1265354b87f47b1e3a3ce32 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-middle@2x.png and b/browser/themes/windows/tabbrowser/tab-background-middle@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png new file mode 100644 index 0000000000000000000000000000000000000000..cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bbfc77dd189d6885201c4d9e923836485cfd8ad1 Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-start.png b/browser/themes/windows/tabbrowser/tab-background-start.png index cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b..d1f0b5561034d68884c0c6e48f0697da4da86474 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-start.png and b/browser/themes/windows/tabbrowser/tab-background-start.png differ diff --git a/browser/themes/windows/tabbrowser/tab-background-start@2x.png b/browser/themes/windows/tabbrowser/tab-background-start@2x.png index bbfc77dd189d6885201c4d9e923836485cfd8ad1..e860275a6435b42e181b6de2e60a0be01b031e55 100644 Binary files a/browser/themes/windows/tabbrowser/tab-background-start@2x.png and b/browser/themes/windows/tabbrowser/tab-background-start@2x.png differ diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js index a0e41de3a15e4e721a01acdb18da9d70c77e1177..49682a0d62379100dd4c0838324fcba68d80e0cf 100644 --- a/mobile/android/chrome/content/aboutLogins.js +++ b/mobile/android/chrome/content/aboutLogins.js @@ -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); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 56174c037a0fba3426385ae5d4a45407b06046cf..7c4e5c6fdebd39d0d459b3a3aa288d9556d5e99e 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4737,11 +4737,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"); diff --git a/testing/taskcluster/tasks/branches/base_jobs.yml b/testing/taskcluster/tasks/branches/base_jobs.yml index 02ece18afd2359524f34b7f80e4bdf67f86e893e..240080db4cd10adcb47f1a5139eb10ac54fa71e0 100644 --- a/testing/taskcluster/tasks/branches/base_jobs.yml +++ b/testing/taskcluster/tasks/branches/base_jobs.yml @@ -110,6 +110,8 @@ tests: allowed_build_tasks: tasks/builds/b2g_desktop_opt.yml: task: tasks/tests/b2g_gaia_js_integration_tests.yml + tasks/builds/mulet_linux.yml: + task: tasks/tests/mulet_gaia_js_integration_tests.yml gaia-linter: allowed_build_tasks: tasks/builds/b2g_desktop_opt.yml: diff --git a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml index c69c5e1f4a9c1c476082e114296aa739efae4545..a7a880137b834eade4d855ca8f807f748a9abf9e 100644 --- a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml +++ b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml @@ -1,7 +1,6 @@ --- $inherits: from: 'tasks/test.yml' -reruns: 2 task: metadata: name: '[TC] Mulet Gaia JS Integration Test' @@ -18,9 +17,9 @@ task: --config-file b2g/gaia_integration_config.py --config-file ./mozharness_configs/gaia_integration_override.py --config-file ./mozharness_configs/remove_executables.py - --installer-url {{build_url}} --no-pull - --test-url {{tests_url}} + --installer-url {{build_url}} + --test-packages-url {{test_packages_url}} --download-symbols ondemand --total-chunk {{total_chunks}} --this-chunk {{chunk}} @@ -35,8 +34,11 @@ task: extra: chunks: total: 20 + treeherderEnv: + - production + - staging treeherder: groupName: Gaia JS Integration Test - groupSymbol: tc-Gij + groupSymbol: Gij symbol: '{{chunk}}' productName: b2g diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp index 2fc86b9570eb838d0e993561851d2902e24188f6..fa23861de14b1097313c33bd0648c02739d62219 100644 --- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp +++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp @@ -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 diff --git a/toolkit/components/passwordmgr/OSCrypto.jsm b/toolkit/components/passwordmgr/OSCrypto.jsm new file mode 100644 index 0000000000000000000000000000000000000000..306e6b4b13bf331c94dd48375c2af22c826a58ee --- /dev/null +++ b/toolkit/components/passwordmgr/OSCrypto.jsm @@ -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"); +} diff --git a/toolkit/components/passwordmgr/OSCrypto_win.js b/toolkit/components/passwordmgr/OSCrypto_win.js new file mode 100644 index 0000000000000000000000000000000000000000..61d4deb87a13d286ed3fff9376e864140acd5bed --- /dev/null +++ b/toolkit/components/passwordmgr/OSCrypto_win.js @@ -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(); + }, +}; diff --git a/toolkit/components/passwordmgr/moz.build b/toolkit/components/passwordmgr/moz.build index 08c8dce981a262e11b1b70649dedf48e6b4e94c1..fc6aa6ae442d17f404a658a45c1931bb1f9cfd00 100644 --- a/toolkit/components/passwordmgr/moz.build +++ b/toolkit/components/passwordmgr/moz.build @@ -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('**'): diff --git a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm index 2a1a274be27c5c96fedc55ef808318a3fb87f150..00342aa56d973fd26c570f73bac26ae83df167e6 100644 --- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm +++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm @@ -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. */ diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js index a1f1f66230d4e64cceb26b7bd37765f024a165f3..85a84260282a363e2d2ca8c09a64d32f01b0336f 100644 --- a/toolkit/components/places/UnifiedComplete.js +++ b/toolkit/components/places/UnifiedComplete.js @@ -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) diff --git a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js index 290d44c2b212580d2734d880af077b04d453a687..6d51c045de43c95b31b18e1ff4d8717507c28c2d 100644 --- a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js @@ -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(); +}); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 8287e4b0e25353011e0d5994bdefd60be799747d..73b66cf3d74da02806b725c36c9877921df158ae 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -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", diff --git a/toolkit/components/url-classifier/SafeBrowsing.jsm b/toolkit/components/url-classifier/SafeBrowsing.jsm index 8d6bbfe9fdd3dd76e48ed6de5bc5cf008809638c..9038a9c02d90a22fa906506aa4016d8dd568e172 100644 --- a/toolkit/components/url-classifier/SafeBrowsing.jsm +++ b/toolkit/components/url-classifier/SafeBrowsing.jsm @@ -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(); diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js index 7f2a4ce9ec530606a936a06904d88e8fe9207668..90475235e077d3ed1fb22b3d3857f88f3b22cd05 100644 --- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -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); } diff --git a/tools/mercurial/hgsetup/wizard.py b/tools/mercurial/hgsetup/wizard.py index 548323b1f1528def29bb72e52331994031e12162..98f83cf49123ef38cb925af576835bd5385446cd 100644 --- a/tools/mercurial/hgsetup/wizard.py +++ b/tools/mercurial/hgsetup/wizard.py @@ -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()]