diff --git a/Makefile.in b/Makefile.in index 4c8baebb9f5a..04cfaf39ff5b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -319,7 +319,7 @@ ifdef MOZ_CRASHREPORTER grep 'sym' $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \ mv $(SYMBOL_INDEX_NAME).tmp $(SYMBOL_INDEX_NAME) cd $(DIST)/crashreporter-symbols && \ - zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt' -x '*test*' -x '*Test*' + zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt' endif # MOZ_CRASHREPORTER uploadsymbols: diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 6e5393ae173e..a91cc6c05519 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -500,7 +500,8 @@ var ClickEventHandler = { ctrlKey: event.ctrlKey, metaKey: event.metaKey, altKey: event.altKey, href: null, title: null, bookmark: false, referrerPolicy: referrerPolicy, - originAttributes: principal ? principal.originAttributes : {} }; + originAttributes: principal ? principal.originAttributes : {}, + isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)}; if (href) { try { diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 95ed7abe2e20..13dbbc4350e1 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -284,6 +284,7 @@ subsuite = clipboard skip-if = true # Disabled due to the clipboard not supporting real file types yet (bug 1288773) [browser_contentAreaClick.js] skip-if = e10s # Clicks in content don't go through contentAreaClick with e10s. +[browser_contentAltClick.js] [browser_contextmenu.js] subsuite = clipboard tags = fullscreen diff --git a/browser/base/content/test/general/browser_contentAltClick.js b/browser/base/content/test/general/browser_contentAltClick.js new file mode 100644 index 000000000000..af4941b76986 --- /dev/null +++ b/browser/base/content/test/general/browser_contentAltClick.js @@ -0,0 +1,107 @@ +/* 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/. */ + +/** + * Test for Bug 1109146. + * The tests opens a new tab and alt + clicks to download files + * and confirms those files are on the download list. + * + * The difference between this and the test "browser_contentAreaClick.js" is that + * the code path in e10s uses ContentClick.jsm instead of browser.js::contentAreaClick() util. + */ +"use strict"; + +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", + "resource://gre/modules/Downloads.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); + +function setup(){ + gPrefService.setBoolPref("browser.altClickSave", true); + + let testPage = + 'data:text/html,' + + '
' + + '' + + ''; + + return BrowserTestUtils.openNewForegroundTab(gBrowser, testPage); +} + +function* clean_up() { + // Remove downloads. + let downloadList = yield Downloads.getList(Downloads.ALL); + let downloads = yield downloadList.getAll(); + for (let download of downloads) { + yield downloadList.remove(download); + yield download.finalize(true); + } + // Remove download history. + yield PlacesTestUtils.clearHistory(); + + gPrefService.clearUserPref("browser.altClickSave"); + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); +} + +add_task(function* test_alt_click() +{ + yield setup(); + + let downloadList = yield Downloads.getList(Downloads.ALL); + let downloads = []; + let downloadView; + // When 1 download has been attempted then resolve the promise. + let finishedAllDownloads = new Promise( (resolve)=> { + downloadView = { + onDownloadAdded: function (aDownload) { + downloads.push(aDownload); + resolve(); + }, + }; + }); + yield downloadList.addView(downloadView); + yield BrowserTestUtils.synthesizeMouseAtCenter("#commonlink", {altKey: true}, gBrowser.selectedBrowser); + + // Wait for all downloads to be added to the download list. + yield finishedAllDownloads; + yield downloadList.removeView(downloadView); + + is(downloads.length, 1, "1 downloads"); + is(downloads[0].source.url, "http://mochi.test/moz/", "Downloaded #commonlink element"); + + yield* clean_up(); +}); + +add_task(function* test_alt_click_on_xlinks() +{ + yield setup(); + + let downloadList = yield Downloads.getList(Downloads.ALL); + let downloads = []; + let downloadView; + // When all 2 downloads have been attempted then resolve the promise. + let finishedAllDownloads = new Promise( (resolve)=> { + downloadView = { + onDownloadAdded: function (aDownload) { + downloads.push(aDownload); + if (downloads.length == 2) { + resolve(); + } + }, + }; + }); + yield downloadList.addView(downloadView); + yield BrowserTestUtils.synthesizeMouseAtCenter("#mathxlink", {altKey: true}, gBrowser.selectedBrowser); + yield BrowserTestUtils.synthesizeMouseAtCenter("#svgxlink", {altKey: true}, gBrowser.selectedBrowser); + + // Wait for all downloads to be added to the download list. + yield finishedAllDownloads; + yield downloadList.removeView(downloadView); + + is(downloads.length, 2, "2 downloads"); + is(downloads[0].source.url, "http://mochi.test/moz/", "Downloaded #mathxlink element"); + is(downloads[1].source.url, "http://mochi.test/moz/", "Downloaded #svgxlink element"); + + yield* clean_up(); +}); \ No newline at end of file diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index 2bac7d00b3c3..b79bf2afc7e0 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -224,13 +224,20 @@ function openLinkIn(url, where, params) { var aIndicateErrorPageLoad = params.indicateErrorPageLoad; if (where == "save") { - if (!aInitiatingDoc) { - Components.utils.reportError("openUILink/openLinkIn was called with " + - "where == 'save' but without initiatingDoc. See bug 814264."); - return; - } // TODO(1073187): propagate referrerPolicy. - saveURL(url, null, null, true, null, aNoReferrer ? null : aReferrerURI, aInitiatingDoc); + + // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc + if ("isContentWindowPrivate" in params) { + saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate); + } + else { + if (!aInitiatingDoc) { + Components.utils.reportError("openUILink/openLinkIn was called with " + + "where == 'save' but without initiatingDoc. See bug 814264."); + return; + } + saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc); + } return; } diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index 5ebb6ad1f9eb..6800bfe547fb 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -508,20 +508,32 @@ this.DownloadsCommon = { // or the file doesn't exist), try using the parent if we have it. let parent = aFile.parent; if (parent) { - try { - // Open the parent directory to show where the file should be. - parent.launch(); - } catch (ex) { - // If launch also fails (probably because it's not implemented), let - // the OS handler try to open the parent. - Cc["@mozilla.org/uriloader/external-protocol-service;1"] - .getService(Ci.nsIExternalProtocolService) - .loadUrl(NetUtil.newURI(parent)); - } + this.showDirectory(parent); } } }, + /** + * Show the specified folder in the system file manager. + * + * @param aDirectory + * a directory to be opened with system file manager. + */ + showDirectory(aDirectory) { + if (!(aDirectory instanceof Ci.nsIFile)) { + throw new Error("aDirectory must be a nsIFile object"); + } + try { + aDirectory.launch(); + } catch (ex) { + // If launch fails (probably because it's not implemented), let + // the OS handler try to open the directory. + Cc["@mozilla.org/uriloader/external-protocol-service;1"] + .getService(Ci.nsIExternalProtocolService) + .loadUrl(NetUtil.newURI(aDirectory)); + } + }, + /** * Displays an alert message box which asks the user if they want to * unblock the downloaded file or not. diff --git a/browser/components/downloads/content/downloads.css b/browser/components/downloads/content/downloads.css index 7077618ef24d..c31c66a0ed4b 100644 --- a/browser/components/downloads/content/downloads.css +++ b/browser/components/downloads/content/downloads.css @@ -19,7 +19,7 @@ richlistitem[type="download"].download-state[state="1"]:not([exists]) .downloadS #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress, #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails, -#downloadsFooter[showingsummary] > #downloadsHistory, +#downloadsFooter[showingsummary] > #downloadsFooterButtons, #downloadsFooter:not([showingsummary]) > #downloadsSummary { display: none; } diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index 5abb8ffb3066..335fac9be856 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -367,6 +367,23 @@ const DownloadsPanel = { this._state = this.kStateHidden; }, + onFooterPopupShowing(aEvent) { + let itemClearList = document.getElementById("downloadsDropdownItemClearList"); + if (DownloadsCommon.getData(window).canRemoveFinished) { + itemClearList.removeAttribute("hidden"); + } else { + itemClearList.setAttribute("hidden", "true"); + } + + document.getElementById("downloadsFooterButtonsSplitter").classList + .add("downloadsDropmarkerSplitterExtend"); + }, + + onFooterPopupHidden(aEvent) { + document.getElementById("downloadsFooterButtonsSplitter").classList + .remove("downloadsDropmarkerSplitterExtend"); + }, + ////////////////////////////////////////////////////////////////////////////// //// Related operations @@ -382,6 +399,13 @@ const DownloadsPanel = { BrowserDownloadsUI(); }, + openDownloadsFolder() { + Downloads.getPreferredDownloadsDirectory().then(downloadsPath => { + DownloadsCommon.showDirectory(new FileUtils.File(downloadsPath)); + }).catch(Cu.reportError); + this.hidePanel(); + }, + ////////////////////////////////////////////////////////////////////////////// //// Internal functions @@ -1188,6 +1212,9 @@ const DownloadsViewController = { //// nsIController supportsCommand(aCommand) { + if (aCommand === "downloadsCmd_clearList") { + return true; + } // Firstly, determine if this is a command that we can handle. if (!DownloadsViewUI.isCommandName(aCommand)) { return false; diff --git a/browser/components/downloads/content/downloadsOverlay.xul b/browser/components/downloads/content/downloadsOverlay.xul index 96569569780f..e32142e0bdf0 100644 --- a/browser/components/downloads/content/downloadsOverlay.xul +++ b/browser/components/downloads/content/downloadsOverlay.xul @@ -104,8 +104,8 @@