Bug 1457027 - Part 4 - Use class syntax for the HandlerInfoWrapper hierarchy. r=jaws
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Mon, 07 May 2018 09:33:36 +0100
changeset 417146 a2dd752c009e025c7033e82f7e14fed83c555e5b
parent 417145 d3afbfe98a712ee4775f7da7156e94f6e424d239
child 417147 93bda045ca0b78b4154f750b46dc9aa408915b12
push id33961
push userrgurzau@mozilla.com
push dateMon, 07 May 2018 22:08:28 +0000
treeherdermozilla-central@59005ba3cd3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1457027
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457027 - Part 4 - Use class syntax for the HandlerInfoWrapper hierarchy. r=jaws This also does some minor clean up of declarations and code comments. MozReview-Commit-ID: 31uRma7NDl8
browser/components/preferences/in-content/main.js
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1376,17 +1376,17 @@ var gMainPane = {
     audioFeedHandlerInfo.handledOnlyByPlugin = false;
   },
 
   /**
    * Load higher level internal handlers so they can be turned on/off in the
    * applications menu.
    */
   _loadInternalHandlers() {
-    var internalHandlers = [pdfHandlerInfo];
+    var internalHandlers = [new PDFHandlerInfoWrapper()];
     for (let internalHandler of internalHandlers) {
       if (internalHandler.enabled) {
         this._handledTypes[internalHandler.type] = internalHandler;
       }
     }
   },
 
   /**
@@ -1980,16 +1980,24 @@ var gMainPane = {
   focusFilterBox() {
     this._filter.focus();
     this._filter.select();
   },
 
 
   // Changes
 
+  // Whether or not we are currently storing the action selected by the user.
+  // We use this to suppress notification-triggered updates to the list when
+  // we make changes that may spawn such updates, specifically when we change
+  // the action for the feed type, which results in feed preference updates,
+  // which spawn "pref changed" notifications that would otherwise cause us
+  // to rebuild the view unnecessarily.
+  _storingAction: false,
+
   onSelectAction(aActionItem) {
     this._storingAction = true;
 
     try {
       this._storeAction(aActionItem);
     } finally {
       this._storingAction = false;
     }
@@ -2610,77 +2618,81 @@ function isFeedType(t) {
  * the list of handled types.
  *
  * We create an instance of this wrapper for each entry we might display
  * in the prefpane, and we compose the instances from various sources,
  * including plugins and the handler service.
  *
  * We don't implement all the original nsIHandlerInfo functionality,
  * just the stuff that the prefpane needs.
- *
- * In theory, all of the custom functionality in this wrapper should get
- * pushed down into nsIHandlerInfo eventually.
  */
-function HandlerInfoWrapper(aType, aHandlerInfo) {
-  this._type = aType;
-  this.wrappedHandlerInfo = aHandlerInfo;
-}
-
-HandlerInfoWrapper.prototype = {
-  // The wrapped nsIHandlerInfo object.  In general, this object is private,
-  // but there are a couple cases where callers access it directly for things
-  // we haven't (yet?) implemented, so we make it a public property.
-  wrappedHandlerInfo: null,
-
-
-  // nsIHandlerInfo
-
-  // The MIME type or protocol scheme.
-  _type: null,
-  get type() {
-    return this._type;
-  },
+class HandlerInfoWrapper {
+  constructor(type, handlerInfo) {
+    this.type = type;
+    this.wrappedHandlerInfo = handlerInfo;
+
+    // A plugin that can handle this type, if any.
+    //
+    // Note: just because we have one doesn't mean it *will* handle the type.
+    // That depends on whether or not the type is in the list of types for which
+    // plugin handling is disabled.
+    this.pluginName = "";
+
+    // Whether or not this type is only handled by a plugin or is also handled
+    // by some user-configured action as specified in the handler info object.
+    //
+    // Note: we can't just check if there's a handler info object for this type,
+    // because OS and user configuration is mixed up in the handler info object,
+    // so we always need to retrieve it for the OS info and can't tell whether
+    // it represents only OS-default information or user-configured information.
+    //
+    // FIXME: once handler info records are broken up into OS-provided records
+    // and user-configured records, stop using this boolean flag and simply
+    // check for the presence of a user-configured record to determine whether
+    // or not this type is only handled by a plugin.  Filed as bug 395142.
+    this.handledOnlyByPlugin = false;
+  }
 
   get description() {
     if (this.wrappedHandlerInfo.description)
       return this.wrappedHandlerInfo.description;
 
     if (this.primaryExtension) {
       var extension = this.primaryExtension.toUpperCase();
       return gMainPane._prefsBundle.getFormattedString("fileEnding",
         [extension]);
     }
 
     return this.type;
-  },
+  }
 
   get preferredApplicationHandler() {
     return this.wrappedHandlerInfo.preferredApplicationHandler;
-  },
+  }
 
   set preferredApplicationHandler(aNewValue) {
     this.wrappedHandlerInfo.preferredApplicationHandler = aNewValue;
 
     // Make sure the preferred handler is in the set of possible handlers.
     if (aNewValue)
       this.addPossibleApplicationHandler(aNewValue);
-  },
+  }
 
   get possibleApplicationHandlers() {
     return this.wrappedHandlerInfo.possibleApplicationHandlers;
-  },
+  }
 
   addPossibleApplicationHandler(aNewHandler) {
     var possibleApps = this.possibleApplicationHandlers.enumerate();
     while (possibleApps.hasMoreElements()) {
       if (possibleApps.getNext().equals(aNewHandler))
         return;
     }
     this.possibleApplicationHandlers.appendElement(aNewHandler);
-  },
+  }
 
   removePossibleApplicationHandler(aHandler) {
     var defaultApp = this.preferredApplicationHandler;
     if (defaultApp && aHandler.equals(defaultApp)) {
       // If the app we remove was the default app, we must make sure
       // it won't be used anymore
       this.alwaysAskBeforeHandling = true;
       this.preferredApplicationHandler = null;
@@ -2689,25 +2701,25 @@ HandlerInfoWrapper.prototype = {
     var handlers = this.possibleApplicationHandlers;
     for (var i = 0; i < handlers.length; ++i) {
       var handler = handlers.queryElementAt(i, Ci.nsIHandlerApp);
       if (handler.equals(aHandler)) {
         handlers.removeElementAt(i);
         break;
       }
     }
-  },
+  }
 
   get hasDefaultHandler() {
     return this.wrappedHandlerInfo.hasDefaultHandler;
-  },
+  }
 
   get defaultDescription() {
     return this.wrappedHandlerInfo.defaultDescription;
-  },
+  }
 
   // What to do with content of this type.
   get preferredAction() {
     // If we have an enabled plugin, then the action is to use that plugin.
     if (this.pluginName && !this.isDisabledPluginType)
       return kActionUsePlugin;
 
     // If the action is to use a helper app, but we don't have a preferred
@@ -2719,17 +2731,17 @@ HandlerInfoWrapper.prototype = {
     if (this.wrappedHandlerInfo.preferredAction == Ci.nsIHandlerInfo.useHelperApp &&
       !gMainPane.isValidHandlerApp(this.preferredApplicationHandler)) {
       if (this.wrappedHandlerInfo.hasDefaultHandler)
         return Ci.nsIHandlerInfo.useSystemDefault;
       return Ci.nsIHandlerInfo.saveToDisk;
     }
 
     return this.wrappedHandlerInfo.preferredAction;
-  },
+  }
 
   set preferredAction(aNewValue) {
     // If the action is to use the plugin,
     // we must set the preferred action to "save to disk".
     // But only if it's not currently the preferred action.
     if ((aNewValue == kActionUsePlugin) &&
       (this.preferredAction != Ci.nsIHandlerInfo.saveToDisk)) {
       aNewValue = Ci.nsIHandlerInfo.saveToDisk;
@@ -2737,17 +2749,17 @@ HandlerInfoWrapper.prototype = {
 
     // We don't modify the preferred action if the new action is to use a plugin
     // because handler info objects don't understand our custom "use plugin"
     // value.  Also, leaving it untouched means that we can automatically revert
     // to the old setting if the user ever removes the plugin.
 
     if (aNewValue != kActionUsePlugin)
       this.wrappedHandlerInfo.preferredAction = aNewValue;
-  },
+  }
 
   get alwaysAskBeforeHandling() {
     // If this type is handled only by a plugin, we can't trust the value
     // in the handler info object, since it'll be a default based on the absence
     // of any user configuration, and the default in that case is to always ask,
     // even though we never ask for content handled by a plugin, so special case
     // plugin-handled types by returning false here.
     if (this.pluginName && this.handledOnlyByPlugin)
@@ -2759,96 +2771,69 @@ HandlerInfoWrapper.prototype = {
     // app, but the preferredApplicationHandler is invalid, and there isn't
     // a default handler, so the preferredAction getter returns save to disk
     // instead.
     if (!(this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo) &&
       this.preferredAction == Ci.nsIHandlerInfo.saveToDisk)
       return true;
 
     return this.wrappedHandlerInfo.alwaysAskBeforeHandling;
-  },
+  }
 
   set alwaysAskBeforeHandling(aNewValue) {
     this.wrappedHandlerInfo.alwaysAskBeforeHandling = aNewValue;
-  },
-
-
-  // nsIMIMEInfo
+  }
 
   // The primary file extension associated with this type, if any.
   //
   // XXX Plugin objects contain an array of MimeType objects with "suffixes"
   // properties; if this object has an associated plugin, shouldn't we check
   // those properties for an extension?
   get primaryExtension() {
     try {
       if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo &&
         this.wrappedHandlerInfo.primaryExtension)
         return this.wrappedHandlerInfo.primaryExtension;
     } catch (ex) { }
 
     return null;
-  },
-
-
-  // Plugin Handling
-
-  // A plugin that can handle this type, if any.
-  //
-  // Note: just because we have one doesn't mean it *will* handle the type.
-  // That depends on whether or not the type is in the list of types for which
-  // plugin handling is disabled.
-  plugin: null,
-
-  // Whether or not this type is only handled by a plugin or is also handled
-  // by some user-configured action as specified in the handler info object.
-  //
-  // Note: we can't just check if there's a handler info object for this type,
-  // because OS and user configuration is mixed up in the handler info object,
-  // so we always need to retrieve it for the OS info and can't tell whether
-  // it represents only OS-default information or user-configured information.
-  //
-  // FIXME: once handler info records are broken up into OS-provided records
-  // and user-configured records, stop using this boolean flag and simply
-  // check for the presence of a user-configured record to determine whether
-  // or not this type is only handled by a plugin.  Filed as bug 395142.
-  handledOnlyByPlugin: undefined,
+  }
 
   get isDisabledPluginType() {
     return this._getDisabledPluginTypes().includes(this.type);
-  },
+  }
 
   _getDisabledPluginTypes() {
     var types = "";
 
     if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES))
       types = Services.prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES);
 
     // Only split if the string isn't empty so we don't end up with an array
     // containing a single empty string.
     if (types != "")
       return types.split(",");
 
     return [];
-  },
+  }
 
   disablePluginType() {
     var disabledPluginTypes = this._getDisabledPluginTypes();
 
     if (!disabledPluginTypes.includes(this.type))
       disabledPluginTypes.push(this.type);
 
     Services.prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
       disabledPluginTypes.join(","));
 
     // Update the category manager so existing browser windows update.
     gCategoryManager.deleteCategoryEntry("Gecko-Content-Viewers",
       this.type,
       false);
-  },
+  }
 
   enablePluginType() {
     var disabledPluginTypes = this._getDisabledPluginTypes();
 
     var type = this.type;
     disabledPluginTypes = disabledPluginTypes.filter(v => v != type);
 
     Services.prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES,
@@ -2856,75 +2841,56 @@ HandlerInfoWrapper.prototype = {
 
     // Update the category manager so existing browser windows update.
     gCategoryManager.addCategoryEntry(
       "Gecko-Content-Viewers",
       this.type,
       "@mozilla.org/content/plugin/document-loader-factory;1",
       false,
       true);
-  },
-
-
-  // Storage
+  }
 
   store() {
     gHandlerService.store(this.wrappedHandlerInfo);
-  },
-
-
-  // Icons
+  }
 
   get smallIcon() {
     return this._getIcon(16);
-  },
+  }
 
   _getIcon(aSize) {
     if (this.primaryExtension)
       return "moz-icon://goat." + this.primaryExtension + "?size=" + aSize;
 
     if (this.wrappedHandlerInfo instanceof Ci.nsIMIMEInfo)
       return "moz-icon://goat?size=" + aSize + "&contentType=" + this.type;
 
     // FIXME: consider returning some generic icon when we can't get a URL for
     // one (for example in the case of protocol schemes).  Filed as bug 395141.
     return null;
   }
-
-};
-
-
-// Feed Handler Info
+}
 
 /**
  * This object implements nsIHandlerInfo for the feed types.  It's a separate
  * object because we currently store handling information for the feed type
  * in a set of preferences rather than the nsIHandlerService-managed datastore.
  *
  * This object inherits from HandlerInfoWrapper in order to get functionality
  * that isn't special to the feed type.
- *
- * XXX Should we inherit from HandlerInfoWrapper?  After all, we override
- * most of that wrapper's properties and methods, and we have to dance around
- * the fact that the wrapper expects to have a wrappedHandlerInfo, which we
- * don't provide.
  */
-
-function FeedHandlerInfo(aMIMEType) {
-  HandlerInfoWrapper.call(this, aMIMEType, null);
-}
-
-FeedHandlerInfo.prototype = {
-  __proto__: HandlerInfoWrapper.prototype,
-
-  // nsIHandlerInfo
+class FeedHandlerInfo extends HandlerInfoWrapper {
+  constructor(aMIMEType, properties) {
+    super(aMIMEType, null);
+    Object.assign(this, properties);
+  }
 
   get description() {
     return gMainPane._prefsBundle.getString(this._appPrefLabel);
-  },
+  }
 
   get preferredApplicationHandler() {
     switch (Preferences.get(this._prefSelectedReader).value) {
       case "client":
         var file = Preferences.get(this._prefSelectedApp).value;
         if (file)
           return getLocalHandlerApp(file);
 
@@ -2938,36 +2904,34 @@ FeedHandlerInfo.prototype = {
 
       case "bookmarks":
       default:
         // When the pref is set to bookmarks, we handle feeds internally,
         // we don't forward them to a local or web handler app, so there is
         // no preferred handler.
         return null;
     }
-  },
+  }
 
   set preferredApplicationHandler(aNewValue) {
     if (aNewValue instanceof Ci.nsILocalHandlerApp) {
       Preferences.get(this._prefSelectedApp).value = aNewValue.executable;
       Preferences.get(this._prefSelectedReader).value = "client";
     } else if (aNewValue instanceof Ci.nsIWebContentHandlerInfo) {
       Preferences.get(this._prefSelectedWeb).value = aNewValue.uri;
       Preferences.get(this._prefSelectedReader).value = "web";
       // Make the web handler be the new "auto handler" for feeds.
       // Note: we don't have to unregister the auto handler when the user picks
       // a non-web handler (local app, Live Bookmarks, etc.) because the service
       // only uses the "auto handler" when the selected reader is a web handler.
       // We also don't have to unregister it when the user turns on "always ask"
       // (i.e. preview in browser), since that also overrides the auto handler.
       gWebContentContentConverterService.setAutoHandler(this.type, aNewValue);
     }
-  },
-
-  _possibleApplicationHandlers: null,
+  }
 
   get possibleApplicationHandlers() {
     if (this._possibleApplicationHandlers)
       return this._possibleApplicationHandlers;
 
     // A minimal implementation of nsIMutableArray.  It only supports the two
     // methods its callers invoke, namely appendElement and nsIArray::enumerate.
     this._possibleApplicationHandlers = {
@@ -3013,19 +2977,18 @@ FeedHandlerInfo.prototype = {
     }
 
     // Add the registered web handlers.  There can be any number of these.
     var webHandlers = gWebContentContentConverterService.getContentHandlers(this.type);
     for (let webHandler of webHandlers)
       this._possibleApplicationHandlers.appendElement(webHandler);
 
     return this._possibleApplicationHandlers;
-  },
-
-  __defaultApplicationHandler: undefined,
+  }
+
   get _defaultApplicationHandler() {
     if (typeof this.__defaultApplicationHandler != "undefined")
       return this.__defaultApplicationHandler;
 
     var defaultFeedReader = null;
     if (AppConstants.HAVE_SHELL_SERVICE) {
       try {
         defaultFeedReader = getShellService().defaultFeedReader;
@@ -3042,38 +3005,38 @@ FeedHandlerInfo.prototype = {
       handlerApp.executable = defaultFeedReader;
 
       this.__defaultApplicationHandler = handlerApp;
     } else {
       this.__defaultApplicationHandler = null;
     }
 
     return this.__defaultApplicationHandler;
-  },
+  }
 
   get hasDefaultHandler() {
     if (AppConstants.HAVE_SHELL_SERVICE) {
       try {
         if (getShellService().defaultFeedReader)
           return true;
       } catch (ex) {
         // no default reader or getShellService() is null
       }
     }
 
     return false;
-  },
+  }
 
   get defaultDescription() {
     if (this.hasDefaultHandler)
       return this._defaultApplicationHandler.name;
 
     // Should we instead return null?
     return "";
-  },
+  }
 
   // What to do with content of this type.
   get preferredAction() {
     switch (Preferences.get(this._prefSelectedAction).value) {
 
       case "bookmarks":
         return Ci.nsIHandlerInfo.handleInternally;
 
@@ -3099,17 +3062,17 @@ FeedHandlerInfo.prototype = {
 
       // If the action is "ask", then alwaysAskBeforeHandling will override
       // the action, so it doesn't matter what we say it is, it just has to be
       // something that doesn't cause the controller to hide the type.
       case "ask":
       default:
         return Ci.nsIHandlerInfo.handleInternally;
     }
-  },
+  }
 
   set preferredAction(aNewValue) {
     switch (aNewValue) {
 
       case Ci.nsIHandlerInfo.handleInternally:
         Preferences.get(this._prefSelectedReader).value = "bookmarks";
         break;
 
@@ -3119,46 +3082,32 @@ FeedHandlerInfo.prototype = {
         // to the new helper app.
         break;
 
       case Ci.nsIHandlerInfo.useSystemDefault:
         Preferences.get(this._prefSelectedAction).value = "reader";
         this.preferredApplicationHandler = this._defaultApplicationHandler;
         break;
     }
-  },
+  }
 
   get alwaysAskBeforeHandling() {
     return Preferences.get(this._prefSelectedAction).value == "ask";
-  },
+  }
 
   set alwaysAskBeforeHandling(aNewValue) {
     if (aNewValue)
       Preferences.get(this._prefSelectedAction).value = "ask";
     else
       Preferences.get(this._prefSelectedAction).value = "reader";
-  },
-
-  // Whether or not we are currently storing the action selected by the user.
-  // We use this to suppress notification-triggered updates to the list when
-  // we make changes that may spawn such updates, specifically when we change
-  // the action for the feed type, which results in feed preference updates,
-  // which spawn "pref changed" notifications that would otherwise cause us
-  // to rebuild the view unnecessarily.
-  _storingAction: false,
-
-
-  // nsIMIMEInfo
+  }
 
   get primaryExtension() {
     return "xml";
-  },
-
-
-  // Storage
+  }
 
   // Changes to the preferred action and handler take effect immediately
   // (we write them out to the preferences right as they happen),
   // so we when the controller calls store() after modifying the handlers,
   // the only thing we need to store is the removal of possible handlers
   // XXX Should we hold off on making the changes until this method gets called?
   store() {
     for (let app of this._possibleApplicationHandlers._removed) {
@@ -3172,89 +3121,86 @@ FeedHandlerInfo.prototype = {
         }
       } else {
         app.QueryInterface(Ci.nsIWebContentHandlerInfo);
         gWebContentContentConverterService.removeContentHandler(app.contentType,
                                                                 app.uri);
       }
     }
     this._possibleApplicationHandlers._removed = [];
-  },
-
-
-  // Icons
+  }
 
   get smallIcon() {
     return this._smallIcon;
   }
-
-};
-
-var feedHandlerInfo = {
-  __proto__: new FeedHandlerInfo(TYPE_MAYBE_FEED),
+}
+
+var feedHandlerInfo = new FeedHandlerInfo(TYPE_MAYBE_FEED, {
   _prefSelectedApp: PREF_FEED_SELECTED_APP,
   _prefSelectedWeb: PREF_FEED_SELECTED_WEB,
   _prefSelectedAction: PREF_FEED_SELECTED_ACTION,
   _prefSelectedReader: PREF_FEED_SELECTED_READER,
   _smallIcon: "chrome://browser/skin/feeds/feedIcon16.png",
   _appPrefLabel: "webFeed"
-};
-
-var videoFeedHandlerInfo = {
-  __proto__: new FeedHandlerInfo(TYPE_MAYBE_VIDEO_FEED),
+});
+
+var videoFeedHandlerInfo = new FeedHandlerInfo(TYPE_MAYBE_VIDEO_FEED, {
   _prefSelectedApp: PREF_VIDEO_FEED_SELECTED_APP,
   _prefSelectedWeb: PREF_VIDEO_FEED_SELECTED_WEB,
   _prefSelectedAction: PREF_VIDEO_FEED_SELECTED_ACTION,
   _prefSelectedReader: PREF_VIDEO_FEED_SELECTED_READER,
   _smallIcon: "chrome://browser/skin/feeds/videoFeedIcon16.png",
   _appPrefLabel: "videoPodcastFeed"
-};
-
-var audioFeedHandlerInfo = {
-  __proto__: new FeedHandlerInfo(TYPE_MAYBE_AUDIO_FEED),
+});
+
+var audioFeedHandlerInfo = new FeedHandlerInfo(TYPE_MAYBE_AUDIO_FEED, {
   _prefSelectedApp: PREF_AUDIO_FEED_SELECTED_APP,
   _prefSelectedWeb: PREF_AUDIO_FEED_SELECTED_WEB,
   _prefSelectedAction: PREF_AUDIO_FEED_SELECTED_ACTION,
   _prefSelectedReader: PREF_AUDIO_FEED_SELECTED_READER,
   _smallIcon: "chrome://browser/skin/feeds/audioFeedIcon16.png",
   _appPrefLabel: "audioPodcastFeed"
-};
+});
 
 /**
  * InternalHandlerInfoWrapper provides a basic mechanism to create an internal
  * mime type handler that can be enabled/disabled in the applications preference
  * menu.
  */
-function InternalHandlerInfoWrapper(aMIMEType) {
-  var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
-  var handlerInfo = mimeSvc.getFromTypeAndExtension(aMIMEType, null);
-
-  HandlerInfoWrapper.call(this, aMIMEType, handlerInfo);
-}
-
-InternalHandlerInfoWrapper.prototype = {
-  __proto__: HandlerInfoWrapper.prototype,
+class InternalHandlerInfoWrapper extends HandlerInfoWrapper {
+  constructor(mimeType) {
+    super(mimeType, gMIMEService.getFromTypeAndExtension(mimeType, null));
+  }
 
   // Override store so we so we can notify any code listening for registration
   // or unregistration of this handler.
   store() {
-    HandlerInfoWrapper.prototype.store.call(this);
+    super.store();
     Services.obs.notifyObservers(null, this._handlerChanged);
-  },
+  }
 
   get enabled() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  },
+  }
 
   get description() {
     return gMainPane._prefsBundle.getString(this._appPrefLabel);
   }
-};
-
-var pdfHandlerInfo = {
-  __proto__: new InternalHandlerInfoWrapper(TYPE_PDF),
-  _handlerChanged: TOPIC_PDFJS_HANDLER_CHANGED,
-  _appPrefLabel: "portableDocumentFormat",
+}
+
+class PDFHandlerInfoWrapper extends InternalHandlerInfoWrapper {
+  constructor() {
+    super(TYPE_PDF);
+  }
+
+  get _handlerChanged() {
+    return TOPIC_PDFJS_HANDLER_CHANGED;
+  }
+
+  get _appPrefLabel() {
+    return "portableDocumentFormat";
+  }
+
   get enabled() {
     return !Services.prefs.getBoolPref(PREF_PDFJS_DISABLED) &&
            Services.policies.isAllowed("PDF.js");
-  },
-};
+  }
+}