From 37a9218a670e44b7486cb9123664cc9711db75fb Mon Sep 17 00:00:00 2001 From: Joseph Yeh Date: Tue, 19 Apr 2016 23:27:14 -0700 Subject: [PATCH 01/14] Bug 530999 - Show site favicons in login manager UI; r=MattN MozReview-Commit-ID: Lh9M3scMTwX --HG-- rename : toolkit/themes/windows/global/passwordmgr.css => toolkit/themes/shared/passwordmgr.css extra : rebase_source : efe99425d69162add061a7824cb1ae3355abce2e --- .../passwordmgr/content/passwordManager.js | 39 ++++++++++++++++++- toolkit/themes/osx/global/jar.mn | 1 - toolkit/themes/shared/jar.inc.mn | 1 + toolkit/themes/shared/non-mac.jar.inc.mn | 1 - .../{osx/global => shared}/passwordmgr.css | 13 +++++++ toolkit/themes/windows/global/passwordmgr.css | 13 ------- 6 files changed, 52 insertions(+), 16 deletions(-) rename toolkit/themes/{osx/global => shared}/passwordmgr.css (53%) delete mode 100644 toolkit/themes/windows/global/passwordmgr.css diff --git a/toolkit/components/passwordmgr/content/passwordManager.js b/toolkit/components/passwordmgr/content/passwordManager.js index 87bc391a10f5..9628a07b66ff 100644 --- a/toolkit/components/passwordmgr/content/passwordManager.js +++ b/toolkit/components/passwordmgr/content/passwordManager.js @@ -5,6 +5,13 @@ /*** =================== SAVED SIGNONS CODE =================== ***/ Cu.import("resource://gre/modules/AppConstants.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", + "resource://gre/modules/PlacesUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", + "resource://gre/modules/DeferredTask.jsm"); var kSignonBundle; var showingPasswords = false; @@ -44,13 +51,43 @@ function setFilter(aFilterString) { } var signonsTreeView = { + // Keep track of which favicons we've fetched or started fetching. + // Maps a login origin to a favicon URL. + _faviconMap: new Map(), _filterSet : [], + // Coalesce invalidations to avoid repeated flickering. + _invalidateTask: new DeferredTask(() => { + signonsTree.treeBoxObject.invalidateColumn(signonsTree.columns.siteCol); + }, 10), _lastSelectedRanges : [], selection: null, rowCount : 0, setTree : function(tree) {}, - getImageSrc : function(row,column) {}, + getImageSrc : function(row, column) { + if (column.element.getAttribute("id") !== "siteCol") { + return ""; + } + + const signon = this._filterSet.length ? this._filterSet[row] : signons[row]; + + // We already have the favicon URL or we started to fetch (value is null). + if (this._faviconMap.has(signon.hostname)) { + return this._faviconMap.get(signon.hostname); + } + + // Record the fact that we already starting fetching a favicon for this + // origin in order to avoid multiple requests for the same origin. + this._faviconMap.set(signon.hostname, null); + + PlacesUtils.promiseFaviconLinkUrl(signon.hostname) + .then(faviconURI => { + this._faviconMap.set(signon.hostname, faviconURI.spec); + this._invalidateTask.arm(); + }).catch(Cu.reportError); + + return ""; + }, getProgressMode : function(row,column) {}, getCellValue : function(row,column) {}, getCellText : function(row,column) { diff --git a/toolkit/themes/osx/global/jar.mn b/toolkit/themes/osx/global/jar.mn index b56f579ef64f..5aaaccfd1284 100644 --- a/toolkit/themes/osx/global/jar.mn +++ b/toolkit/themes/osx/global/jar.mn @@ -29,7 +29,6 @@ toolkit.jar: * skin/classic/global/notification.css skin/classic/global/netError.css skin/classic/global/numberbox.css - skin/classic/global/passwordmgr.css skin/classic/global/popup.css skin/classic/global/preferences.css skin/classic/global/progressmeter.css diff --git a/toolkit/themes/shared/jar.inc.mn b/toolkit/themes/shared/jar.inc.mn index ef714965e621..7744323f320c 100644 --- a/toolkit/themes/shared/jar.inc.mn +++ b/toolkit/themes/shared/jar.inc.mn @@ -46,6 +46,7 @@ toolkit.jar: skin/classic/global/in-content/dropdown.svg (../../shared/in-content/dropdown.svg) skin/classic/global/in-content/help-glyph.svg (../../shared/in-content/help-glyph.svg) skin/classic/global/in-content/radio.svg (../../shared/in-content/radio.svg) + skin/classic/global/passwordmgr.css (../../shared/passwordmgr.css) skin/classic/global/reader/RM-Close-24x24.svg (../../shared/reader/RM-Close-24x24.svg) skin/classic/global/reader/RM-Minus-24x24.svg (../../shared/reader/RM-Minus-24x24.svg) skin/classic/global/reader/RM-Plus-24x24.svg (../../shared/reader/RM-Plus-24x24.svg) diff --git a/toolkit/themes/shared/non-mac.jar.inc.mn b/toolkit/themes/shared/non-mac.jar.inc.mn index cd8eb1dc6338..775c44188d03 100644 --- a/toolkit/themes/shared/non-mac.jar.inc.mn +++ b/toolkit/themes/shared/non-mac.jar.inc.mn @@ -18,7 +18,6 @@ skin/classic/global/filefield.css (../../windows/global/filefield.css) skin/classic/global/globalBindings.xml (../../windows/global/globalBindings.xml) skin/classic/global/linkTree.css (../../windows/global/linkTree.css) - skin/classic/global/passwordmgr.css (../../windows/global/passwordmgr.css) skin/classic/global/progressmeter.css (../../windows/global/progressmeter.css) skin/classic/global/resizer.css (../../windows/global/resizer.css) skin/classic/global/richlistbox.css (../../windows/global/richlistbox.css) diff --git a/toolkit/themes/osx/global/passwordmgr.css b/toolkit/themes/shared/passwordmgr.css similarity index 53% rename from toolkit/themes/osx/global/passwordmgr.css rename to toolkit/themes/shared/passwordmgr.css index b3c7b59a3558..9cb48d1c2fc2 100644 --- a/toolkit/themes/osx/global/passwordmgr.css +++ b/toolkit/themes/shared/passwordmgr.css @@ -11,3 +11,16 @@ .actionButtons { margin: 0px 3px 6px 3px !important; } + +treechildren::-moz-tree-image(siteCol) { + list-style-image: url(chrome://mozapps/skin/places/defaultFavicon.png); + width: 16px; + height: 16px; + -moz-margin-end: 5px; +} + +@media (min-resolution: 1.1dppx) { + treechildren::-moz-tree-image(siteCol) { + list-style-image: url(chrome://mozapps/skin/places/defaultFavicon@2x.png); + } +} diff --git a/toolkit/themes/windows/global/passwordmgr.css b/toolkit/themes/windows/global/passwordmgr.css deleted file mode 100644 index b3c7b59a3558..000000000000 --- a/toolkit/themes/windows/global/passwordmgr.css +++ /dev/null @@ -1,13 +0,0 @@ -/* 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/. */ - -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); - -.contentPane { - margin: 9px 8px 5px 8px; -} - -.actionButtons { - margin: 0px 3px 6px 3px !important; -} From 48c944de6a47b839d4b62b53a5773016ba300174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 20 Apr 2016 09:33:41 +0200 Subject: [PATCH 02/14] Bug 1265017 - Make Bookmarks > "Other Bookmarks" open a sub menu and move it after "Bookmarks Toolbar". r=mak --- browser/base/content/browser-menubar.inc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index 6a60a60143b5..0099d178c335 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -468,13 +468,20 @@ onpopupshowing="if (!this.parentNode._placesView) new PlacesMenu(event, 'place:folder=TOOLBAR');"/> + + + - - From 5a69d2648673c4617243eb55281cf8e3d52cb0c3 Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Wed, 20 Apr 2016 00:44:28 -0700 Subject: [PATCH 03/14] No bug - passwordManager.js style consistency fixes MozReview-Commit-ID: 1fZXlESnmMC --HG-- extra : rebase_source : c03e93d91357d9ae24157ea9980430a0bbb9a916 --- .../passwordmgr/content/passwordManager.js | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/toolkit/components/passwordmgr/content/passwordManager.js b/toolkit/components/passwordmgr/content/passwordManager.js index 9628a07b66ff..be24dd145b86 100644 --- a/toolkit/components/passwordmgr/content/passwordManager.js +++ b/toolkit/components/passwordmgr/content/passwordManager.js @@ -7,11 +7,10 @@ Cu.import("resource://gre/modules/AppConstants.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", + "resource://gre/modules/PlacesUtils.jsm"); var kSignonBundle; var showingPasswords = false; @@ -54,17 +53,17 @@ var signonsTreeView = { // Keep track of which favicons we've fetched or started fetching. // Maps a login origin to a favicon URL. _faviconMap: new Map(), - _filterSet : [], + _filterSet: [], // Coalesce invalidations to avoid repeated flickering. _invalidateTask: new DeferredTask(() => { signonsTree.treeBoxObject.invalidateColumn(signonsTree.columns.siteCol); }, 10), - _lastSelectedRanges : [], + _lastSelectedRanges: [], selection: null, - rowCount : 0, - setTree : function(tree) {}, - getImageSrc : function(row, column) { + rowCount: 0, + setTree(tree) {}, + getImageSrc(row, column) { if (column.element.getAttribute("id") !== "siteCol") { return ""; } @@ -88,9 +87,9 @@ var signonsTreeView = { return ""; }, - getProgressMode : function(row,column) {}, - getCellValue : function(row,column) {}, - getCellText : function(row,column) { + getProgressMode(row, column) {}, + getCellValue(row, column) {}, + getCellText(row, column) { var time; var signon = this._filterSet.length ? this._filterSet[row] : signons[row]; switch (column.id) { @@ -117,25 +116,25 @@ var signonsTreeView = { return ""; } }, - isEditable : function(row, col) { + isEditable(row, col) { if (col.id == "userCol" || col.id == "passwordCol") { return true; } return false; }, - isSeparator : function(index) { return false; }, - isSorted : function() { return false; }, - isContainer : function(index) { return false; }, - cycleHeader : function(column) {}, - getRowProperties : function(row) { return ""; }, - getColumnProperties : function(column) { return ""; }, - getCellProperties : function(row,column) { + isSeparator(index) { return false; }, + isSorted() { return false; }, + isContainer(index) { return false; }, + cycleHeader(column) {}, + getRowProperties(row) { return ""; }, + getColumnProperties(column) { return ""; }, + getCellProperties(row, column) { if (column.element.getAttribute("id") == "siteCol") return "ltr"; return ""; }, - setCellText : function(row, col, value) { + setCellText(row, col, value) { // If there is a filter, _filterSet needs to be used, otherwise signons is used. let table = signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons; function _editLogin(field) { @@ -261,7 +260,7 @@ function AskUserShowPasswords() { } function FinalizeSignonDeletions(syncNeeded) { - for (var s=0; s Date: Wed, 20 Apr 2016 00:44:52 -0700 Subject: [PATCH 04/14] Bug 1033450 - Consolidate safebrowsing prefs in all.js. r=francois MozReview-Commit-ID: EA2H08dlCLl --HG-- extra : rebase_source : 9dfa4af8b71b48c7841b3d5d07c106e24ae0ee05 --- b2g/app/b2g.js | 38 ---------------------------------- browser/app/profile/firefox.js | 30 --------------------------- mobile/android/app/mobile.js | 35 ------------------------------- modules/libpref/init/all.js | 37 +++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 103 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index e6ae009e2aec..f626d1535dbc 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -346,48 +346,10 @@ pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240" pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240" #ifdef MOZ_SAFE_BROWSING -pref("browser.safebrowsing.enabled", true); -// Prevent loading of pages identified as malware -pref("browser.safebrowsing.malware.enabled", true); pref("browser.safebrowsing.downloads.enabled", true); pref("browser.safebrowsing.downloads.remote.enabled", true); -pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); -pref("browser.safebrowsing.downloads.remote.url", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.downloads.remote.block_dangerous", true); -pref("browser.safebrowsing.downloads.remote.block_dangerous_host", true); -pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", false); -pref("browser.safebrowsing.downloads.remote.block_uncommon", false); -pref("browser.safebrowsing.debug", false); - -pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); -pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); -pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); - -pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.id", "Firefox"); - -// Tables for application reputation. -pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar"); - -// The number of random entries to send with a gethash request. -pref("urlclassifier.gethashnoise", 4); - -// Gethash timeout for Safebrowsing. -pref("urlclassifier.gethash.timeout_ms", 5000); - -// If an urlclassifier table has not been updated in this number of seconds, -// a gethash request will be forced to check that the result is still in -// the database. -pref("urlclassifier.max-complete-age", 2700); - -// Tracking protection -pref("privacy.trackingprotection.enabled", false); -pref("privacy.trackingprotection.pbmode.enabled", true); - #endif // True if this is the first time we are showing about:firstrun diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index be14d7bd5c5f..d3449d1e9e69 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -780,26 +780,8 @@ pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region pref("gecko.handlerService.allowRegisterFromDifferentHost", false); #ifdef MOZ_SAFE_BROWSING -pref("browser.safebrowsing.enabled", true); -pref("browser.safebrowsing.malware.enabled", true); pref("browser.safebrowsing.downloads.enabled", true); pref("browser.safebrowsing.downloads.remote.enabled", true); -pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); -pref("browser.safebrowsing.downloads.remote.url", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.downloads.remote.block_dangerous", true); -pref("browser.safebrowsing.downloads.remote.block_dangerous_host", true); -pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", false); -pref("browser.safebrowsing.downloads.remote.block_uncommon", false); -pref("browser.safebrowsing.debug", false); - -pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); -pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); -pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); - -pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); #ifdef MOZILLA_OFFICIAL // Normally the "client ID" sent in updates is appinfo.name, but for @@ -811,18 +793,6 @@ pref("browser.safebrowsing.id", "navclient-auto-ffox"); // pages on phishing/malware hits. (bug 399233) pref("urlclassifier.alternate_error_page", "blocked"); -// The number of random entries to send with a gethash request. -pref("urlclassifier.gethashnoise", 4); - -// Gethash timeout for Safebrowsing. -pref("urlclassifier.gethash.timeout_ms", 5000); - -// If an urlclassifier table has not been updated in this number of seconds, -// a gethash request will be forced to check that the result is still in -// the database. -pref("urlclassifier.max-complete-age", 2700); -// Tables for application reputation. -pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar"); #ifdef XP_WIN // Only download the whitelist on Windows, since the whitelist is // only useful for suppressing remote lookups for signed binaries which we can diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index dd6501f17753..860f7a09a5b2 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -305,9 +305,6 @@ pref("browser.search.order.US.3", "chrome://browser/locale/region.properties"); // disable updating pref("browser.search.update", false); -// enable tracking protection for private browsing -pref("privacy.trackingprotection.pbmode.enabled", true); - // disable search suggestions by default pref("browser.search.suggest.enabled", false); pref("browser.search.suggest.prompted", false); @@ -649,46 +646,14 @@ pref("media.mediasource.enabled", true); pref("image.downscale-during-decode.enabled", true); #ifdef MOZ_SAFE_BROWSING -pref("browser.safebrowsing.enabled", true); -pref("browser.safebrowsing.malware.enabled", true); pref("browser.safebrowsing.downloads.enabled", false); pref("browser.safebrowsing.downloads.remote.enabled", false); -pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); -pref("browser.safebrowsing.downloads.remote.url", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.downloads.remote.block_dangerous", true); -pref("browser.safebrowsing.downloads.remote.block_dangerous_host", true); -pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", false); -pref("browser.safebrowsing.downloads.remote.block_uncommon", false); -pref("browser.safebrowsing.debug", false); - -pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); -pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); -pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); - -pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.id", @MOZ_APP_UA_NAME@); // Name of the about: page contributed by safebrowsing to handle display of error // pages on phishing/malware hits. (bug 399233) pref("urlclassifier.alternate_error_page", "blocked"); - -// The number of random entries to send with a gethash request. -pref("urlclassifier.gethashnoise", 4); - -// Gethash timeout for Safebrowsing. -pref("urlclassifier.gethash.timeout_ms", 5000); - -// If an urlclassifier table has not been updated in this number of seconds, -// a gethash request will be forced to check that the result is still in -// the database. -pref("urlclassifier.max-complete-age", 2700); - -// Tables for application reputation. -pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar"); #endif // True if this is the first time we are showing about:firstrun diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 483fbd89ffa6..75837402dfab 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4956,6 +4956,7 @@ pref("dom.inter-app-communication-api.enabled", false); // Disable mapped array buffer by default. pref("dom.mapped_arraybuffer.enabled", false); +#ifdef MOZ_SAFE_BROWSING // The tables used for Safebrowsing phishing and malware checks. pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple"); pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple"); @@ -4968,6 +4969,41 @@ pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simpl pref("urlclassifier.trackingTable", "test-track-simple,mozstd-track-digest256"); pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256"); +// Tables for application reputation. +pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar"); + +// The number of random entries to send with a gethash request. +pref("urlclassifier.gethashnoise", 4); + +// Gethash timeout for Safebrowsing. +pref("urlclassifier.gethash.timeout_ms", 5000); + +// If an urlclassifier table has not been updated in this number of seconds, +// a gethash request will be forced to check that the result is still in +// the database. +pref("urlclassifier.max-complete-age", 2700); + +pref("browser.safebrowsing.enabled", true); +pref("browser.safebrowsing.malware.enabled", true); + +pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); +pref("browser.safebrowsing.downloads.remote.url", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.downloads.remote.block_dangerous", true); +pref("browser.safebrowsing.downloads.remote.block_dangerous_host", true); +pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", false); +pref("browser.safebrowsing.downloads.remote.block_uncommon", false); +pref("browser.safebrowsing.debug", false); + +pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); +pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); + +pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); +pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); +pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); + + // The table and global pref for blocking access to sites forbidden by policy pref("browser.safebrowsing.forbiddenURIs.enabled", false); pref("urlclassifier.forbiddenTable", "test-forbid-simple"); @@ -4990,6 +5026,7 @@ pref("browser.safebrowsing.provider.mozilla.lists.mozfull.description", "mozfull // Allow users to ignore Safe Browsing warnings. pref("browser.safebrowsing.allowOverride", true); +#endif // Turn off Spatial navigation by default. pref("snav.enabled", false); From 9187edc78864d8a68e9b33d39f415d33c156b995 Mon Sep 17 00:00:00 2001 From: Andrzej Hunt Date: Wed, 13 Apr 2016 11:16:51 -0700 Subject: [PATCH 05/14] Bug 1264381 - Add comment explaining lack of SQLite forward-compatibility r=mcomella MozReview-Commit-ID: J84s62BsoPJ --HG-- extra : rebase_source : 024fbb89590f35beeec577028e4f199a363d836e --- .../gecko/tests/testBrowserDatabaseHelperUpgrades.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserDatabaseHelperUpgrades.java b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserDatabaseHelperUpgrades.java index 4ee0e0c5fda6..eec5c4b33f20 100644 --- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserDatabaseHelperUpgrades.java +++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testBrowserDatabaseHelperUpgrades.java @@ -47,6 +47,11 @@ import java.util.ArrayList; * * Note: when the application starts for testing, it may need to upgrade the database from your existing version. If * this fails, the application will crash and the test may fail to start. * + * IMPORTANT: + * Test DBs must be created on the oldest version of Android that is currently supported. SQLite + * is not forwards compatible. E.g. uploading a DB created on a 6.0 device will cause failures + * when robocop tests running on 4.3 are unable to load it. + * * Implementation inspired by: * http://riggaroo.co.za/automated-testing-sqlite-database-upgrades-android/ */ From b887fa24f17929eabcce1ac74e446c40935378c6 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 15 Apr 2016 15:30:57 +0100 Subject: [PATCH 06/14] Bug 1198181 - Part 1 - Separate functional and styling classes for download item buttons. r=jaws MozReview-Commit-ID: 8ijvQtWRuxz --HG-- extra : rebase_source : 439b0dc11db26d7577d19f73641325f67d2d3940 --- .../components/downloads/content/download.xml | 8 +- .../downloads/allDownloadsViewOverlay.css | 56 ++++---- browser/themes/linux/downloads/downloads.css | 28 ++-- .../osx/downloads/allDownloadsViewOverlay.css | 112 +++++++--------- browser/themes/osx/downloads/downloads.css | 124 ++++++++---------- .../downloads/allDownloadsViewOverlay.css | 56 ++++---- .../themes/windows/downloads/downloads.css | 40 +++--- 7 files changed, 184 insertions(+), 240 deletions(-) diff --git a/browser/components/downloads/content/download.xml b/browser/components/downloads/content/download.xml index 024d23a195a6..e31e4dd7b62b 100644 --- a/browser/components/downloads/content/download.xml +++ b/browser/components/downloads/content/download.xml @@ -47,20 +47,20 @@ xbl:inherits="value=status,tooltiptext=statusTip"/> - - - - diff --git a/browser/themes/linux/downloads/allDownloadsViewOverlay.css b/browser/themes/linux/downloads/allDownloadsViewOverlay.css index 075fd37841fc..3efa41f9f235 100644 --- a/browser/themes/linux/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/linux/downloads/allDownloadsViewOverlay.css @@ -16,85 +16,77 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -@itemFocused@ .downloadButton.downloadConfirmBlock, -@itemFocused@ .downloadButton.downloadCancel { +@itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 80px, 16px, 64px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock, -@itemFocused@:hover .downloadButton.downloadCancel { +@itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 96px, 16px, 80px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, -@itemFocused@:hover .downloadButton.downloadCancel:hover { +@itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 112px, 16px, 96px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock:active, -@itemFocused@:hover .downloadButton.downloadCancel:active { +@itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 16px, 112px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@item@:hover .downloadButton.downloadShow { +@item@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } -@item@:hover .downloadButton.downloadShow:hover { +@item@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } -@item@:hover .downloadButton.downloadShow:active { +@item@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } -@itemFocused@ .downloadButton.downloadShow { +@itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 80px, 32px, 64px); } -@itemFocused@:hover .downloadButton.downloadShow { +@itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } -@itemFocused@:hover .downloadButton.downloadShow:hover { +@itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } -@itemFocused@:hover .downloadButton.downloadShow:active { +@itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } -@itemFocused@ .downloadButton.downloadRetry { +@itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 80px, 48px, 64px); } -@itemFocused@:hover .downloadButton.downloadRetry { +@itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 96px, 48px, 80px); } -@itemFocused@:hover .downloadButton.downloadRetry:hover { +@itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 112px, 48px, 96px); } -@itemFocused@:hover .downloadButton.downloadRetry:active { +@itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 128px, 48px, 112px); } diff --git a/browser/themes/linux/downloads/downloads.css b/browser/themes/linux/downloads/downloads.css index 96fa4ec88ef7..6965140bed92 100644 --- a/browser/themes/linux/downloads/downloads.css +++ b/browser/themes/linux/downloads/downloads.css @@ -63,45 +63,41 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:hover { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:active { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } diff --git a/browser/themes/osx/downloads/allDownloadsViewOverlay.css b/browser/themes/osx/downloads/allDownloadsViewOverlay.css index 318f0a5d4a8b..ef7c3289c11b 100644 --- a/browser/themes/osx/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/osx/downloads/allDownloadsViewOverlay.css @@ -12,86 +12,78 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -@itemFocused@ .downloadButton.downloadConfirmBlock, -@itemFocused@ .downloadButton.downloadCancel { +@itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 80px, 16px, 64px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock, -@itemFocused@:hover .downloadButton.downloadCancel { +@itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 96px, 16px, 80px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, -@itemFocused@:hover .downloadButton.downloadCancel:hover { +@itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 112px, 16px, 96px); } -@itemFocused@:hover .downloadButton.downloadConfirmBlock:active, -@itemFocused@:hover .downloadButton.downloadCancel:active { +@itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 16px, 112px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@item@:hover .downloadButton.downloadShow { +@item@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } -@item@:hover .downloadButton.downloadShow:hover { +@item@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } -@item@:hover .downloadButton.downloadShow:active { +@item@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } -@itemFocused@ .downloadButton.downloadShow { +@itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 80px, 32px, 64px); } -@itemFocused@:hover .downloadButton.downloadShow { +@itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } -@itemFocused@:hover .downloadButton.downloadShow:hover { +@itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } -@itemFocused@:hover .downloadButton.downloadShow:active { +@itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } -@itemFocused@ .downloadButton.downloadRetry { +@itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 80px, 48px, 64px); } -@itemFocused@:hover .downloadButton.downloadRetry { +@itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 96px, 48px, 80px); } -@itemFocused@:hover .downloadButton.downloadRetry:hover { +@itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 112px, 48px, 96px); } -@itemFocused@:hover .downloadButton.downloadRetry:active { +@itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 128px, 48px, 112px); } @@ -104,86 +96,78 @@ height: 16px; } - .downloadButton.downloadConfirmBlock, - .downloadButton.downloadCancel { + .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 32px, 0px); } - @item@:hover .downloadButton.downloadConfirmBlock, - @item@:hover .downloadButton.downloadCancel { + @item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 64px, 32px, 32px); } - @item@:hover .downloadButton.downloadConfirmBlock:hover, - @item@:hover .downloadButton.downloadCancel:hover { + @item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 96px, 32px, 64px); } - @item@:hover .downloadButton.downloadConfirmBlock:active, - @item@:hover .downloadButton.downloadCancel:active { + @item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 32px, 96px); } - @itemFocused@ .downloadButton.downloadConfirmBlock, - @itemFocused@ .downloadButton.downloadCancel { + @itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 160px, 32px, 128px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock, - @itemFocused@:hover .downloadButton.downloadCancel { + @itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 192px, 32px, 160px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, - @itemFocused@:hover .downloadButton.downloadCancel:hover { + @itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 224px, 32px, 192px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock:active, - @itemFocused@:hover .downloadButton.downloadCancel:active { + @itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 256px, 32px, 224px); } - .downloadButton.downloadShow { + .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 32px, 64px, 0px); } - @item@:hover .downloadButton.downloadShow { + @item@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 64px, 64px, 32px); } - @item@:hover .downloadButton.downloadShow:hover { + @item@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(32px, 96px, 64px, 64px); } - @item@:hover .downloadButton.downloadShow:active { + @item@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(32px, 128px, 64px, 96px); } - @itemFocused@ .downloadButton.downloadShow { + @itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 160px, 64px, 128px); } - @itemFocused@:hover .downloadButton.downloadShow { + @itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 192px, 64px, 160px); } - @itemFocused@:hover .downloadButton.downloadShow:hover { + @itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(32px, 224px, 64px, 192px); } - @itemFocused@:hover .downloadButton.downloadShow:active { + @itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(32px, 256px, 64px, 224px); } - .downloadButton.downloadRetry { + .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 32px, 96px, 0px); } - @item@:hover .downloadButton.downloadRetry { + @item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 64px, 96px, 32px); } - @item@:hover .downloadButton.downloadRetry:hover { + @item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(64px, 96px, 96px, 64px); } - @item@:hover .downloadButton.downloadRetry:active { + @item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(64px, 128px, 96px, 96px); } - @itemFocused@ .downloadButton.downloadRetry { + @itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 160px, 96px, 128px); } - @itemFocused@:hover .downloadButton.downloadRetry { + @itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 192px, 96px, 160px); } - @itemFocused@:hover .downloadButton.downloadRetry:hover { + @itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(64px, 224px, 96px, 192px); } - @itemFocused@:hover .downloadButton.downloadRetry:active { + @itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(64px, 256px, 96px, 224px); } } diff --git a/browser/themes/osx/downloads/downloads.css b/browser/themes/osx/downloads/downloads.css index 6e98bb8ac2fb..dca0b778b554 100644 --- a/browser/themes/osx/downloads/downloads.css +++ b/browser/themes/osx/downloads/downloads.css @@ -64,89 +64,81 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -@keyfocus@ @itemFocused@ .downloadButton.downloadConfirmBlock, -@keyfocus@ @itemFocused@ .downloadButton.downloadCancel { +@keyfocus@ @itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 80px, 16px, 64px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 96px, 16px, 80px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel:hover { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 112px, 16px, 96px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock:active, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel:active { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 16px, 112px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow { +@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } -@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow:hover { +@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } -@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow:active { +@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } -@keyfocus@ @itemFocused@ .downloadButton.downloadShow { +@keyfocus@ @itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 80px, 32px, 64px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadShow { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow, +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:hover, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadShow:hover { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover, +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } -@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:active, -@keyfocus@ @itemFocused@:hover .downloadButton.downloadShow:active { +@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active, +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } -@keyfocus@ @itemFocused@ .downloadButton.downloadRetry { +@keyfocus@ @itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 80px, 48px, 64px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 96px, 48px, 80px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry:hover { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 112px, 48px, 96px); } -@keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry:active { +@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 128px, 48px, 112px); } @@ -159,89 +151,81 @@ height: 16px; } - .downloadButton.downloadConfirmBlock, - .downloadButton.downloadCancel { + .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 32px, 0px); } - @item@:hover .downloadButton.downloadConfirmBlock, - @item@:hover .downloadButton.downloadCancel { + @item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 64px, 32px, 32px); } - @item@:hover .downloadButton.downloadConfirmBlock:hover, - @item@:hover .downloadButton.downloadCancel:hover { + @item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 96px, 32px, 64px); } - @item@:hover .downloadButton.downloadConfirmBlock:active, - @item@:hover .downloadButton.downloadCancel:active { + @item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 32px, 96px); } - @keyfocus@ @itemFocused@ .downloadButton.downloadConfirmBlock, - @keyfocus@ @itemFocused@ .downloadButton.downloadCancel { + @keyfocus@ @itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 160px, 32px, 128px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 192px, 32px, 160px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel:hover { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 224px, 32px, 192px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadConfirmBlock:active, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadCancel:active { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 256px, 32px, 224px); } - .downloadButton.downloadShow { + .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 32px, 64px, 0px); } - @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow { + @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 64px, 64px, 32px); } - @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow:hover { + @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover { -moz-image-region: rect(32px, 96px, 64px, 64px); } - @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadShow:active { + @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active { -moz-image-region: rect(32px, 128px, 64px, 96px); } - @keyfocus@ @itemFocused@ .downloadButton.downloadShow { + @keyfocus@ @itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 160px, 64px, 128px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadShow { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow, + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 192px, 64px, 160px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:hover, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadShow:hover { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover, + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(32px, 224px, 64px, 192px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:active, - @keyfocus@ @itemFocused@:hover .downloadButton.downloadShow:active { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active, + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(32px, 256px, 64px, 224px); } - .downloadButton.downloadRetry { + .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 32px, 96px, 0px); } - @item@:hover .downloadButton.downloadRetry { + @item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 64px, 96px, 32px); } - @item@:hover .downloadButton.downloadRetry:hover { + @item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(64px, 96px, 96px, 64px); } - @item@:hover .downloadButton.downloadRetry:active { + @item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(64px, 128px, 96px, 96px); } - @keyfocus@ @itemFocused@ .downloadButton.downloadRetry { + @keyfocus@ @itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 160px, 96px, 128px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(64px, 192px, 96px, 160px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry:hover { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(64px, 224px, 96px, 192px); } - @keyfocus@ @itemFocused@:hover .downloadButton.downloadRetry:active { + @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(64px, 256px, 96px, 224px); } } diff --git a/browser/themes/windows/downloads/allDownloadsViewOverlay.css b/browser/themes/windows/downloads/allDownloadsViewOverlay.css index 754341c43f7d..20ee5ab82a66 100644 --- a/browser/themes/windows/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/windows/downloads/allDownloadsViewOverlay.css @@ -44,90 +44,82 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@item@:hover .downloadButton.downloadShow { +@item@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } -@item@:hover .downloadButton.downloadShow:hover { +@item@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } -@item@:hover .downloadButton.downloadShow:active { +@item@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } @media (-moz-os-version: windows-xp) { - @itemFocused@ .downloadButton.downloadConfirmBlock, - @itemFocused@ .downloadButton.downloadCancel { + @itemFocused@ .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 80px, 16px, 64px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock, - @itemFocused@:hover .downloadButton.downloadCancel { + @itemFocused@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 96px, 16px, 80px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock:hover, - @itemFocused@:hover .downloadButton.downloadCancel:hover { + @itemFocused@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 112px, 16px, 96px); } - @itemFocused@:hover .downloadButton.downloadConfirmBlock:active, - @itemFocused@:hover .downloadButton.downloadCancel:active { + @itemFocused@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 128px, 16px, 112px); } - @itemFocused@ .downloadButton.downloadShow { + @itemFocused@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 80px, 32px, 64px); } - @itemFocused@:hover .downloadButton.downloadShow { + @itemFocused@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } - @itemFocused@:hover .downloadButton.downloadShow:hover { + @itemFocused@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } - @itemFocused@:hover .downloadButton.downloadShow:active { + @itemFocused@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } - @itemFocused@ .downloadButton.downloadRetry { + @itemFocused@ .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 80px, 48px, 64px); } - @itemFocused@:hover .downloadButton.downloadRetry { + @itemFocused@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 96px, 48px, 80px); } - @itemFocused@:hover .downloadButton.downloadRetry:hover { + @itemFocused@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 112px, 48px, 96px); } - @itemFocused@:hover .downloadButton.downloadRetry:active { + @itemFocused@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 128px, 48px, 112px); } } diff --git a/browser/themes/windows/downloads/downloads.css b/browser/themes/windows/downloads/downloads.css index 297b23e493f0..e87ec4d37f3c 100644 --- a/browser/themes/windows/downloads/downloads.css +++ b/browser/themes/windows/downloads/downloads.css @@ -148,65 +148,61 @@ /*** Button icons ***/ -.downloadButton.downloadConfirmBlock, -.downloadButton.downloadCancel { +.downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -@item@:hover .downloadButton.downloadConfirmBlock, -@item@:hover .downloadButton.downloadCancel { +@item@:hover .downloadButton.downloadIconCancel { -moz-image-region: rect(0px, 32px, 16px, 16px); } -@item@:hover .downloadButton.downloadConfirmBlock:hover, -@item@:hover .downloadButton.downloadCancel:hover { +@item@:hover .downloadButton.downloadIconCancel:hover { -moz-image-region: rect(0px, 48px, 16px, 32px); } -@item@:hover .downloadButton.downloadConfirmBlock:active, -@item@:hover .downloadButton.downloadCancel:active { +@item@:hover .downloadButton.downloadIconCancel:active { -moz-image-region: rect(0px, 64px, 16px, 48px); } -.downloadButton.downloadShow { +.downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -@item@:hover .downloadButton.downloadShow { +@item@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } -@item@:hover .downloadButton.downloadShow:hover { +@item@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } -@item@:hover .downloadButton.downloadShow:active { +@item@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } @media (-moz-os-version: windows-xp) { - @keyfocus@ @itemFinished@:hover .downloadButton.downloadShow { + @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } - @keyfocus@ @itemFinished@:hover .downloadButton.downloadShow:hover { + @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } - @keyfocus@ @itemFinished@:hover .downloadButton.downloadShow:active { + @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:hover { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 112px, 32px, 96px); } - @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadShow:active { + @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 128px, 32px, 112px); } } -.downloadButton.downloadRetry { +.downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -@item@:hover .downloadButton.downloadRetry { +@item@:hover .downloadButton.downloadIconRetry { -moz-image-region: rect(32px, 32px, 48px, 16px); } -@item@:hover .downloadButton.downloadRetry:hover { +@item@:hover .downloadButton.downloadIconRetry:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -@item@:hover .downloadButton.downloadRetry:active { +@item@:hover .downloadButton.downloadIconRetry:active { -moz-image-region: rect(32px, 64px, 48px, 48px); } From 916f9900024bf5233bb17e07c6b82fa44683b1bc Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 15 Apr 2016 15:37:22 +0100 Subject: [PATCH 07/14] Bug 1198181 - Part 2 - Unify the code that updates the state of download items. r=jaws MozReview-Commit-ID: KptYcAGbMG2 --HG-- extra : rebase_source : dadc1863d877de730107b12ff063c8695cf8c90d --- browser/components/downloads/DownloadsViewUI.jsm | 5 +++++ .../downloads/content/allDownloadsViewOverlay.js | 4 +--- browser/components/downloads/content/downloads.js | 9 +-------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/browser/components/downloads/DownloadsViewUI.jsm b/browser/components/downloads/DownloadsViewUI.jsm index aa6117e74f37..d26add83a29d 100644 --- a/browser/components/downloads/DownloadsViewUI.jsm +++ b/browser/components/downloads/DownloadsViewUI.jsm @@ -132,6 +132,11 @@ this.DownloadsViewUI.DownloadElementShell.prototype = { } } + // When a block is confirmed, the removal of blocked data will not trigger a + // state change for the download, so this class must be updated here. + this.element.classList.toggle("temporary-block", + !!this.download.hasBlockedData); + // The progress bar is only displayed for in-progress downloads. if (this.download.hasProgress) { this.element.setAttribute("progressmode", "normal"); diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.js b/browser/components/downloads/content/allDownloadsViewOverlay.js index 087ea7b652b3..6e22b007fd1f 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.js +++ b/browser/components/downloads/content/allDownloadsViewOverlay.js @@ -298,9 +298,7 @@ HistoryDownloadElementShell.prototype = { }, onStateChanged() { - this.element.setAttribute("image", this.image); - this.element.setAttribute("state", - DownloadsCommon.stateOfDownload(this.download)); + this._updateState(); if (this.element.selected) { goUpdateDownloadCommands(); diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index a50c118f70e5..f5229f8ed9f6 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -1035,17 +1035,10 @@ DownloadsViewItem.prototype = { _element: null, onStateChanged() { - this.element.setAttribute("image", this.image); - this.element.setAttribute("state", - DownloadsCommon.stateOfDownload(this.download)); + this._updateState(); }, onChanged() { - // This cannot be placed within onStateChanged because - // when a download goes from hasBlockedData to !hasBlockedData - // it will still remain in the same state. - this.element.classList.toggle("temporary-block", - !!this.download.hasBlockedData); this._updateProgress(); }, From 139f20eff959435c0ddb7a147f95b2d2c1b7a4fc Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 15 Apr 2016 15:44:33 +0100 Subject: [PATCH 08/14] Bug 1198181 - Part 3 - Showing the unblock dialog should be the visible action for verdicts other than malware. r=past MozReview-Commit-ID: GJNojR8L3Mx --HG-- extra : rebase_source : 2626cf280548ff60b4c5cf316513757082617e28 --- browser/components/downloads/DownloadsViewUI.jsm | 8 ++++++++ browser/components/downloads/content/download.xml | 3 +++ browser/components/downloads/content/downloads.css | 13 ++++++++++++- browser/themes/linux/downloads/downloads.css | 9 +++++++++ browser/themes/osx/downloads/downloads.css | 6 ++++++ browser/themes/shared/downloads/downloads.inc.css | 1 + 6 files changed, 39 insertions(+), 1 deletion(-) diff --git a/browser/components/downloads/DownloadsViewUI.jsm b/browser/components/downloads/DownloadsViewUI.jsm index d26add83a29d..a5c4995e4048 100644 --- a/browser/components/downloads/DownloadsViewUI.jsm +++ b/browser/components/downloads/DownloadsViewUI.jsm @@ -112,6 +112,14 @@ this.DownloadsViewUI.DownloadElementShell.prototype = { this.element.setAttribute("state", DownloadsCommon.stateOfDownload(this.download)); + if (this.download.error && + this.download.error.becauseBlockedByReputationCheck) { + this.element.setAttribute("verdict", + this.download.error.reputationCheckVerdict); + } else { + this.element.removeAttribute("verdict"); + } + // Since state changed, reset the time left estimation. this.lastEstimatedSecondsLeft = Infinity; diff --git a/browser/components/downloads/content/download.xml b/browser/components/downloads/content/download.xml index e31e4dd7b62b..245b142c0745 100644 --- a/browser/components/downloads/content/download.xml +++ b/browser/components/downloads/content/download.xml @@ -63,6 +63,9 @@ + diff --git a/browser/components/downloads/content/downloads.css b/browser/components/downloads/content/downloads.css index 3eb49a4603a7..b60345dd68e2 100644 --- a/browser/components/downloads/content/downloads.css +++ b/browser/components/downloads/content/downloads.css @@ -118,11 +118,22 @@ richlistitem.download button { .downloadCancel, /* Blocked (dirty) downloads that have not been confirmed and - have temporary data. */ + have temporary data, for the Malware case. */ .download-state:not( [state="8"] /* Blocked (dirty) */) .downloadConfirmBlock, .download-state[state="8"]:not(.temporary-block) .downloadConfirmBlock, +.download-state[state="8"].temporary-block:not([verdict="Malware"]) + .downloadConfirmBlock, + +/* Blocked (dirty) downloads that have not been confirmed and + have temporary data, for cases other than Malware. */ +.download-state:not( [state="8"] /* Blocked (dirty) */) + .downloadUnblock, +.download-state[state="8"]:not(.temporary-block) + .downloadUnblock, +.download-state[state="8"].temporary-block[verdict="Malware"] + .downloadUnblock, .download-state:not(:-moz-any([state="2"], /* Failed */ [state="3"]) /* Canceled */) diff --git a/browser/themes/linux/downloads/downloads.css b/browser/themes/linux/downloads/downloads.css index 6965140bed92..a607ec0d0b3c 100644 --- a/browser/themes/linux/downloads/downloads.css +++ b/browser/themes/linux/downloads/downloads.css @@ -79,6 +79,15 @@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } +@itemNotFinished@:hover .downloadButton.downloadIconShow { + -moz-image-region: rect(16px, 32px, 32px, 16px); +} +@itemNotFinished@:hover .downloadButton.downloadIconShow:hover { + -moz-image-region: rect(16px, 48px, 32px, 32px); +} +@itemNotFinished@:hover .downloadButton.downloadIconShow:active { + -moz-image-region: rect(16px, 64px, 32px, 48px); +} @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 96px, 32px, 80px); } diff --git a/browser/themes/osx/downloads/downloads.css b/browser/themes/osx/downloads/downloads.css index dca0b778b554..ef182e55c97a 100644 --- a/browser/themes/osx/downloads/downloads.css +++ b/browser/themes/osx/downloads/downloads.css @@ -92,12 +92,15 @@ .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } +@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow { -moz-image-region: rect(16px, 32px, 32px, 16px); } +@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:hover, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover { -moz-image-region: rect(16px, 48px, 32px, 32px); } +@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:active, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active { -moz-image-region: rect(16px, 64px, 32px, 48px); } @@ -179,12 +182,15 @@ .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 32px, 64px, 0px); } + @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow { -moz-image-region: rect(32px, 64px, 64px, 32px); } + @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:hover, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover { -moz-image-region: rect(32px, 96px, 64px, 64px); } + @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:active, @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active { -moz-image-region: rect(32px, 128px, 64px, 96px); } diff --git a/browser/themes/shared/downloads/downloads.inc.css b/browser/themes/shared/downloads/downloads.inc.css index ab9c5c42a1ef..eda6a4a331e8 100644 --- a/browser/themes/shared/downloads/downloads.inc.css +++ b/browser/themes/shared/downloads/downloads.inc.css @@ -8,6 +8,7 @@ %define notKeyfocus #downloadsPanel:not([keyfocus]) %define item richlistitem[type="download"] %define itemFinished @item@[state="1"] +%define itemNotFinished @item@:not([state="1"]) %define itemFocused #downloadsListBox:focus > @item@[selected] /*** Panel and outer controls ***/ From 68b3b45f09377feba0cd12a0c65de18df9e4ed7c Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Tue, 19 Apr 2016 11:26:47 +0100 Subject: [PATCH 09/14] Bug 1198181 - Part 4 - Differentiate the icons for blocked downloads depending on the verdict. r=past MozReview-Commit-ID: 1wvRJdJ3NhM --HG-- extra : rebase_source : 7eb23e83f767a651a3a7e821c01cffd1f7c9a4a5 --- browser/components/downloads/DownloadsViewUI.jsm | 14 +++++++++++++- .../linux/downloads/allDownloadsViewOverlay.css | 10 +++++++++- browser/themes/linux/downloads/downloads.css | 10 +++++++++- .../osx/downloads/allDownloadsViewOverlay.css | 8 ++++++++ browser/themes/osx/downloads/downloads.css | 8 ++++++++ .../windows/downloads/allDownloadsViewOverlay.css | 8 ++++++++ browser/themes/windows/downloads/downloads.css | 8 ++++++++ 7 files changed, 63 insertions(+), 3 deletions(-) diff --git a/browser/components/downloads/DownloadsViewUI.jsm b/browser/components/downloads/DownloadsViewUI.jsm index a5c4995e4048..642a8db873b3 100644 --- a/browser/components/downloads/DownloadsViewUI.jsm +++ b/browser/components/downloads/DownloadsViewUI.jsm @@ -17,6 +17,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", + "resource://gre/modules/Downloads.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", "resource://gre/modules/DownloadUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", @@ -233,7 +235,17 @@ this.DownloadsViewUI.DownloadElementShell.prototype = { } else if (this.download.error.becauseBlockedByParentalControls) { stateLabel = s.stateBlockedParentalControls; } else if (this.download.error.becauseBlockedByReputationCheck) { - stateLabel = s.stateDirty; + switch (this.download.error.reputationCheckVerdict) { + case Downloads.Error.BLOCK_VERDICT_UNCOMMON: + stateLabel = s.blockedUncommon; + break; + case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED: + stateLabel = s.blockedPotentiallyUnwanted; + break; + default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE + stateLabel = s.blockedMalware; + break; + } } else { stateLabel = s.stateFailed; } diff --git a/browser/themes/linux/downloads/allDownloadsViewOverlay.css b/browser/themes/linux/downloads/allDownloadsViewOverlay.css index 3efa41f9f235..92ea9939d6c0 100644 --- a/browser/themes/linux/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/linux/downloads/allDownloadsViewOverlay.css @@ -11,7 +11,15 @@ } .blockedIcon { - list-style-image: url("moz-icon://stock/gtk-dialog-error?size=dialog"); + list-style-image: url("moz-icon://stock/gtk-dialog-error?size=32"); +} + +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=32"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("moz-icon://stock/gtk-dialog-info?size=32"); } /*** Button icons ***/ diff --git a/browser/themes/linux/downloads/downloads.css b/browser/themes/linux/downloads/downloads.css index a607ec0d0b3c..d20a1b5e83ff 100644 --- a/browser/themes/linux/downloads/downloads.css +++ b/browser/themes/linux/downloads/downloads.css @@ -37,7 +37,15 @@ } .blockedIcon { - list-style-image: url("moz-icon://stock/gtk-dialog-error?size=dialog"); + list-style-image: url("moz-icon://stock/gtk-dialog-error?size=32"); +} + +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("moz-icon://stock/gtk-dialog-warning?size=32"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("moz-icon://stock/gtk-dialog-info?size=32"); } .downloadButton:focus > .button-box { diff --git a/browser/themes/osx/downloads/allDownloadsViewOverlay.css b/browser/themes/osx/downloads/allDownloadsViewOverlay.css index ef7c3289c11b..666830fb4da1 100644 --- a/browser/themes/osx/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/osx/downloads/allDownloadsViewOverlay.css @@ -10,6 +10,14 @@ --downloads-item-height: 6em; } +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/warning-32.png"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/information-32.png"); +} + /*** Button icons ***/ .downloadButton.downloadIconCancel { diff --git a/browser/themes/osx/downloads/downloads.css b/browser/themes/osx/downloads/downloads.css index ef182e55c97a..aba4d28c6957 100644 --- a/browser/themes/osx/downloads/downloads.css +++ b/browser/themes/osx/downloads/downloads.css @@ -47,6 +47,14 @@ outline-offset: -2px; } +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/warning-32.png"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/information-32.png"); +} + /*** Highlighted list items ***/ @keyfocus@ @itemFocused@, diff --git a/browser/themes/windows/downloads/allDownloadsViewOverlay.css b/browser/themes/windows/downloads/allDownloadsViewOverlay.css index 20ee5ab82a66..331d36ad3f0e 100644 --- a/browser/themes/windows/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/windows/downloads/allDownloadsViewOverlay.css @@ -10,6 +10,14 @@ --downloads-item-height: 6em; } +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/Warning.png"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/information-32.png"); +} + /*** Highlighted list items ***/ @media not all and (-moz-os-version: windows-xp) { diff --git a/browser/themes/windows/downloads/downloads.css b/browser/themes/windows/downloads/downloads.css index e87ec4d37f3c..0249478225ee 100644 --- a/browser/themes/windows/downloads/downloads.css +++ b/browser/themes/windows/downloads/downloads.css @@ -96,6 +96,14 @@ border: 1px dotted ThreeDDarkShadow; } +@item@[verdict="PotentiallyUnwanted"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/Warning.png"); +} + +@item@[verdict="Uncommon"] .blockedIcon { + list-style-image: url("chrome://global/skin/icons/information-32.png"); +} + /*** Highlighted list items ***/ @keyfocus@ @itemFocused@ { From e0a1fb4878ce8628c2916ac9e85f771c4c431579 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Wed, 20 Apr 2016 11:16:00 +0100 Subject: [PATCH 10/14] Bug 1198181 - Part 5 - Change the unblock dialog actions based on the verdict. r=past MozReview-Commit-ID: 3NkvpI9EqpB --HG-- extra : rebase_source : 029bde9cc35128aea9c2fd61ab1d42b27ad78822 --- .../components/downloads/DownloadsCommon.jsm | 24 ++++++++++++------- .../components/downloads/DownloadsViewUI.jsm | 19 +++++++++++++++ .../content/allDownloadsViewOverlay.js | 7 +----- .../components/downloads/content/downloads.js | 7 +----- .../browser_confirm_unblock_download.js | 4 ++-- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index b2cbbd0168dd..869791b9de28 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -528,29 +528,36 @@ this.DownloadsCommon = { * @param aOwnerWindow * The window with which this action is associated. * - * @return True to unblock the file, false to keep the user safe and - * cancel the operation. + * @return {Promise} + * @resolves String representing the action that should be executed: + * - "unblock" to allow the download without opening the file. + * - "confirmBlock" to delete the blocked data permanently. + * - "cancel" to do nothing and cancel the operation. */ confirmUnblockDownload: Task.async(function* (aVerdict, aOwnerWindow) { let s = DownloadsCommon.strings; let title = s.unblockHeader; let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) + - (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) + - Ci.nsIPrompt.BUTTON_POS_1_DEFAULT; + (Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1); let type = ""; let message = s.unblockTip; - let okButton = s.unblockButtonContinue; - let cancelButton = s.unblockButtonCancel; + let unblockButton = s.unblockButtonContinue; + let confirmBlockButton = s.unblockButtonCancel; switch (aVerdict) { case Downloads.Error.BLOCK_VERDICT_UNCOMMON: type = s.unblockTypeUncommon; + buttonFlags += (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) + + Ci.nsIPrompt.BUTTON_POS_0_DEFAULT; break; case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED: type = s.unblockTypePotentiallyUnwanted; + buttonFlags += (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) + + Ci.nsIPrompt.BUTTON_POS_2_DEFAULT; break; default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE type = s.unblockTypeMalware; + buttonFlags += Ci.nsIPrompt.BUTTON_POS_1_DEFAULT; break; } @@ -580,8 +587,9 @@ this.DownloadsCommon = { // The ordering of the ok/cancel buttons is used this way to allow "cancel" // to have the same result as hitting the ESC or Close button (see bug 345067). let rv = Services.prompt.confirmEx(aOwnerWindow, title, message, buttonFlags, - okButton, cancelButton, null, null, {}); - return (rv == 0); + unblockButton, null, confirmBlockButton, + null, {}); + return ["unblock", "cancel", "confirmBlock"][rv]; }), }; diff --git a/browser/components/downloads/DownloadsViewUI.jsm b/browser/components/downloads/DownloadsViewUI.jsm index 642a8db873b3..ee40d8266960 100644 --- a/browser/components/downloads/DownloadsViewUI.jsm +++ b/browser/components/downloads/DownloadsViewUI.jsm @@ -264,6 +264,25 @@ this.DownloadsViewUI.DownloadElementShell.prototype = { return { text, tip: tip || text }; }, + /** + * Shows the appropriate unblock dialog based on the verdict, and executes the + * action selected by the user in the dialog, which may involve unblocking, + * opening or removing the file. + * + * @param window + * The window to which the dialog should be anchored. + */ + confirmUnblock(window) { + let verdict = this.download.error.reputationCheckVerdict; + DownloadsCommon.confirmUnblockDownload(verdict, window).then(action => { + if (action == "unblock") { + return this.download.unblock(); + } else if (action == "confirmBlock") { + return this.download.confirmBlock(); + } + }).catch(Cu.reportError); + }, + /** * Returns the name of the default command to use for the current state of the * download, when there is a double click or another default interaction. If diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.js b/browser/components/downloads/content/allDownloadsViewOverlay.js index 6e22b007fd1f..9b5cefc42d7c 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.js +++ b/browser/components/downloads/content/allDownloadsViewOverlay.js @@ -377,12 +377,7 @@ HistoryDownloadElementShell.prototype = { }, downloadsCmd_unblock() { - let verdict = this.download.error.reputationCheckVerdict; - DownloadsCommon.confirmUnblockDownload(verdict, window).then(confirmed => { - if (confirmed) { - return this.download.unblock(); - } - }).catch(Cu.reportError); + this.confirmUnblock(window); }, // Returns whether or not the download handled by this shell should diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index f5229f8ed9f6..f0d5501ec928 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -1092,12 +1092,7 @@ DownloadsViewItem.prototype = { downloadsCmd_unblock() { DownloadsPanel.hidePanel(); - let verdict = this.download.error.reputationCheckVerdict; - DownloadsCommon.confirmUnblockDownload(verdict, window).then(confirmed => { - if (confirmed) { - return this.download.unblock(); - } - }).catch(Cu.reportError); + this.confirmUnblock(window); }, downloadsCmd_open() { diff --git a/browser/components/downloads/test/browser/browser_confirm_unblock_download.js b/browser/components/downloads/test/browser/browser_confirm_unblock_download.js index a40d3f6fda8a..0aee6ce16af1 100644 --- a/browser/components/downloads/test/browser/browser_confirm_unblock_download.js +++ b/browser/components/downloads/test/browser/browser_confirm_unblock_download.js @@ -35,12 +35,12 @@ add_task(function* test_confirm_unblock_dialog_unblock() { addDialogOpenObserver("accept"); let result = yield DownloadsCommon.confirmUnblockDownload(Downloads.Error.BLOCK_VERDICT_MALWARE, window); - ok(result, "Should return true when the user clicks on `Unblock` button."); + is(result, "unblock"); }); add_task(function* test_confirm_unblock_dialog_keep_safe() { addDialogOpenObserver("cancel"); let result = yield DownloadsCommon.confirmUnblockDownload(Downloads.Error.BLOCK_VERDICT_MALWARE, window); - ok(!result, "Should return false when the user clicks on `Keep me safe` button."); + is(result, "cancel"); }); From cbc051ef5c4b773e507c780d5e452bb4bd665309 Mon Sep 17 00:00:00 2001 From: Jan Odvarko Date: Wed, 20 Apr 2016 12:22:11 +0200 Subject: [PATCH 11/14] Bug 1266021 - Remove bottom border for HTTP details tab. r=helenvholmes --- devtools/client/webconsole/net/components/net-info-body.css | 1 - 1 file changed, 1 deletion(-) diff --git a/devtools/client/webconsole/net/components/net-info-body.css b/devtools/client/webconsole/net/components/net-info-body.css index 290bfc64018d..3c80a6ad48b1 100644 --- a/devtools/client/webconsole/net/components/net-info-body.css +++ b/devtools/client/webconsole/net/components/net-info-body.css @@ -109,7 +109,6 @@ .theme-firebug .netInfoBody > .tabs .tabs-menu-item.is-active a:focus { border: 1px solid var(--net-border); border-bottom-color: transparent; - border-color: var(--net-border); } .theme-firebug .netInfoBody > .tabs .tabs-menu-item:hover a { From 9979406b4a31a1180a57a70ccd047a0991a4540b Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Wed, 20 Apr 2016 11:50:44 -0400 Subject: [PATCH 12/14] Bug 1266100 - [ESLint] Fix ESLint issues in devtools/client/shared/widgets/Spectrum.js r=tromey --- devtools/client/shared/widgets/Spectrum.js | 104 +++++++++++---------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/devtools/client/shared/widgets/Spectrum.js b/devtools/client/shared/widgets/Spectrum.js index 68587bd30515..93a1e0219ee8 100644 --- a/devtools/client/shared/widgets/Spectrum.js +++ b/devtools/client/shared/widgets/Spectrum.js @@ -14,7 +14,8 @@ const EventEmitter = require("devtools/shared/event-emitter"); * const {Spectrum} = require("devtools/client/shared/widgets/Spectrum"); * let s = new Spectrum(containerElement, [255, 126, 255, 1]); * s.on("changed", (event, rgba, color) => { - * console.log("rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ", " + rgba[3] + ")"); + * console.log("rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ", " + + * rgba[3] + ")"); * }); * s.show(); * s.destroy(); @@ -31,32 +32,32 @@ const EventEmitter = require("devtools/shared/event-emitter"); function Spectrum(parentEl, rgb) { EventEmitter.decorate(this); - this.element = parentEl.ownerDocument.createElement('div'); + this.element = parentEl.ownerDocument.createElement("div"); this.parentEl = parentEl; this.element.className = "spectrum-container"; - this.element.innerHTML = [ - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - "
", - ].join(""); + this.element.innerHTML = ` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `; this.onElementClick = this.onElementClick.bind(this); this.element.addEventListener("click", this.onElementClick, false); @@ -94,12 +95,12 @@ Spectrum.hsvToRgb = function(h, s, v, a) { let t = v * (1 - (1 - f) * s); switch(i % 6) { - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; } return [r * 255, g * 255, b * 255, a]; @@ -116,10 +117,10 @@ Spectrum.rgbToHsv = function(r, g, b, a) { let d = max - min; s = max == 0 ? 0 : d / max; - if(max == min) { - h = 0; // achromatic - } - else { + if (max == min) { + // achromatic + h = 0; + } else { switch(max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; @@ -220,8 +221,10 @@ Spectrum.prototype = { }, get rgb() { - let rgb = Spectrum.hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2], this.hsv[3]); - return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), Math.round(rgb[3]*100)/100]; + let rgb = Spectrum.hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2], + this.hsv[3]); + return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), + Math.round(rgb[3] * 100) / 100]; }, get rgbNoSatVal() { @@ -231,11 +234,12 @@ Spectrum.prototype = { get rgbCssString() { let rgb = this.rgb; - return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")"; + return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + + rgb[3] + ")"; }, show: function() { - this.element.classList.add('spectrum-show'); + this.element.classList.add("spectrum-show"); this.slideHeight = this.slider.offsetHeight; this.dragWidth = this.dragger.offsetWidth; @@ -276,9 +280,11 @@ Spectrum.prototype = { }, updateHelperLocations: function() { - // If the UI hasn't been shown yet then none of the dimensions will be correct - if (!this.element.classList.contains('spectrum-show')) + // If the UI hasn't been shown yet then none of the dimensions will be + // correct + if (!this.element.classList.contains("spectrum-show")) { return; + } let h = this.hsv[0]; let s = this.hsv[1]; @@ -287,7 +293,7 @@ Spectrum.prototype = { // Placing the color dragger let dragX = s * this.dragWidth; let dragY = this.dragHeight - (v * this.dragHeight); - let helperDim = this.dragHelperHeight/2; + let helperDim = this.dragHelperHeight / 2; dragX = Math.max( -helperDim, @@ -302,11 +308,12 @@ Spectrum.prototype = { this.dragHelper.style.left = dragX + "px"; // Placing the hue slider - let slideY = (h * this.slideHeight) - this.slideHelperHeight/2; + let slideY = (h * this.slideHeight) - this.slideHelperHeight / 2; this.slideHelper.style.top = slideY + "px"; // Placing the alpha slider - let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) - (this.alphaSliderHelperWidth / 2); + let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) - + (this.alphaSliderHelperWidth / 2); this.alphaSliderHelper.style.left = alphaSliderX + "px"; }, @@ -316,14 +323,15 @@ Spectrum.prototype = { let rgb = this.rgb; let rgbNoSatVal = this.rgbNoSatVal; - let flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " + rgbNoSatVal[2] + ")"; - let fullColor = "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")"; + let flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " + + rgbNoSatVal[2] + ")"; this.dragger.style.backgroundColor = flatColor; - var rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; - var rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)"; - var alphaGradient = "linear-gradient(to right, " + rgbAlpha0 + ", " + rgbNoAlpha + ")"; + let rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + let rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)"; + let alphaGradient = "linear-gradient(to right, " + rgbAlpha0 + ", " + + rgbNoAlpha + ")"; this.alphaSliderInner.style.background = alphaGradient; }, From 7f155f79ba4e3ca78a3c8fcb5b03912509631b94 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Wed, 20 Apr 2016 11:01:09 -0700 Subject: [PATCH 13/14] Backed out 2 changesets (bug 1247138) for permafailing media mochitests on OSX 10.6 a=backout Backed out changeset 34f5a7144be3 (bug 1247138) Backed out changeset fbcd1815776a (bug 1247138) MozReview-Commit-ID: 5PLY4dZx3me --- dom/media/mediasource/TrackBuffersManager.cpp | 8 ++ .../mediasource/test/aac20-48000-64000-1.m4s | Bin 24328 -> 0 bytes .../test/aac20-48000-64000-1.m4s^headers^ | 1 - .../mediasource/test/aac20-48000-64000-2.m4s | Bin 24132 -> 0 bytes .../test/aac20-48000-64000-2.m4s^headers^ | 1 - .../test/aac20-48000-64000-init.mp4 | Bin 1246 -> 0 bytes .../test/aac20-48000-64000-init.mp4^headers^ | 1 - .../mediasource/test/aac51-48000-128000-1.m4s | Bin 48979 -> 0 bytes .../test/aac51-48000-128000-1.m4s^headers^ | 1 - .../mediasource/test/aac51-48000-128000-2.m4s | Bin 47727 -> 0 bytes .../test/aac51-48000-128000-2.m4s^headers^ | 1 - .../test/aac51-48000-128000-init.mp4 | Bin 634 -> 0 bytes .../test/aac51-48000-128000-init.mp4^headers^ | 1 - dom/media/mediasource/test/mochitest.ini | 8 -- .../test/test_AudioChange_mp4.html | 72 ------------------ 15 files changed, 8 insertions(+), 86 deletions(-) delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-1.m4s delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-1.m4s^headers^ delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-2.m4s delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-2.m4s^headers^ delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-init.mp4 delete mode 100644 dom/media/mediasource/test/aac20-48000-64000-init.mp4^headers^ delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-1.m4s delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-1.m4s^headers^ delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-2.m4s delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-2.m4s^headers^ delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-init.mp4 delete mode 100644 dom/media/mediasource/test/aac51-48000-128000-init.mp4^headers^ delete mode 100644 dom/media/mediasource/test/test_AudioChange_mp4.html diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index 1fcd695130f8..1f75091a3966 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -1094,6 +1094,14 @@ TrackBuffersManager::OnDemuxerInitDone(nsresult) // 6. Set first initialization segment received flag to true. mFirstInitializationSegmentReceived = true; } else { + // Check that audio configuration hasn't changed as this is something + // we do not support yet (bug 1185827). + if (mAudioTracks.mNumTracks && + (info.mAudio.mChannels != mAudioTracks.mInfo->GetAsAudioInfo()->mChannels || + info.mAudio.mRate != mAudioTracks.mInfo->GetAsAudioInfo()->mRate)) { + RejectAppend(NS_ERROR_FAILURE, __func__); + return; + } mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID); mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID); } diff --git a/dom/media/mediasource/test/aac20-48000-64000-1.m4s b/dom/media/mediasource/test/aac20-48000-64000-1.m4s deleted file mode 100644 index 56506e1f2da0b9304bafc93d3b26e2d88d1f1339..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24328 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 DkaU3- diff --git a/dom/media/mediasource/test/aac20-48000-64000-1.m4s^headers^ b/dom/media/mediasource/test/aac20-48000-64000-1.m4s^headers^ deleted file mode 100644 index 4030ea1d3ddb..000000000000 --- a/dom/media/mediasource/test/aac20-48000-64000-1.m4s^headers^ +++ /dev/null @@ -1 +0,0 @@ -Cache-Control: no-store diff --git a/dom/media/mediasource/test/aac20-48000-64000-2.m4s b/dom/media/mediasource/test/aac20-48000-64000-2.m4s deleted file mode 100644 index 3faff17ebf709d76b471751ba9e3f0e37e522b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24132 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 a!B+R!`X^gG6n?7d>K diff --git a/dom/media/mediasource/test/aac20-48000-64000-init.mp4^headers^ b/dom/media/mediasource/test/aac20-48000-64000-init.mp4^headers^ deleted file mode 100644 index 4030ea1d3ddb..000000000000 --- a/dom/media/mediasource/test/aac20-48000-64000-init.mp4^headers^ +++ /dev/null @@ -1 +0,0 @@ -Cache-Control: no-store diff --git a/dom/media/mediasource/test/aac51-48000-128000-1.m4s b/dom/media/mediasource/test/aac51-48000-128000-1.m4s deleted file mode 100644 index 3424acfecca8ef311a09914026d0b81f2b002977..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48979 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ⅇ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 diff --git a/dom/media/mediasource/test/aac51-48000-128000-1.m4s^headers^ b/dom/media/mediasource/test/aac51-48000-128000-1.m4s^headers^ deleted file mode 100644 index 4030ea1d3ddb..000000000000 --- a/dom/media/mediasource/test/aac51-48000-128000-1.m4s^headers^ +++ /dev/null @@ -1 +0,0 @@ -Cache-Control: no-store diff --git a/dom/media/mediasource/test/aac51-48000-128000-2.m4s b/dom/media/mediasource/test/aac51-48000-128000-2.m4s deleted file mode 100644 index b02bfd043d38d87da6538f3fd72813aee7d2b675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47727 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 - - - MSE: basic functionality - - - - - -
-
-
- - From b8f98ffe9f26c25896f6d853d66785661f8cb57e Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Wed, 20 Apr 2016 11:01:36 -0700 Subject: [PATCH 14/14] Backed out 8 changesets (bug 1264199) for permafailing media mochitests on OSX 10.6 a=backout Backed out changeset 0f20f2080824 (bug 1264199) Backed out changeset 90351d2719be (bug 1264199) Backed out changeset f484b42cbdda (bug 1264199) Backed out changeset b79b62146120 (bug 1264199) Backed out changeset f587df589b69 (bug 1264199) Backed out changeset 02f781a573b4 (bug 1264199) Backed out changeset d3677ae4c8ec (bug 1264199) Backed out changeset d08288654ec9 (bug 1264199) MozReview-Commit-ID: HVtiBSOxf7t --- dom/media/AudioConverter.cpp | 145 ++------ dom/media/AudioConverter.h | 19 +- dom/media/AudioStream.cpp | 22 +- dom/media/AudioStream.h | 14 +- dom/media/MediaDecoderStateMachine.cpp | 2 +- dom/media/mediasink/DecodedAudioDataSink.cpp | 317 +++++------------- dom/media/mediasink/DecodedAudioDataSink.h | 28 +- .../handle-memory-error.patch | 46 --- media/libspeex_resampler/src/resample.c | 21 +- media/libspeex_resampler/update.sh | 1 - 10 files changed, 145 insertions(+), 470 deletions(-) delete mode 100644 media/libspeex_resampler/handle-memory-error.patch diff --git a/dom/media/AudioConverter.cpp b/dom/media/AudioConverter.cpp index fddcc31d0675..23269e07aefe 100644 --- a/dom/media/AudioConverter.cpp +++ b/dom/media/AudioConverter.cpp @@ -7,7 +7,6 @@ #include "AudioConverter.h" #include #include -#include /* * Parts derived from MythTV AudioConvert Class @@ -27,13 +26,25 @@ AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut) MOZ_DIAGNOSTIC_ASSERT(aIn.Format() == aOut.Format() && aIn.Interleaved() == aOut.Interleaved(), "No format or rate conversion is supported at this stage"); - MOZ_DIAGNOSTIC_ASSERT(aOut.Channels() <= 2 || + MOZ_DIAGNOSTIC_ASSERT((aIn.Channels() > aOut.Channels() && aOut.Channels() <= 2) || aIn.Channels() == aOut.Channels(), - "Only down/upmixing to mono or stereo is supported at this stage"); + "Only downmixing to mono or stereo is supported at this stage"); MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported"); mIn.Layout().MappingTable(mOut.Layout(), mChannelOrderMap); if (aIn.Rate() != aOut.Rate()) { - RecreateResampler(); + int error; + mResampler = speex_resampler_init(aOut.Channels(), + aIn.Rate(), + aOut.Rate(), + SPEEX_RESAMPLER_QUALITY_DEFAULT, + &error); + + if (error == RESAMPLER_ERR_SUCCESS) { + speex_resampler_skip_zeros(mResampler); + } else { + NS_WARNING("Failed to initialize resampler."); + mResampler = nullptr; + } } } @@ -49,7 +60,6 @@ bool AudioConverter::CanWorkInPlace() const { bool needDownmix = mIn.Channels() > mOut.Channels(); - bool needUpmix = mIn.Channels() < mOut.Channels(); bool canDownmixInPlace = mIn.Channels() * AudioConfig::SampleSize(mIn.Format()) >= mOut.Channels() * AudioConfig::SampleSize(mOut.Format()); @@ -58,7 +68,7 @@ AudioConverter::CanWorkInPlace() const // We should be able to work in place if 1s of audio input takes less space // than 1s of audio output. However, as we downmix before resampling we can't // perform any upsampling in place (e.g. if incoming rate >= outgoing rate) - return !needUpmix && (!needDownmix || canDownmixInPlace) && + return (!needDownmix || canDownmixInPlace) && (!needResample || canResampleInPlace); } @@ -67,9 +77,8 @@ AudioConverter::ProcessInternal(void* aOut, const void* aIn, size_t aFrames) { if (mIn.Channels() > mOut.Channels()) { return DownmixAudio(aOut, aIn, aFrames); - } else if (mIn.Channels() < mOut.Channels()) { - return UpmixAudio(aOut, aIn, aFrames); - } else if (mIn.Layout() != mOut.Layout() && CanReorderAudio()) { + } else if (mIn.Layout() != mOut.Layout() && + CanReorderAudio()) { ReOrderInterleavedChannels(aOut, aIn, aFrames); } else if (aIn != aOut) { memmove(aOut, aIn, FramesOutToBytes(aFrames)); @@ -105,7 +114,10 @@ AudioConverter::ReOrderInterleavedChannels(void* aOut, const void* aIn, { MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() == mOut.Channels()); - if (mOut.Channels() == 1 || mOut.Layout() == mIn.Layout()) { + if (mOut.Layout() == mIn.Layout()) { + return; + } + if (mOut.Channels() == 1) { // If channel count is 1, planar and non-planar formats are the same and // there's nothing to reorder. if (aOut != aIn) { @@ -219,7 +231,7 @@ AudioConverter::DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const if (mIn.Format() == AudioConfig::FORMAT_FLT) { const float* in = static_cast(aIn); float* out = static_cast(aOut); - for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) { + for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) { float sample = 0.0; // The sample of the buffer would be interleaved. sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5; @@ -228,7 +240,7 @@ AudioConverter::DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const } else if (mIn.Format() == AudioConfig::FORMAT_S16) { const int16_t* in = static_cast(aIn); int16_t* out = static_cast(aOut); - for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) { + for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) { int32_t sample = 0.0; // The sample of the buffer would be interleaved. sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5; @@ -250,126 +262,27 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames) uint32_t outframes = ResampleRecipientFrames(aFrames); uint32_t inframes = aFrames; - int error; if (mOut.Format() == AudioConfig::FORMAT_FLT) { const float* in = reinterpret_cast(aIn); float* out = reinterpret_cast(aOut); - error = - speex_resampler_process_interleaved_float(mResampler, in, &inframes, - out, &outframes); + speex_resampler_process_interleaved_float(mResampler, in, &inframes, + out, &outframes); } else if (mOut.Format() == AudioConfig::FORMAT_S16) { const int16_t* in = reinterpret_cast(aIn); int16_t* out = reinterpret_cast(aOut); - error = - speex_resampler_process_interleaved_int(mResampler, in, &inframes, - out, &outframes); + speex_resampler_process_interleaved_int(mResampler, in, &inframes, + out, &outframes); } else { MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type"); } - MOZ_ASSERT(error == RESAMPLER_ERR_SUCCESS); - if (error != RESAMPLER_ERR_SUCCESS) { - speex_resampler_destroy(mResampler); - mResampler = nullptr; - return 0; - } MOZ_ASSERT(inframes == aFrames, "Some frames will be dropped"); return outframes; } -void -AudioConverter::RecreateResampler() -{ - if (mResampler) { - speex_resampler_destroy(mResampler); - } - int error; - mResampler = speex_resampler_init(mOut.Channels(), - mIn.Rate(), - mOut.Rate(), - SPEEX_RESAMPLER_QUALITY_DEFAULT, - &error); - - if (error == RESAMPLER_ERR_SUCCESS) { - speex_resampler_skip_zeros(mResampler); - } else { - NS_WARNING("Failed to initialize resampler."); - mResampler = nullptr; - } -} - -size_t -AudioConverter::DrainResampler(void* aOut) -{ - if (!mResampler) { - return 0; - } - int frames = speex_resampler_get_input_latency(mResampler); - AlignedByteBuffer buffer(FramesOutToBytes(frames)); - if (!buffer) { - // OOM - return 0; - } - frames = ResampleAudio(aOut, buffer.Data(), frames); - // Tore down the resampler as it's easier than handling follow-up. - RecreateResampler(); - return frames; -} - -size_t -AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const -{ - MOZ_ASSERT(mIn.Format() == AudioConfig::FORMAT_S16 || - mIn.Format() == AudioConfig::FORMAT_FLT); - MOZ_ASSERT(mIn.Channels() < mOut.Channels()); - MOZ_ASSERT(mIn.Channels() == 1, "Can only upmix mono for now"); - MOZ_ASSERT(mOut.Channels() == 2, "Can only upmix to stereo for now"); - - if (mOut.Channels() != 2) { - return 0; - } - - // Upmix mono to stereo. - // This is a very dumb mono to stereo upmixing, power levels are preserved - // following the calculation: left = right = -3dB*mono. - if (mIn.Format() == AudioConfig::FORMAT_FLT) { - const float m3db = std::sqrt(0.5); // -3dB = sqrt(1/2) - const float* in = static_cast(aIn); - float* out = static_cast(aOut); - for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) { - float sample = in[fIdx] * m3db; - // The samples of the buffer would be interleaved. - *out++ = sample; - *out++ = sample; - } - } else if (mIn.Format() == AudioConfig::FORMAT_S16) { - const int16_t* in = static_cast(aIn); - int16_t* out = static_cast(aOut); - for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) { - int16_t sample = ((int32_t)in[fIdx] * 11585) >> 14; // close enough to i*sqrt(0.5) - // The samples of the buffer would be interleaved. - *out++ = sample; - *out++ = sample; - } - } else { - MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type"); - } - - return aFrames; -} - size_t AudioConverter::ResampleRecipientFrames(size_t aFrames) const { - if (!aFrames && mIn.Rate() != mOut.Rate()) { - // The resampler will be drained, account for frames currently buffered - // in the resampler. - if (!mResampler) { - return 0; - } - return speex_resampler_get_output_latency(mResampler); - } else { - return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1; - } + return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1; } size_t diff --git a/dom/media/AudioConverter.h b/dom/media/AudioConverter.h index 637ffcdc2f76..6f5f6d8ab745 100644 --- a/dom/media/AudioConverter.h +++ b/dom/media/AudioConverter.h @@ -123,8 +123,6 @@ public: // Convert the AudioDataBuffer. // Conversion will be done in place if possible. Otherwise a new buffer will // be returned. - // Providing an empty buffer and resampling is expected, the resampler - // will be drained. template AudioDataBuffer Process(AudioDataBuffer&& aBuffer) { @@ -154,7 +152,7 @@ public: return AudioDataBuffer(Move(temp1)); } frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames); - if (mIn.Rate() == mOut.Rate()) { + if (!frames || mIn.Rate() == mOut.Rate()) { temp1.SetLength(FramesOutToSamples(frames)); return AudioDataBuffer(Move(temp1)); } @@ -163,17 +161,13 @@ public: // If we are downsampling we can re-use it. AlignedBuffer* outputBuffer = &temp1; AlignedBuffer temp2; - if (!frames || mOut.Rate() > mIn.Rate()) { - // We are upsampling or about to drain, we can't work in place. - // Allocate another temporary buffer where the upsampling will occur. + if (mOut.Rate() > mIn.Rate()) { + // We are upsampling, we can't work in place. Allocate another temporary + // buffer where the upsampling will occur. temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames))); outputBuffer = &temp2; } - if (!frames) { - frames = DrainResampler(outputBuffer->Data()); - } else { - frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); - } + frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); outputBuffer->SetLength(FramesOutToSamples(frames)); return AudioDataBuffer(Move(*outputBuffer)); } @@ -219,7 +213,6 @@ private: size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames); void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const; size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const; - size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const; size_t FramesOutToSamples(size_t aFrames) const; size_t SamplesInToFrames(size_t aSamples) const; @@ -229,8 +222,6 @@ private: SpeexResamplerState* mResampler; size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames); size_t ResampleRecipientFrames(size_t aFrames) const; - void RecreateResampler(); - size_t DrainResampler(void* aOut); }; } // namespace mozilla diff --git a/dom/media/AudioStream.cpp b/dom/media/AudioStream.cpp index 9ae7ff9e32eb..6ff866db6c50 100644 --- a/dom/media/AudioStream.cpp +++ b/dom/media/AudioStream.cpp @@ -129,6 +129,7 @@ AudioStream::AudioStream(DataSource& aSource) , mTimeStretcher(nullptr) , mDumpFile(nullptr) , mState(INITIALIZED) + , mIsMonoAudioEnabled(gfxPrefs::MonoAudio()) , mDataSource(aSource) { } @@ -330,7 +331,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, ("%s channels: %d, rate: %d for %p", __FUNCTION__, aNumChannels, aRate, this)); mInRate = mOutRate = aRate; mChannels = aNumChannels; - mOutChannels = aNumChannels; + mOutChannels = mIsMonoAudioEnabled ? 1 : aNumChannels; mDumpFile = OpenDumpFile(this); @@ -352,6 +353,11 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate, params.format = ToCubebFormat::value; mAudioClock.Init(); + if (mIsMonoAudioEnabled) { + AudioConfig inConfig(mChannels, mInRate); + AudioConfig outConfig(mOutChannels, mOutRate); + mAudioConverter = MakeUnique(inConfig, outConfig); + } return OpenCubeb(params); } @@ -546,7 +552,7 @@ AudioStream::IsPaused() } bool -AudioStream::IsValidAudioFormat(Chunk* aChunk) +AudioStream::Downmix(Chunk* aChunk) { if (aChunk->Rate() != mInRate) { LOGW("mismatched sample %u, mInRate=%u", aChunk->Rate(), mInRate); @@ -557,6 +563,10 @@ AudioStream::IsValidAudioFormat(Chunk* aChunk) return false; } + if (mAudioConverter) { + mAudioConverter->Process(aChunk->GetWritable(), aChunk->Frames()); + } + return true; } @@ -585,10 +595,10 @@ AudioStream::GetUnprocessed(AudioBufferWriter& aWriter) break; } MOZ_ASSERT(c->Frames() <= aWriter.Available()); - if (IsValidAudioFormat(c.get())) { + if (Downmix(c.get())) { aWriter.Write(c->Data(), c->Frames()); } else { - // Write silence if invalid format. + // Write silence if downmixing fails. aWriter.WriteZeros(c->Frames()); } } @@ -613,10 +623,10 @@ AudioStream::GetTimeStretched(AudioBufferWriter& aWriter) break; } MOZ_ASSERT(c->Frames() <= toPopFrames); - if (IsValidAudioFormat(c.get())) { + if (Downmix(c.get())) { mTimeStretcher->putSamples(c->Data(), c->Frames()); } else { - // Write silence if invalid format. + // Write silence if downmixing fails. AutoTArray buf; buf.SetLength(mOutChannels * c->Frames()); memset(buf.Elements(), 0, buf.Length() * sizeof(AudioDataValue)); diff --git a/dom/media/AudioStream.h b/dom/media/AudioStream.h index d813c093507c..e105e6b1b954 100644 --- a/dom/media/AudioStream.h +++ b/dom/media/AudioStream.h @@ -286,11 +286,6 @@ public: // Returns true when the audio stream is paused. bool IsPaused(); - static uint32_t GetPreferredRate() - { - CubebUtils::InitPreferredSampleRate(); - return CubebUtils::PreferredSampleRate(); - } uint32_t GetRate() { return mOutRate; } uint32_t GetChannels() { return mChannels; } uint32_t GetOutChannels() { return mOutChannels; } @@ -333,9 +328,8 @@ private: nsresult EnsureTimeStretcherInitializedUnlocked(); - // Return true if audio frames are valid (correct sampling rate and valid - // channel count) otherwise false. - bool IsValidAudioFormat(Chunk* aChunk); + // Return true if downmixing succeeds otherwise false. + bool Downmix(Chunk* aChunk); void GetUnprocessed(AudioBufferWriter& aWriter); void GetTimeStretched(AudioBufferWriter& aWriter); @@ -375,8 +369,12 @@ private: StreamState mState; bool mIsFirst; + // Get this value from the preference, if true, we would downmix the stereo. + bool mIsMonoAudioEnabled; DataSource& mDataSource; + + UniquePtr mAudioConverter; }; } // namespace mozilla diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 4b26ef4d5017..8ead2a36ae0e 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -362,7 +362,7 @@ MediaDecoderStateMachine::CreateAudioSink() auto audioSinkCreator = [self] () { MOZ_ASSERT(self->OnTaskQueue()); return new DecodedAudioDataSink( - self->mTaskQueue, self->mAudioQueue, self->GetMediaTime(), + self->mAudioQueue, self->GetMediaTime(), self->mInfo.mAudio, self->mAudioChannel); }; return new AudioSinkWrapper(mTaskQueue, audioSinkCreator); diff --git a/dom/media/mediasink/DecodedAudioDataSink.cpp b/dom/media/mediasink/DecodedAudioDataSink.cpp index fd4cdba47e21..a42934e61840 100644 --- a/dom/media/mediasink/DecodedAudioDataSink.cpp +++ b/dom/media/mediasink/DecodedAudioDataSink.cpp @@ -29,8 +29,7 @@ namespace media { // The amount of audio frames that is used to fuzz rounding errors. static const int64_t AUDIO_FUZZ_FRAMES = 1; -DecodedAudioDataSink::DecodedAudioDataSink(AbstractThread* aThread, - MediaQueue& aAudioQueue, +DecodedAudioDataSink::DecodedAudioDataSink(MediaQueue& aAudioQueue, int64_t aStartTime, const AudioInfo& aInfo, dom::AudioChannel aChannel) @@ -41,32 +40,16 @@ DecodedAudioDataSink::DecodedAudioDataSink(AbstractThread* aThread, , mInfo(aInfo) , mChannel(aChannel) , mPlaying(true) - , mErrored(false) , mPlaybackComplete(false) - , mOwnerThread(aThread) - , mFramesParsed(0) - , mLastEndTime(0) { bool resampling = gfxPrefs::AudioSinkResampling(); - - if (resampling) { - mOutputRate = gfxPrefs::AudioSinkResampleRate(); - } else if (mInfo.mRate == 44100 || mInfo.mRate == 48000) { - // The original rate is of good quality and we want to minimize unecessary - // resampling. The common scenario being that the sampling rate is one or - // the other, this allows to minimize audio quality regression and hoping - // content provider want change from those rates mid-stream. - mOutputRate = mInfo.mRate; - } else { - // We will resample all data to match cubeb's preferred sampling rate. - mOutputRate = AudioStream::GetPreferredRate(); - } - MOZ_DIAGNOSTIC_ASSERT(mOutputRate, "output rate can't be 0."); - - bool monoAudioEnabled = gfxPrefs::MonoAudio(); - - mOutputChannels = monoAudioEnabled - ? 1 : (gfxPrefs::AudioSinkForceStereo() ? 2 : mInfo.mChannels); + uint32_t resamplingRate = gfxPrefs::AudioSinkResampleRate(); + mConverter = + MakeUnique( + AudioConfig(mInfo.mChannels, mInfo.mRate), + AudioConfig(mInfo.mChannels > 2 && gfxPrefs::AudioSinkForceStereo() + ? 2 : mInfo.mChannels, + resampling ? resamplingRate : mInfo.mRate)); } DecodedAudioDataSink::~DecodedAudioDataSink() @@ -76,16 +59,6 @@ DecodedAudioDataSink::~DecodedAudioDataSink() RefPtr DecodedAudioDataSink::Init(const PlaybackParams& aParams) { - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); - - mAudioQueueListener = mAudioQueue.PushEvent().Connect( - mOwnerThread, this, &DecodedAudioDataSink::OnAudioPushed); - mProcessedQueueListener = mProcessedQueue.PopEvent().Connect( - mOwnerThread, this, &DecodedAudioDataSink::OnAudioPopped); - - // To ensure at least one audio packet will be popped from AudioQueue and - // ready to be played. - NotifyAudioNeeded(); RefPtr p = mEndPromise.Ensure(__func__); nsresult rv = InitializeAudioStream(aParams); if (NS_FAILED(rv)) { @@ -122,16 +95,10 @@ DecodedAudioDataSink::HasUnplayedFrames() void DecodedAudioDataSink::Shutdown() { - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); - - mAudioQueueListener.Disconnect(); - mProcessedQueueListener.Disconnect(); - if (mAudioStream) { mAudioStream->Shutdown(); mAudioStream = nullptr; } - mProcessedQueue.Reset(); mEndPromise.ResolveIfExists(true, __func__); } @@ -179,7 +146,9 @@ nsresult DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams) { mAudioStream = new AudioStream(*this); - nsresult rv = mAudioStream->Init(mOutputChannels, mOutputRate, mChannel); + nsresult rv = mAudioStream->Init(mConverter->OutputConfig().Channels(), + mConverter->OutputConfig().Rate(), + mChannel); if (NS_FAILED(rv)) { mAudioStream->Shutdown(); mAudioStream = nullptr; @@ -199,14 +168,13 @@ DecodedAudioDataSink::InitializeAudioStream(const PlaybackParams& aParams) int64_t DecodedAudioDataSink::GetEndTime() const { - CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mOutputRate) + mStartTime; + CheckedInt64 playedUsecs = + FramesToUsecs(mWritten, mConverter->OutputConfig().Rate()) + mStartTime; if (!playedUsecs.isValid()) { NS_WARNING("Int overflow calculating audio end time"); return -1; } - // As we may be resampling, rounding errors may occur. Ensure we never get - // past the original end time. - return std::min(mLastEndTime, playedUsecs.value()); + return playedUsecs.value(); } UniquePtr @@ -249,13 +217,78 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames) UniquePtr mData; }; - if (!mCurrentData) { + while (!mCurrentData) { // No data in the queue. Return an empty chunk. - if (!mProcessedQueue.GetSize()) { + if (AudioQueue().GetSize() == 0) { return MakeUnique(); } - mCurrentData = dont_AddRef(mProcessedQueue.PopFront().take()); + AudioData* a = AudioQueue().PeekFront()->As(); + + // Ignore the element with 0 frames and try next. + if (a->mFrames == 0) { + RefPtr releaseMe = AudioQueue().PopFront(); + continue; + } + + // Ignore invalid samples. + if (a->mRate != mInfo.mRate || a->mChannels != mInfo.mChannels) { + NS_WARNING(nsPrintfCString( + "mismatched sample format, data=%p rate=%u channels=%u frames=%u", + a->mAudioData.get(), a->mRate, a->mChannels, a->mFrames).get()); + RefPtr releaseMe = AudioQueue().PopFront(); + continue; + } + + // See if there's a gap in the audio. If there is, push silence into the + // audio hardware, so we can play across the gap. + // Calculate the timestamp of the next chunk of audio in numbers of + // samples. + CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, + mConverter->OutputConfig().Rate()); + // Calculate the number of frames that have been pushed onto the audio hardware. + CheckedInt64 playedFrames = UsecsToFrames(mStartTime, + mConverter->OutputConfig().Rate()) + + static_cast(mWritten); + CheckedInt64 missingFrames = sampleTime - playedFrames; + + if (!missingFrames.isValid() || !sampleTime.isValid()) { + NS_WARNING("Int overflow in DecodedAudioDataSink"); + mErrored = true; + return MakeUnique(); + } + + const uint32_t rate = mConverter->OutputConfig().Rate(); + const uint32_t channels = mConverter->OutputConfig().Channels(); + + if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { + // The next audio chunk begins some time after the end of the last chunk + // we pushed to the audio hardware. We must push silence into the audio + // hardware so that the next audio chunk begins playback at the correct + // time. + missingFrames = std::min(UINT32_MAX, missingFrames.value()); + auto framesToPop = std::min(missingFrames.value(), aFrames); + mWritten += framesToPop; + return MakeUnique(framesToPop, channels, rate); + } + + RefPtr data = + dont_AddRef(AudioQueue().PopFront().take()->As()); + if (mConverter->InputConfig() != mConverter->OutputConfig()) { + AlignedAudioBuffer convertedData = + mConverter->Process(AudioSampleBuffer(Move(data->mAudioData))).Forget(); + mCurrentData = + new AudioData(data->mOffset, + data->mTime, + data->mDuration, + convertedData.Length() / channels, + Move(convertedData), + channels, + rate); + } else { + mCurrentData = Move(data); + } + mCursor = MakeUnique(mCurrentData->mAudioData.get(), mCurrentData->mChannels, mCurrentData->mFrames); @@ -275,7 +308,7 @@ DecodedAudioDataSink::PopFrames(uint32_t aFrames) // All frames are popped. Reset mCurrentData so we can pop new elements from // the audio queue in next calls to PopFrames(). - if (!mCursor->Available()) { + if (mCursor->Available() == 0) { mCurrentData = nullptr; } @@ -297,189 +330,5 @@ DecodedAudioDataSink::Drained() mEndPromise.ResolveIfExists(true, __func__); } -void -DecodedAudioDataSink::OnAudioPopped(const RefPtr& aSample) -{ - SINK_LOG_V("AudioStream has used an audio packet."); - NotifyAudioNeeded(); -} - -void -DecodedAudioDataSink::OnAudioPushed(const RefPtr& aSample) -{ - SINK_LOG_V("One new audio packet available."); - NotifyAudioNeeded(); -} - -void -DecodedAudioDataSink::NotifyAudioNeeded() -{ - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn(), - "Not called from the owner's thread"); - - if (AudioQueue().IsFinished() && !AudioQueue().GetSize()) { - // We have reached the end of the data, drain the resampler. - DrainConverter(); - return; - } - - // Always ensure we have two processed frames pending to allow for processing - // latency. - while (AudioQueue().GetSize() && mProcessedQueue.GetSize() < 2) { - RefPtr data = - dont_AddRef(AudioQueue().PopFront().take()->As()); - - // Ignore the element with 0 frames and try next. - if (!data->mFrames) { - continue; - } - - if (!mConverter || - (data->mRate != mConverter->InputConfig().Rate() || - data->mChannels != mConverter->InputConfig().Channels())) { - SINK_LOG_V("Audio format changed from %u@%uHz to %u@%uHz", - mConverter? mConverter->InputConfig().Channels() : 0, - mConverter ? mConverter->InputConfig().Rate() : 0, - data->mChannels, data->mRate); - - DrainConverter(); - - // mFramesParsed indicates the current playtime in frames at the current - // input sampling rate. Recalculate it per the new sampling rate. - if (mFramesParsed) { - // We minimize overflow. - uint32_t oldRate = mConverter->InputConfig().Rate(); - uint32_t newRate = data->mRate; - int64_t major = mFramesParsed / oldRate; - int64_t remainder = mFramesParsed % oldRate; - CheckedInt64 result = - CheckedInt64(remainder) * newRate / oldRate + major * oldRate; - if (!result.isValid()) { - NS_WARNING("Int overflow in DecodedAudioDataSink"); - mErrored = true; - return; - } - mFramesParsed = result.value(); - } - - mConverter = - MakeUnique( - AudioConfig(data->mChannels, data->mRate), - AudioConfig(mOutputChannels, mOutputRate)); - } - - // See if there's a gap in the audio. If there is, push silence into the - // audio hardware, so we can play across the gap. - // Calculate the timestamp of the next chunk of audio in numbers of - // samples. - CheckedInt64 sampleTime = UsecsToFrames(data->mTime - mStartTime, - data->mRate); - // Calculate the number of frames that have been pushed onto the audio hardware. - CheckedInt64 missingFrames = sampleTime - mFramesParsed; - - if (!missingFrames.isValid()) { - NS_WARNING("Int overflow in DecodedAudioDataSink"); - mErrored = true; - return; - } - - if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { - // The next audio packet begins some time after the end of the last packet - // we pushed to the audio hardware. We must push silence into the audio - // hardware so that the next audio packet begins playback at the correct - // time. - missingFrames = std::min(INT32_MAX, missingFrames.value()); - mFramesParsed += missingFrames.value(); - // We need to insert silence, first use drained frames if any. - missingFrames -= DrainConverter(missingFrames.value()); - // Insert silence is still needed. - if (missingFrames.value()) { - AlignedAudioBuffer silenceData(missingFrames.value() * mOutputChannels); - if (!silenceData) { - NS_WARNING("OOM in DecodedAudioDataSink"); - mErrored = true; - return; - } - RefPtr silence = CreateAudioFromBuffer(Move(silenceData), data); - if (silence) { - mProcessedQueue.Push(silence); - } - } - } - - mLastEndTime = data->GetEndTime(); - mFramesParsed += data->mFrames; - - if (mConverter->InputConfig() != mConverter->OutputConfig()) { - AlignedAudioBuffer convertedData = - mConverter->Process(AudioSampleBuffer(Move(data->mAudioData))).Forget(); - data = CreateAudioFromBuffer(Move(convertedData), data); - if (!data) { - continue; - } - } - mProcessedQueue.Push(data); - mLastProcessedPacket = Some(data); - } -} - -already_AddRefed -DecodedAudioDataSink::CreateAudioFromBuffer(AlignedAudioBuffer&& aBuffer, - AudioData* aReference) -{ - uint32_t frames = aBuffer.Length() / mOutputChannels; - if (!frames) { - return nullptr; - } - CheckedInt64 duration = FramesToUsecs(frames, mOutputRate); - if (!duration.isValid()) { - NS_WARNING("Int overflow in DecodedAudioDataSink"); - mErrored = true; - return nullptr; - } - RefPtr data = - new AudioData(aReference->mOffset, - aReference->mTime, - duration.value(), - frames, - Move(aBuffer), - mOutputChannels, - mOutputRate); - return data.forget(); -} - -uint32_t -DecodedAudioDataSink::DrainConverter(uint32_t aMaxFrames) -{ - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); - - if (!mConverter || !mLastProcessedPacket) { - // nothing to drain. - return 0; - } - - RefPtr lastPacket = mLastProcessedPacket.ref(); - mLastProcessedPacket.reset(); - - // To drain we simply provide an empty packet to the audio converter. - AlignedAudioBuffer convertedData = - mConverter->Process(AudioSampleBuffer(AlignedAudioBuffer())).Forget(); - - uint32_t frames = convertedData.Length() / mOutputChannels; - if (!convertedData.SetLength(std::min(frames, aMaxFrames) * mOutputChannels)) { - // This can never happen as we were reducing the length of convertData. - mErrored = true; - return 0; - } - - RefPtr data = - CreateAudioFromBuffer(Move(convertedData), lastPacket); - if (!data) { - return 0; - } - mProcessedQueue.Push(data); - return data->mFrames; -} - } // namespace media } // namespace mozilla diff --git a/dom/media/mediasink/DecodedAudioDataSink.h b/dom/media/mediasink/DecodedAudioDataSink.h index 505316ab835b..ba16042167c3 100644 --- a/dom/media/mediasink/DecodedAudioDataSink.h +++ b/dom/media/mediasink/DecodedAudioDataSink.h @@ -28,8 +28,7 @@ namespace media { class DecodedAudioDataSink : public AudioSink, private AudioStream::DataSource { public: - DecodedAudioDataSink(AbstractThread* aThread, - MediaQueue& aAudioQueue, + DecodedAudioDataSink(MediaQueue& aAudioQueue, int64_t aStartTime, const AudioInfo& aInfo, dom::AudioChannel aChannel); @@ -103,35 +102,12 @@ private: // Keep track of the read position of mCurrentData. UniquePtr mCursor; // True if there is any error in processing audio data like overflow. - Atomic mErrored; + bool mErrored = false; // Set on the callback thread of cubeb once the stream has drained. Atomic mPlaybackComplete; - const RefPtr mOwnerThread; - - // Audio Processing objects and methods - void OnAudioPopped(const RefPtr& aSample); - void OnAudioPushed(const RefPtr& aSample); - void NotifyAudioNeeded(); - // Drain the converter and add the output to the processed audio queue. - // A maximum of aMaxFrames will be added. - uint32_t DrainConverter(uint32_t aMaxFrames = UINT32_MAX); - already_AddRefed CreateAudioFromBuffer(AlignedAudioBuffer&& aBuffer, - AudioData* aReference); UniquePtr mConverter; - MediaQueue mProcessedQueue; - MediaEventListener mAudioQueueListener; - MediaEventListener mProcessedQueueListener; - // Number of frames processed from AudioQueue(). Used to determine gaps in - // the input stream. It indicates the time in frames since playback started - // at the current input framerate. - int64_t mFramesParsed; - Maybe> mLastProcessedPacket; - int64_t mLastEndTime; - // Never modifed after construction. - uint32_t mOutputRate; - uint32_t mOutputChannels; }; } // namespace media diff --git a/media/libspeex_resampler/handle-memory-error.patch b/media/libspeex_resampler/handle-memory-error.patch deleted file mode 100644 index f712a2de681a..000000000000 --- a/media/libspeex_resampler/handle-memory-error.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/media/libspeex_resampler/src/resample.c b/media/libspeex_resampler/src/resample.c -index 83ad119..a3859e3 100644 ---- a/media/libspeex_resampler/src/resample.c -+++ b/media/libspeex_resampler/src/resample.c -@@ -811,6 +811,12 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, - return NULL; - } - st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); -+ if (!st) -+ { -+ if (err) -+ *err = RESAMPLER_ERR_ALLOC_FAILED; -+ return NULL; -+ } - st->initialised = 0; - st->started = 0; - st->in_rate = 0; -@@ -832,9 +838,12 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, - st->buffer_size = 160; - - /* Per channel data */ -- st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)); -- st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); -- st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); -+ if (!(st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)))) -+ goto fail; -+ if (!(st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) -+ goto fail; -+ if (!(st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) -+ goto fail; - for (i=0;ilast_sample[i] = 0; -@@ -857,6 +866,12 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, - *err = filter_err; - - return st; -+ -+fail: -+ if (err) -+ *err = RESAMPLER_ERR_ALLOC_FAILED; -+ speex_resampler_destroy(st); -+ return NULL; - } - - EXPORT void speex_resampler_destroy(SpeexResamplerState *st) diff --git a/media/libspeex_resampler/src/resample.c b/media/libspeex_resampler/src/resample.c index a3859e348a1c..83ad119d4513 100644 --- a/media/libspeex_resampler/src/resample.c +++ b/media/libspeex_resampler/src/resample.c @@ -811,12 +811,6 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, return NULL; } st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); - if (!st) - { - if (err) - *err = RESAMPLER_ERR_ALLOC_FAILED; - return NULL; - } st->initialised = 0; st->started = 0; st->in_rate = 0; @@ -838,12 +832,9 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, st->buffer_size = 160; /* Per channel data */ - if (!(st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)))) - goto fail; - if (!(st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) - goto fail; - if (!(st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) - goto fail; + st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)); + st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); + st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)); for (i=0;ilast_sample[i] = 0; @@ -866,12 +857,6 @@ EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, *err = filter_err; return st; - -fail: - if (err) - *err = RESAMPLER_ERR_ALLOC_FAILED; - speex_resampler_destroy(st); - return NULL; } EXPORT void speex_resampler_destroy(SpeexResamplerState *st) diff --git a/media/libspeex_resampler/update.sh b/media/libspeex_resampler/update.sh index d4a025b8723e..716a4088d877 100644 --- a/media/libspeex_resampler/update.sh +++ b/media/libspeex_resampler/update.sh @@ -25,4 +25,3 @@ patch -p3 < simd-detect-runtime.patch patch -p3 < set-skip-frac.patch patch -p3 < hugemem.patch patch -p3 < remove-empty-asm-clobber.patch -patch -p3 < handle-memory-error.patch