* |_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, managedElsewhere: 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() managedElsewhere: true, }, { extension: "webp", mimeTypes: ["image/webp"], available: true, managedElsewhere: false, }, { extension: "avif", mimeTypes: ["image/avif"], available: true, managedElsewhere: false, }, { extension: "jxl", mimeTypes: ["image/jxl"], initAvailable() { XPCOMUtils.defineLazyPreferenceGetter( this, "available", "image.jxl.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); lazy.HandlerService.store(handlerInfo); } else { // Nothing to restore, just remove the handler. lazy.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 (!lazy.HandlerService.exists(fakeHandlerInfo)) { lazy.HandlerService.store(fakeHandlerInfo); } else { const handlerInfo = lazy.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; lazy.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 = lazy.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 ); }, }; PK