forked from mirrors/gecko-dev
Bug 1639067 part 1 - Open more downloaded file types internally. r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D86650
This commit is contained in:
parent
7cf9cb9d07
commit
c681abc203
7 changed files with 633 additions and 5 deletions
|
|
@ -386,6 +386,11 @@ pref("browser.download.openInSystemViewerContextMenuItem", true);
|
||||||
// This records whether or not to show the 'Always open...' context menu item when appropriate
|
// This records whether or not to show the 'Always open...' context menu item when appropriate
|
||||||
pref("browser.download.alwaysOpenInSystemViewerContextMenuItem", true);
|
pref("browser.download.alwaysOpenInSystemViewerContextMenuItem", true);
|
||||||
|
|
||||||
|
// Open downloaded file types internally for the given types.
|
||||||
|
// This is a comma-separated list, the empty string ("") means no types are
|
||||||
|
// viewable internally.
|
||||||
|
pref("browser.download.viewableInternally.enabledTypes", "xml,svg,webp,avif");
|
||||||
|
|
||||||
|
|
||||||
// This controls whether the button is automatically shown/hidden depending
|
// This controls whether the button is automatically shown/hidden depending
|
||||||
// on whether there are downloads to show.
|
// on whether there are downloads to show.
|
||||||
|
|
|
||||||
|
|
@ -711,6 +711,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
Corroborate: "resource://gre/modules/Corroborate.jsm",
|
Corroborate: "resource://gre/modules/Corroborate.jsm",
|
||||||
Discovery: "resource:///modules/Discovery.jsm",
|
Discovery: "resource:///modules/Discovery.jsm",
|
||||||
DoHController: "resource:///modules/DoHController.jsm",
|
DoHController: "resource:///modules/DoHController.jsm",
|
||||||
|
DownloadsViewableInternally:
|
||||||
|
"resource:///modules/DownloadsViewableInternally.jsm",
|
||||||
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
|
ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
|
||||||
FirefoxMonitor: "resource:///modules/FirefoxMonitor.jsm",
|
FirefoxMonitor: "resource:///modules/FirefoxMonitor.jsm",
|
||||||
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
|
FxAccounts: "resource://gre/modules/FxAccounts.jsm",
|
||||||
|
|
@ -1124,6 +1126,10 @@ BrowserGlue.prototype = {
|
||||||
// pdf content handler, and initializes parent side message manager
|
// pdf content handler, and initializes parent side message manager
|
||||||
// shim for privileged api access.
|
// shim for privileged api access.
|
||||||
PdfJs.init(this._isNewProfile);
|
PdfJs.init(this._isNewProfile);
|
||||||
|
|
||||||
|
// Allow certain viewable internally types to be opened from downloads.
|
||||||
|
DownloadsViewableInternally.register();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "shield-init-complete":
|
case "shield-init-complete":
|
||||||
this._shieldInitComplete = true;
|
this._shieldInitComplete = true;
|
||||||
|
|
|
||||||
351
browser/components/downloads/DownloadsViewableInternally.jsm
Normal file
351
browser/components/downloads/DownloadsViewableInternally.jsm
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: This is based on what PdfJs was already doing, it would be
|
||||||
|
* best to use this over there as well to reduce duplication and
|
||||||
|
* inconsistency.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = [
|
||||||
|
"DownloadsViewableInternally",
|
||||||
|
"PREF_ENABLED_TYPES",
|
||||||
|
"PREF_BRANCH_WAS_REGISTERED",
|
||||||
|
"PREF_BRANCH_PREVIOUS_ACTION",
|
||||||
|
"PREF_BRANCH_PREVIOUS_ASK",
|
||||||
|
];
|
||||||
|
|
||||||
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(
|
||||||
|
this,
|
||||||
|
"HandlerService",
|
||||||
|
"@mozilla.org/uriloader/handler-service;1",
|
||||||
|
"nsIHandlerService"
|
||||||
|
);
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(
|
||||||
|
this,
|
||||||
|
"MIMEService",
|
||||||
|
"@mozilla.org/mime;1",
|
||||||
|
"nsIMIMEService"
|
||||||
|
);
|
||||||
|
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"Integration",
|
||||||
|
"resource://gre/modules/Integration.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
|
const PREF_BRANCH = "browser.download.viewableInternally.";
|
||||||
|
const PREF_ENABLED_TYPES = PREF_BRANCH + "enabledTypes";
|
||||||
|
const PREF_BRANCH_WAS_REGISTERED = PREF_BRANCH + "typeWasRegistered.";
|
||||||
|
const PREF_BRANCH_PREVIOUS_ACTION =
|
||||||
|
PREF_BRANCH + "previousHandler.preferredAction.";
|
||||||
|
const PREF_BRANCH_PREVIOUS_ASK =
|
||||||
|
PREF_BRANCH + "previousHandler.alwaysAskBeforeHandling.";
|
||||||
|
|
||||||
|
let DownloadsViewableInternally = {
|
||||||
|
/**
|
||||||
|
* Initially add/remove handlers, watch pref, register with Integration.downloads.
|
||||||
|
*/
|
||||||
|
register() {
|
||||||
|
// Watch the pref
|
||||||
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
this,
|
||||||
|
"_enabledTypes",
|
||||||
|
PREF_ENABLED_TYPES,
|
||||||
|
"",
|
||||||
|
() => this._updateAllHandlers(),
|
||||||
|
pref => {
|
||||||
|
let itemStr = pref.trim();
|
||||||
|
return itemStr ? itemStr.split(",").map(s => s.trim()) : [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let handlerType of this._downloadTypesViewableInternally) {
|
||||||
|
if (handlerType.initAvailable) {
|
||||||
|
handlerType.initAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially update handlers
|
||||||
|
this._updateAllHandlers();
|
||||||
|
|
||||||
|
// Register the check for use in DownloadIntegration
|
||||||
|
Integration.downloads.register(base => ({
|
||||||
|
shouldViewDownloadInternally: this._shouldViewDownloadInternally.bind(
|
||||||
|
this
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIME types to handle with an internal viewer, for downloaded files.
|
||||||
|
*
|
||||||
|
* |extension| is an extenson that will be viewable, as an alternative for
|
||||||
|
* the MIME type itself. It is also used more generally to identify this
|
||||||
|
* type: It is part of a pref name to indicate the handler was set up once,
|
||||||
|
* and it is the string present in |PREF_ENABLED_TYPES| to enable the type.
|
||||||
|
*
|
||||||
|
* |mimeTypes| are the types that will be viewable. A handler is set up for
|
||||||
|
* the first element in the array.
|
||||||
|
*
|
||||||
|
* If |managedElsewhere| is falsy, |_updateAllHandlers()| will set
|
||||||
|
* up or remove handlers for the type, and |_shouldViewDownloadInternally()|
|
||||||
|
* will check for it in |PREF_ENABLED_TYPES|.
|
||||||
|
*
|
||||||
|
* |available| is used to check whether this type should have
|
||||||
|
* handleInternally handlers set up, and if false then
|
||||||
|
* |_shouldViewDownloadInternally()| will also return false for this
|
||||||
|
* type. If |available| would change, |DownloadsViewableInternally._updateHandler()|
|
||||||
|
* should be called for the type.
|
||||||
|
*
|
||||||
|
* |initAvailable()| is an opportunity to initially set |available|, set up
|
||||||
|
* observers to change it when prefs change, etc.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_downloadTypesViewableInternally: [
|
||||||
|
{
|
||||||
|
extension: "xml",
|
||||||
|
mimeTypes: ["text/xml", "application/xml"],
|
||||||
|
available: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extension: "svg",
|
||||||
|
mimeTypes: ["image/svg+xml"],
|
||||||
|
|
||||||
|
initAvailable() {
|
||||||
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
this,
|
||||||
|
"available",
|
||||||
|
"svg.disabled",
|
||||||
|
true,
|
||||||
|
() => DownloadsViewableInternally._updateHandler(this),
|
||||||
|
// transform disabled to enabled/available
|
||||||
|
disabledPref => !disabledPref
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// available getter is set by initAvailable()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extension: "webp",
|
||||||
|
mimeTypes: ["image/webp"],
|
||||||
|
initAvailable() {
|
||||||
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
this,
|
||||||
|
"available",
|
||||||
|
"image.webp.enabled",
|
||||||
|
false,
|
||||||
|
() => DownloadsViewableInternally._updateHandler(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// available getter is set by initAvailable()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extension: "avif",
|
||||||
|
mimeTypes: ["image/avif"],
|
||||||
|
initAvailable() {
|
||||||
|
// NOTE: This does not handle MOZ_AV1, which determines whether
|
||||||
|
// the browser is built with AV1, and therefore AVIF, support.
|
||||||
|
// When image.avif.enabled is set true by default in
|
||||||
|
// StaticPrefList.yaml, it should be wrapped in #ifdef MOZ_AV1
|
||||||
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
this,
|
||||||
|
"available",
|
||||||
|
"image.avif.enabled",
|
||||||
|
false,
|
||||||
|
() => DownloadsViewableInternally._updateHandler(this)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// available getter is set by initAvailable()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
extension: "pdf",
|
||||||
|
mimeTypes: ["application/pdf"],
|
||||||
|
// PDF uses pdfjs.disabled rather than PREF_ENABLED_TYPES.
|
||||||
|
// pdfjs.disabled isn't checked here because PdfJs's own _becomeHandler
|
||||||
|
// and _unbecomeHandler manage the handler if the pref is set, and there
|
||||||
|
// is an explicit check in nsUnknownContentTypeDialog.shouldShowInternalHandlerOption
|
||||||
|
available: true,
|
||||||
|
managedElsewhere: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation for DownloadIntegration.shouldViewDownloadInternally
|
||||||
|
*/
|
||||||
|
_shouldViewDownloadInternally(aMimeType, aExtension) {
|
||||||
|
if (!aMimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._downloadTypesViewableInternally.some(handlerType => {
|
||||||
|
if (
|
||||||
|
!handlerType.managedElsewhere &&
|
||||||
|
!this._enabledTypes.includes(handlerType.extension)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
(handlerType.mimeTypes.includes(aMimeType) ||
|
||||||
|
handlerType.extension == aExtension?.toLowerCase()) &&
|
||||||
|
handlerType.available
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_makeFakeHandler(aMimeType, aExtension) {
|
||||||
|
// Based on PdfJs gPdfFakeHandlerInfo.
|
||||||
|
return {
|
||||||
|
QueryInterface: ChromeUtils.generateQI(["nsIMIMEInfo"]),
|
||||||
|
getFileExtensions() {
|
||||||
|
return [aExtension];
|
||||||
|
},
|
||||||
|
possibleApplicationHandlers: Cc["@mozilla.org/array;1"].createInstance(
|
||||||
|
Ci.nsIMutableArray
|
||||||
|
),
|
||||||
|
extensionExists(ext) {
|
||||||
|
return ext == aExtension;
|
||||||
|
},
|
||||||
|
alwaysAskBeforeHandling: false,
|
||||||
|
preferredAction: Ci.nsIHandlerInfo.handleInternally,
|
||||||
|
type: aMimeType,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveSettings(handlerInfo, handlerType) {
|
||||||
|
Services.prefs.setIntPref(
|
||||||
|
PREF_BRANCH_PREVIOUS_ACTION + handlerType.extension,
|
||||||
|
handlerInfo.preferredAction
|
||||||
|
);
|
||||||
|
Services.prefs.setBoolPref(
|
||||||
|
PREF_BRANCH_PREVIOUS_ASK + handlerType.extension,
|
||||||
|
handlerInfo.alwaysAskBeforeHandling
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_restoreSettings(handlerInfo, handlerType) {
|
||||||
|
const prevActionPref = PREF_BRANCH_PREVIOUS_ACTION + handlerType.extension;
|
||||||
|
if (Services.prefs.prefHasUserValue(prevActionPref)) {
|
||||||
|
handlerInfo.alwaysAskBeforeHandling = Services.prefs.getBoolPref(
|
||||||
|
PREF_BRANCH_PREVIOUS_ASK + handlerType.extension
|
||||||
|
);
|
||||||
|
handlerInfo.preferredAction = Services.prefs.getIntPref(prevActionPref);
|
||||||
|
HandlerService.store(handlerInfo);
|
||||||
|
} else {
|
||||||
|
// Nothing to restore, just remove the handler.
|
||||||
|
HandlerService.remove(handlerInfo);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearSavedSettings(extension) {
|
||||||
|
Services.prefs.clearUserPref(PREF_BRANCH_PREVIOUS_ACTION + extension);
|
||||||
|
Services.prefs.clearUserPref(PREF_BRANCH_PREVIOUS_ASK + extension);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateAllHandlers() {
|
||||||
|
// Set up or remove handlers for each type, if not done already
|
||||||
|
for (const handlerType of this._downloadTypesViewableInternally) {
|
||||||
|
if (!handlerType.managedElsewhere) {
|
||||||
|
this._updateHandler(handlerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateHandler(handlerType) {
|
||||||
|
const wasRegistered = Services.prefs.getBoolPref(
|
||||||
|
PREF_BRANCH_WAS_REGISTERED + handlerType.extension,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
const toBeRegistered =
|
||||||
|
this._enabledTypes.includes(handlerType.extension) &&
|
||||||
|
handlerType.available;
|
||||||
|
|
||||||
|
if (toBeRegistered && !wasRegistered) {
|
||||||
|
this._becomeHandler(handlerType);
|
||||||
|
} else if (!toBeRegistered && wasRegistered) {
|
||||||
|
this._unbecomeHandler(handlerType);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_becomeHandler(handlerType) {
|
||||||
|
// Set up an empty handler with only a preferred action, to avoid
|
||||||
|
// having to ask the OS about handlers on startup.
|
||||||
|
let fakeHandlerInfo = this._makeFakeHandler(
|
||||||
|
handlerType.mimeTypes[0],
|
||||||
|
handlerType.extension
|
||||||
|
);
|
||||||
|
if (!HandlerService.exists(fakeHandlerInfo)) {
|
||||||
|
HandlerService.store(fakeHandlerInfo);
|
||||||
|
} else {
|
||||||
|
const handlerInfo = MIMEService.getFromTypeAndExtension(
|
||||||
|
handlerType.mimeTypes[0],
|
||||||
|
handlerType.extension
|
||||||
|
);
|
||||||
|
|
||||||
|
if (handlerInfo.preferredAction != Ci.nsIHandlerInfo.handleInternally) {
|
||||||
|
// Save the previous settings of preferredAction and
|
||||||
|
// alwaysAskBeforeHandling in case we need to revert them.
|
||||||
|
// Even if we don't force preferredAction here, the user could
|
||||||
|
// set handleInternally manually.
|
||||||
|
this._saveSettings(handlerInfo, handlerType);
|
||||||
|
} else {
|
||||||
|
// handleInternally shouldn't already have been set, the best we
|
||||||
|
// can do to restore is to remove the handler, so make sure
|
||||||
|
// the settings are clear.
|
||||||
|
this._clearSavedSettings(handlerType.extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the preferred action if it didn't indicate an external viewer.
|
||||||
|
// Note: This is a point of departure from PdfJs, which always replaces
|
||||||
|
// the preferred action.
|
||||||
|
if (
|
||||||
|
handlerInfo.preferredAction != Ci.nsIHandlerInfo.useHelperApp &&
|
||||||
|
handlerInfo.preferredAction != Ci.nsIHandlerInfo.useSystemDefault
|
||||||
|
) {
|
||||||
|
handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
|
||||||
|
handlerInfo.alwaysAskBeforeHandling = false;
|
||||||
|
|
||||||
|
HandlerService.store(handlerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we set up for this type so a) we don't keep replacing the
|
||||||
|
// handler and b) so it can be cleared later.
|
||||||
|
Services.prefs.setBoolPref(
|
||||||
|
PREF_BRANCH_WAS_REGISTERED + handlerType.extension,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_unbecomeHandler(handlerType) {
|
||||||
|
let handlerInfo;
|
||||||
|
try {
|
||||||
|
handlerInfo = MIMEService.getFromTypeAndExtension(
|
||||||
|
handlerType.mimeTypes[0],
|
||||||
|
handlerType.extension
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
// Allow the handler lookup to fail.
|
||||||
|
}
|
||||||
|
// Restore preferred action if it is still handleInternally
|
||||||
|
// (possibly just removing the handler if nothing was saved for it).
|
||||||
|
if (handlerInfo?.preferredAction == Ci.nsIHandlerInfo.handleInternally) {
|
||||||
|
this._restoreSettings(handlerInfo, handlerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In any case we do not control this handler now.
|
||||||
|
this._clearSavedSettings(handlerType.extension);
|
||||||
|
Services.prefs.clearUserPref(
|
||||||
|
PREF_BRANCH_WAS_REGISTERED + handlerType.extension
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -15,6 +15,7 @@ EXTRA_JS_MODULES += [
|
||||||
'DownloadsCommon.jsm',
|
'DownloadsCommon.jsm',
|
||||||
'DownloadsSubview.jsm',
|
'DownloadsSubview.jsm',
|
||||||
'DownloadsTaskbar.jsm',
|
'DownloadsTaskbar.jsm',
|
||||||
|
'DownloadsViewableInternally.jsm',
|
||||||
'DownloadsViewUI.jsm',
|
'DownloadsViewUI.jsm',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -26,4 +27,4 @@ if toolkit == 'cocoa':
|
||||||
with Files('**'):
|
with Files('**'):
|
||||||
BUG_COMPONENT = ('Firefox', 'Downloads Panel')
|
BUG_COMPONENT = ('Firefox', 'Downloads Panel')
|
||||||
|
|
||||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const PREF_SVG_DISABLED = "svg.disabled";
|
||||||
|
const PREF_WEBP_ENABLED = "image.webp.enabled";
|
||||||
|
const PDF_MIME = "application/pdf";
|
||||||
|
const OCTET_MIME = "application/octet-stream";
|
||||||
|
const XML_MIME = "text/xml";
|
||||||
|
const SVG_MIME = "image/svg+xml";
|
||||||
|
const WEBP_MIME = "image/webp";
|
||||||
|
|
||||||
|
const { Integration } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/Integration.jsm"
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
DownloadsViewableInternally,
|
||||||
|
PREF_ENABLED_TYPES,
|
||||||
|
PREF_BRANCH_WAS_REGISTERED,
|
||||||
|
PREF_BRANCH_PREVIOUS_ACTION,
|
||||||
|
PREF_BRANCH_PREVIOUS_ASK,
|
||||||
|
} = ChromeUtils.import("resource:///modules/DownloadsViewableInternally.jsm");
|
||||||
|
|
||||||
|
/* global DownloadIntegration */
|
||||||
|
Integration.downloads.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"DownloadIntegration",
|
||||||
|
"resource://gre/modules/DownloadIntegration.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
|
const HandlerService = Cc[
|
||||||
|
"@mozilla.org/uriloader/handler-service;1"
|
||||||
|
].getService(Ci.nsIHandlerService);
|
||||||
|
const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||||
|
|
||||||
|
function checkPreferInternal(mime, ext, expectedPreferInternal) {
|
||||||
|
const handler = MIMEService.getFromTypeAndExtension(mime, ext);
|
||||||
|
if (expectedPreferInternal) {
|
||||||
|
Assert.equal(
|
||||||
|
handler?.preferredAction,
|
||||||
|
Ci.nsIHandlerInfo.handleInternally,
|
||||||
|
`checking ${mime} preferredAction == handleInternally`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Assert.notEqual(
|
||||||
|
handler?.preferredAction,
|
||||||
|
Ci.nsIHandlerInfo.handleInternally,
|
||||||
|
`checking ${mime} preferredAction != handleInternally`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldView(mime, ext) {
|
||||||
|
return DownloadIntegration.shouldViewDownloadInternally(mime, ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkShouldView(mime, ext, expectedShouldView) {
|
||||||
|
Assert.equal(
|
||||||
|
shouldView(mime, ext),
|
||||||
|
expectedShouldView,
|
||||||
|
`checking ${mime} shouldViewDownloadInternally`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkWasRegistered(ext, expectedWasRegistered) {
|
||||||
|
Assert.equal(
|
||||||
|
Services.prefs.getBoolPref(PREF_BRANCH_WAS_REGISTERED + ext, false),
|
||||||
|
expectedWasRegistered,
|
||||||
|
`checking ${ext} was registered pref`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAll(mime, ext, expected) {
|
||||||
|
checkPreferInternal(mime, ext, expected);
|
||||||
|
checkShouldView(mime, ext, expected);
|
||||||
|
checkWasRegistered(ext, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_viewable_internally() {
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "xml , svg,webp");
|
||||||
|
Services.prefs.setBoolPref(PREF_SVG_DISABLED, false);
|
||||||
|
Services.prefs.setBoolPref(PREF_WEBP_ENABLED, true);
|
||||||
|
|
||||||
|
checkAll(XML_MIME, "xml", false);
|
||||||
|
checkAll(SVG_MIME, "svg", false);
|
||||||
|
checkAll(WEBP_MIME, "webp", false);
|
||||||
|
|
||||||
|
DownloadsViewableInternally.register();
|
||||||
|
|
||||||
|
checkAll(XML_MIME, "xml", true);
|
||||||
|
checkAll(SVG_MIME, "svg", true);
|
||||||
|
checkAll(WEBP_MIME, "webp", true);
|
||||||
|
|
||||||
|
// Remove SVG so it won't be cleared
|
||||||
|
Services.prefs.clearUserPref(PREF_BRANCH_WAS_REGISTERED + "svg");
|
||||||
|
|
||||||
|
// Disable xml and svg, check that xml becomes disabled
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "webp");
|
||||||
|
|
||||||
|
checkAll(XML_MIME, "xml", false);
|
||||||
|
checkAll(WEBP_MIME, "webp", true);
|
||||||
|
|
||||||
|
// SVG shouldn't be cleared
|
||||||
|
checkPreferInternal(SVG_MIME, "svg", true);
|
||||||
|
|
||||||
|
Assert.ok(
|
||||||
|
shouldView(PDF_MIME),
|
||||||
|
"application/pdf should be unaffected by pref"
|
||||||
|
);
|
||||||
|
Assert.ok(
|
||||||
|
shouldView(OCTET_MIME, "pdf"),
|
||||||
|
".pdf should be accepted by extension"
|
||||||
|
);
|
||||||
|
Assert.ok(
|
||||||
|
shouldView(OCTET_MIME, "PDF"),
|
||||||
|
".pdf should be detected case-insensitively"
|
||||||
|
);
|
||||||
|
Assert.ok(!shouldView(OCTET_MIME, "exe"), ".exe shouldn't be accepted");
|
||||||
|
|
||||||
|
Assert.ok(!shouldView(XML_MIME), "text/xml should be disabled by pref");
|
||||||
|
Assert.ok(!shouldView(SVG_MIME), "image/xml+svg should be disabled by pref");
|
||||||
|
|
||||||
|
// Enable, check that everything is enabled again
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "xml,svg,webp");
|
||||||
|
|
||||||
|
checkPreferInternal(XML_MIME, "xml", true);
|
||||||
|
checkPreferInternal(SVG_MIME, "svg", true);
|
||||||
|
|
||||||
|
Assert.ok(
|
||||||
|
shouldView(PDF_MIME),
|
||||||
|
"application/pdf should be unaffected by pref"
|
||||||
|
);
|
||||||
|
Assert.ok(shouldView(XML_MIME), "text/xml should be enabled by pref");
|
||||||
|
Assert.ok(
|
||||||
|
shouldView("application/xml"),
|
||||||
|
"alternate MIME type application/xml should be accepted"
|
||||||
|
);
|
||||||
|
Assert.ok(
|
||||||
|
shouldView(OCTET_MIME, "xml"),
|
||||||
|
".xml should be accepted by extension"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disable viewable internally, pre-set handlers.
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "");
|
||||||
|
|
||||||
|
for (const [mime, ext, action, ask] of [
|
||||||
|
[XML_MIME, "xml", Ci.nsIHandlerInfo.useSystemDefault, true],
|
||||||
|
[SVG_MIME, "svg", Ci.nsIHandlerInfo.saveToDisk, true],
|
||||||
|
[WEBP_MIME, "webp", Ci.nsIHandlerInfo.saveToDisk, false],
|
||||||
|
]) {
|
||||||
|
let handler = MIMEService.getFromTypeAndExtension(mime, ext);
|
||||||
|
handler.preferredAction = action;
|
||||||
|
handler.alwaysAskBeforeHandling = ask;
|
||||||
|
|
||||||
|
HandlerService.store(handler);
|
||||||
|
checkPreferInternal(mime, ext, false);
|
||||||
|
|
||||||
|
// Expect to read back the same values
|
||||||
|
handler = MIMEService.getFromTypeAndExtension(mime, ext);
|
||||||
|
Assert.equal(handler.preferredAction, action);
|
||||||
|
Assert.equal(handler.alwaysAskBeforeHandling, ask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable viewable internally, XML should not be replaced, SVG and WebP should be saved.
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "svg,webp,xml");
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
Services.prefs.getIntPref(PREF_BRANCH_PREVIOUS_ACTION + "svg"),
|
||||||
|
Ci.nsIHandlerInfo.saveToDisk,
|
||||||
|
"svg action should be saved"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
Services.prefs.getBoolPref(PREF_BRANCH_PREVIOUS_ASK + "svg"),
|
||||||
|
true,
|
||||||
|
"svg ask should be saved"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
Services.prefs.getIntPref(PREF_BRANCH_PREVIOUS_ACTION + "webp"),
|
||||||
|
Ci.nsIHandlerInfo.saveToDisk,
|
||||||
|
"webp action should be saved"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
Services.prefs.getBoolPref(PREF_BRANCH_PREVIOUS_ASK + "webp"),
|
||||||
|
false,
|
||||||
|
"webp ask should be saved"
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
let handler = MIMEService.getFromTypeAndExtension(XML_MIME, "xml");
|
||||||
|
Assert.equal(
|
||||||
|
handler.preferredAction,
|
||||||
|
Ci.nsIHandlerInfo.useSystemDefault,
|
||||||
|
"svg action should be preserved"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
!!handler.alwaysAskBeforeHandling,
|
||||||
|
true,
|
||||||
|
"svg ask should be preserved"
|
||||||
|
);
|
||||||
|
// Clean up
|
||||||
|
HandlerService.remove(handler);
|
||||||
|
}
|
||||||
|
// It should still be possible to view XML internally
|
||||||
|
checkShouldView(XML_MIME, "xml", true);
|
||||||
|
checkWasRegistered("xml", true);
|
||||||
|
|
||||||
|
checkAll(SVG_MIME, "svg", true);
|
||||||
|
checkAll(WEBP_MIME, "webp", true);
|
||||||
|
|
||||||
|
// Disable SVG to test SVG enabled check (depends on the pref)
|
||||||
|
Services.prefs.setBoolPref(PREF_SVG_DISABLED, true);
|
||||||
|
// Should have restored the settings from above
|
||||||
|
{
|
||||||
|
let handler = MIMEService.getFromTypeAndExtension(SVG_MIME, "svg");
|
||||||
|
Assert.equal(handler.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
|
||||||
|
Assert.equal(!!handler.alwaysAskBeforeHandling, true);
|
||||||
|
// Clean up
|
||||||
|
HandlerService.remove(handler);
|
||||||
|
}
|
||||||
|
checkAll(SVG_MIME, "svg", false);
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref(PREF_SVG_DISABLED, false);
|
||||||
|
checkAll(SVG_MIME, "svg", true);
|
||||||
|
|
||||||
|
// Test WebP enabled check (depends on the pref)
|
||||||
|
Services.prefs.setBoolPref(PREF_WEBP_ENABLED, false);
|
||||||
|
// Should have restored the settings from above
|
||||||
|
{
|
||||||
|
let handler = MIMEService.getFromTypeAndExtension(WEBP_MIME, "webp");
|
||||||
|
Assert.equal(handler.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
|
||||||
|
Assert.equal(!!handler.alwaysAskBeforeHandling, false);
|
||||||
|
// Clean up
|
||||||
|
HandlerService.remove(handler);
|
||||||
|
}
|
||||||
|
checkAll(WEBP_MIME, "webp", false);
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref(PREF_WEBP_ENABLED, true);
|
||||||
|
checkAll(WEBP_MIME, "webp", true);
|
||||||
|
|
||||||
|
Assert.ok(!shouldView(null, "pdf"), "missing MIME shouldn't be accepted");
|
||||||
|
Assert.ok(!shouldView(null, "xml"), "missing MIME shouldn't be accepted");
|
||||||
|
Assert.ok(!shouldView(OCTET_MIME), "unsupported MIME shouldn't be accepted");
|
||||||
|
Assert.ok(!shouldView(OCTET_MIME, "exe"), ".exe shouldn't be accepted");
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
// Clear all types to remove any saved values
|
||||||
|
Services.prefs.setCharPref(PREF_ENABLED_TYPES, "");
|
||||||
|
// Reset to the defaults
|
||||||
|
Services.prefs.clearUserPref(PREF_ENABLED_TYPES);
|
||||||
|
Services.prefs.clearUserPref(PREF_SVG_DISABLED);
|
||||||
|
Services.prefs.clearUserPref(PREF_WEBP_ENABLED);
|
||||||
|
});
|
||||||
|
|
@ -6,3 +6,4 @@ skip-if = toolkit == 'android'
|
||||||
|
|
||||||
[test_DownloadsCommon_getMimeInfo.js]
|
[test_DownloadsCommon_getMimeInfo.js]
|
||||||
[test_DownloadsCommon_isFileOfType.js]
|
[test_DownloadsCommon_isFileOfType.js]
|
||||||
|
[test_DownloadsViewableInternally.js]
|
||||||
|
|
|
||||||
|
|
@ -698,6 +698,21 @@ var DownloadIntegration = {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide whether a download of this type, opened from the downloads
|
||||||
|
* list, should open internally.
|
||||||
|
*
|
||||||
|
* @param aMimeType
|
||||||
|
* The MIME type of the file, as a string
|
||||||
|
* @param [optional] aExtension
|
||||||
|
* The file extension, which can match instead of the MIME type.
|
||||||
|
*/
|
||||||
|
shouldViewDownloadInternally(aMimeType, aExtension) {
|
||||||
|
// Refuse all files by default, this is meant to be replaced with a check
|
||||||
|
// for specific types via Integration.downloads.register().
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launches a file represented by the target of a download. This can
|
* Launches a file represented by the target of a download. This can
|
||||||
* open the file with the default application for the target MIME type
|
* open the file with the default application for the target MIME type
|
||||||
|
|
@ -794,8 +809,6 @@ var DownloadIntegration = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PDF_CONTENT_TYPE = "application/pdf";
|
|
||||||
|
|
||||||
if (!useSystemDefault && mimeInfo) {
|
if (!useSystemDefault && mimeInfo) {
|
||||||
useSystemDefault = mimeInfo.preferredAction == mimeInfo.useSystemDefault;
|
useSystemDefault = mimeInfo.preferredAction == mimeInfo.useSystemDefault;
|
||||||
}
|
}
|
||||||
|
|
@ -804,8 +817,7 @@ var DownloadIntegration = {
|
||||||
if (
|
if (
|
||||||
aDownload.handleInternally ||
|
aDownload.handleInternally ||
|
||||||
(mimeInfo &&
|
(mimeInfo &&
|
||||||
(mimeInfo.type == PDF_CONTENT_TYPE ||
|
this.shouldViewDownloadInternally(mimeInfo.type, fileExtension) &&
|
||||||
fileExtension?.toLowerCase() == "pdf") &&
|
|
||||||
!mimeInfo.alwaysAskBeforeHandling &&
|
!mimeInfo.alwaysAskBeforeHandling &&
|
||||||
mimeInfo.preferredAction === Ci.nsIHandlerInfo.handleInternally &&
|
mimeInfo.preferredAction === Ci.nsIHandlerInfo.handleInternally &&
|
||||||
!aDownload.launchWhenSucceeded)
|
!aDownload.launchWhenSucceeded)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue