home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbCDRipServicePaneService.js < prev    next >
Text File  |  2012-06-08  |  22KB  |  650 lines

  1. /*
  2.  *=BEGIN SONGBIRD GPL
  3.  *
  4.  * This file is part of the Songbird web player.
  5.  *
  6.  * Copyright(c) 2005-2010 POTI, Inc.
  7.  * http://www.songbirdnest.com
  8.  *
  9.  * This file may be licensed under the terms of of the
  10.  * GNU General Public License Version 2 (the ``GPL'').
  11.  *
  12.  * Software distributed under the License is distributed
  13.  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
  14.  * express or implied. See the GPL for the specific language
  15.  * governing rights and limitations.
  16.  *
  17.  * You should have received a copy of the GPL along with this
  18.  * program. If not, go to http://www.gnu.org/licenses/gpl.html
  19.  * or write to the Free Software Foundation, Inc.,
  20.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21.  *
  22.  *=END SONGBIRD GPL
  23.  */
  24.  
  25. if (typeof(Cc) == "undefined")
  26.   var Cc = Components.classes;
  27. if (typeof(Ci) == "undefined")
  28.   var Ci = Components.interfaces;
  29. if (typeof(Cr) == "undefined")
  30.   var Cr = Components.results;
  31. if (typeof(Cu) == "undefined")
  32.   var Cu = Components.utils;
  33.  
  34. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  35. Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
  36. Cu.import("resource://app/jsmodules/DOMUtils.jsm");
  37. Cu.import("resource://app/jsmodules/PlatformUtils.jsm");
  38. Cu.import("resource://app/jsmodules/sbProperties.jsm");
  39.  
  40. const CDRIPNS = 'http://songbirdnest.com/rdf/servicepane/cdrip#';
  41. const SPNS = 'http://songbirdnest.com/rdf/servicepane#';
  42.  
  43. // For some device events we need to check if they are from the CD Device
  44. // marshall and not others.
  45. const CDDEVICEMARSHALLNAME = "sbCDDeviceMarshall";
  46.  
  47. var sbCDRipServicePaneServiceConfig = {
  48.   className:      "Songbird CD Rip Device Support Module",
  49.   cid:            Components.ID("{9925b565-5c19-4feb-87a8-413d86570cd9}"),
  50.   contractID:     "@songbirdnest.com/servicepane/cdDevice;1",
  51.   
  52.   ifList: [ Ci.sbIServicePaneModule,
  53.             Ci.nsIObserver ],
  54.             
  55.   categoryList:
  56.   [
  57.     {
  58.       category: "service-pane",
  59.       entry: "cdrip-device"
  60.     }
  61.   ],
  62.  
  63.   devCatName:     "CD Rip Device",
  64.  
  65.   appQuitTopic:   "quit-application",
  66.  
  67.   devMgrURL:      "chrome://songbird/content/mediapages/cdripMediaView.xul"
  68. };
  69. if ("sbIWindowsAutoPlayActionHandler" in Ci) {
  70.   sbCDRipServicePaneServiceConfig.ifList.push(Ci.sbIWindowsAutoPlayActionHandler);
  71. }
  72.  
  73. function sbCDRipServicePaneService() {
  74.  
  75. }
  76.  
  77. sbCDRipServicePaneService.prototype.constructor = sbCDRipServicePaneService;
  78.  
  79. sbCDRipServicePaneService.prototype = {
  80.   classDescription: sbCDRipServicePaneServiceConfig.className,
  81.   classID: sbCDRipServicePaneServiceConfig.cid,
  82.   contractID: sbCDRipServicePaneServiceConfig.contractID,
  83.  
  84.   _cfg: sbCDRipServicePaneServiceConfig,
  85.   _xpcom_categories: sbCDRipServicePaneServiceConfig.categoryList,
  86.  
  87.   // Services to use.
  88.   _deviceManagerSvc:      null,
  89.   _deviceServicePaneSvc:  null,
  90.   _observerSvc:           null,
  91.   _servicePaneSvc:        null,
  92.  
  93.   _deviceInfoList:        [],
  94.   
  95.   _deviceScanInProgress:  false,
  96.   
  97.   // ************************************
  98.   // sbIServicePaneService implementation
  99.   // ************************************
  100.   servicePaneInit: function sbCDRipServicePaneService_servicePaneInit(aServicePaneService) {
  101.     this._servicePaneSvc = aServicePaneService;
  102.     this._initialize();
  103.   },
  104.  
  105.   fillContextMenu: function sbCDRipServicePaneService_fillContextMenu(aNode,
  106.                                                                   aContextMenu,
  107.                                                                   aParentWindow) {
  108.     // Get the node device ID.  Do nothing if not a device node.
  109.     var deviceID = aNode.getAttributeNS(CDRIPNS, "DeviceId");
  110.     if (!deviceID)
  111.       return;
  112.   
  113.     // Get the device node type.
  114.     var deviceNodeType = aNode.getAttributeNS(CDRIPNS, "deviceNodeType");
  115.  
  116.     // Import device context menu items into the context menu.
  117.     if (deviceNodeType == "cd-device") {
  118.       DOMUtils.importChildElements(aContextMenu,
  119.                                    this._deviceContextMenuDoc,
  120.                                    "cddevice_context_menu_items",
  121.                                    { "device-id": deviceID,
  122.                                      "service_pane_node_id": aNode.id });
  123.     }
  124.   },
  125.  
  126.   fillNewItemMenu: function sbCDRipServicePaneService_fillNewItemMenu(aNode,
  127.                                                                   aContextMenu,
  128.                                                                   aParentWindow) {
  129.   },
  130.  
  131.   onSelectionChanged: function sbCDRipServicePaneService_onSelectionChanged(aNode,
  132.                                                           aContainer,
  133.                                                           aParentWindow) {
  134.   },
  135.  
  136.   canDrop: function sbCDRipServicePaneService_canDrop(aNode, 
  137.                                                   aDragSession, 
  138.                                                   aOrientation, 
  139.                                                   aWindow) {
  140.     // Currently no drag and drop allowed
  141.     return false;
  142.   },
  143.  
  144.   onDrop: function sbCDRipServicePaneService_onDrop(aNode, 
  145.                                                 aDragSession, 
  146.                                                 aOrientation, 
  147.                                                 aWindow) {
  148.     // Currently no drag and drop allowed
  149.   },
  150.   
  151.   onDragGesture: function sbCDRipServicePaneService_onDragGesture(aNode, 
  152.                                                               aDataTransfer) {
  153.     // Currently no drag and drop allowed
  154.   },
  155.  
  156.   onBeforeRename: function sbCDRipServicePaneService_onBeforeRename(aNode) {
  157.     // Rename is not allowed for CD Devices
  158.   },
  159.  
  160.   onRename: function sbCDRipServicePaneService_onRename(aNode, 
  161.                                                     aNewName) {
  162.     // Rename is not allowed for CD Devices
  163.   },
  164.  
  165.   shutdown: function sbCDRipServicePaneService_shutdown() {
  166.     // Do nothing, since we shut down on quit-application
  167.   },
  168.  
  169.   // ************************************
  170.   // nsIObserver implementation
  171.   // ************************************
  172.   observe: function sbCDRipServicePaneService_observe(aSubject, 
  173.                                                   aTopic, 
  174.                                                   aData) {
  175.     switch (aTopic) {
  176.       case this._cfg.appQuitTopic :
  177.         this._shutdown();
  178.       break;
  179.     }
  180.  
  181.   },
  182.   
  183.   // ************************************
  184.   // nsISupports implementation
  185.   // ************************************
  186.   QueryInterface: XPCOMUtils.generateQI(sbCDRipServicePaneServiceConfig.ifList),
  187.  
  188.   // ************************************
  189.   // Internal methods
  190.   // ************************************
  191.   
  192.   /**
  193.    * \brief Initialize the CD Device nodes.
  194.    */
  195.   _initialize: function sbCDRipServicePaneService_initialize() {
  196.     this._observerSvc = Cc["@mozilla.org/observer-service;1"]
  197.                           .getService(Ci.nsIObserverService);
  198.     
  199.     this._observerSvc.addObserver(this, this._cfg.appQuitTopic, false);
  200.  
  201.     this._deviceServicePaneSvc = Cc["@songbirdnest.com/servicepane/device;1"]
  202.                                    .getService(Ci.sbIDeviceServicePaneService);
  203.  
  204.     this._deviceManagerSvc = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
  205.                                .getService(Ci.sbIDeviceManager2);
  206.  
  207.     // Add a listener for CDDevice Events
  208.     var deviceEventListener = {
  209.       cdDeviceServicePaneSvc: this,
  210.       
  211.       onDeviceEvent: function deviceEventListener_onDeviceEvent(aDeviceEvent) {
  212.         this.cdDeviceServicePaneSvc._processDeviceManagerEvent(aDeviceEvent);
  213.       }
  214.     };
  215.     
  216.     this._deviceEventListener = deviceEventListener;
  217.     this._deviceManagerSvc.addEventListener(deviceEventListener);
  218.     
  219.     // load the cd-device context menu document
  220.     this._deviceContextMenuDoc =
  221.           DOMUtils.loadDocument
  222.             ("chrome://songbird/content/xul/device/deviceContextMenu.xul");
  223.  
  224.     if (PlatformUtils.platformString == "Windows_NT") {
  225.       // Register autoplay handler
  226.       var autoPlayActionHandler = {
  227.         cdDeviceServicePaneSvc: this,
  228.  
  229.         handleAction: function autoPlayActionHandler_handleAction(aAction, aActionArg) {
  230.           return this.cdDeviceServicePaneSvc._processAutoPlayAction(aAction);
  231.         },
  232.  
  233.         QueryInterface: XPCOMUtils.generateQI([Ci.sbIWindowsAutoPlayActionHandler])
  234.       }
  235.  
  236.       this._autoPlayActionHandler = autoPlayActionHandler;
  237.       var windowsAutoPlayService =
  238.             Cc["@songbirdnest.com/Songbird/WindowsAutoPlayService;1"]
  239.               .getService(Ci.sbIWindowsAutoPlayService);
  240.       windowsAutoPlayService.addActionHandler
  241.         (autoPlayActionHandler,
  242.          Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP);
  243.     }
  244.   },
  245.   
  246.   /**
  247.    * \brief Shutdown and remove the CD Device nodes.
  248.    */
  249.   _shutdown: function sbCDRipServicePaneService_shutdown() {
  250.     this._observerSvc.removeObserver(this, this._cfg.appQuitTopic);
  251.  
  252.     this._deviceManagerSvc.removeEventListener(this._deviceEventListener);
  253.     this._deviceEventListener = null;
  254.     
  255.     if (PlatformUtils.platformString == "Windows_NT") {
  256.       // Unregister autoplay handler
  257.       var windowsAutoPlayService =
  258.             Cc["@songbirdnest.com/Songbird/WindowsAutoPlayService;1"]
  259.               .getService(Ci.sbIWindowsAutoPlayService);
  260.       windowsAutoPlayService.removeActionHandler
  261.         (this._autoPlayActionHandler,
  262.          Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP);
  263.       this._autoPlayActionHandler = null;
  264.     }
  265.  
  266.     // Remove all references to nodes
  267.     this._deviceInfoList = [];
  268.  
  269.     this._deviceManagerSvc = null;
  270.     this._deviceServicePaneSvc = null;
  271.     this._servicePaneSvc = null;
  272.     this._observerSvc = null;
  273.   },
  274.  
  275.   /**
  276.    * \brief Process events from the Device Manager.
  277.    * \param aDeviceEvent - Event that occured for a device.
  278.    */
  279.   _processDeviceManagerEvent:
  280.     function sbCDRipServicePaneService_processDeviceManagerEvent(aDeviceEvent) {
  281.  
  282.     switch(aDeviceEvent.type) {
  283.       case Ci.sbIDeviceEvent.EVENT_DEVICE_SCAN_START:
  284.         var marshall = aDeviceEvent.origin.QueryInterface(Ci.sbIDeviceMarshall);
  285.         if (marshall.name == CDDEVICEMARSHALLNAME)
  286.           this._deviceScanInProgress = true;
  287.         break;
  288.       
  289.       case Ci.sbIDeviceEvent.EVENT_DEVICE_SCAN_END:
  290.         var marshall = aDeviceEvent.origin.QueryInterface(Ci.sbIDeviceMarshall);
  291.         if (marshall.name == CDDEVICEMARSHALLNAME)
  292.           this._deviceScanInProgress = false;
  293.         break;
  294.       
  295.       case Ci.sbIDeviceEvent.EVENT_DEVICE_ADDED:
  296.         var result = this._addDeviceFromEvent(aDeviceEvent);
  297.  
  298.         // if we successfully added the device, switch the media tab
  299.         // to the CD rip view as long as this is not during the initial scan
  300.         // of CD Devices.
  301.         if (result)
  302.           this._loadCDViewFromEvent(aDeviceEvent);
  303.         break;
  304.  
  305.       case Ci.sbIDeviceEvent.EVENT_DEVICE_REMOVED:
  306.         this._removeDeviceFromEvent(aDeviceEvent);
  307.         break;
  308.  
  309.       case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_INITIATED:
  310.         this._updateState(aDeviceEvent, true);
  311.         break;
  312.  
  313.       case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_COMPLETED:
  314.         this._updateState(aDeviceEvent, false);
  315.         break;
  316.  
  317.       case Ci.sbICDDeviceEvent.EVENT_CDLOOKUP_METADATA_COMPLETE:
  318.         this._updateState(aDeviceEvent, false);
  319.         break;
  320.  
  321.       case Ci.sbIDeviceEvent.EVENT_DEVICE_STATE_CHANGED:
  322.         this._updateState(aDeviceEvent, false);
  323.         break;
  324.  
  325.       default:
  326.         break;
  327.     }
  328.   },
  329.  
  330.   /**
  331.    * \brief Handles autoplay actions initiated by the user.
  332.    * \param aAction - Action to be handled.
  333.    */
  334.   _processAutoPlayAction:
  335.       function sbCDRipServicePaneService_processAutoPlayAction(aAction) {
  336.  
  337.     switch (aAction) {
  338.       case Ci.sbIWindowsAutoPlayService.ACTION_CD_RIP:
  339.         // No way to tell which CD the user meant to rip, let's take one randomly
  340.         // (hopefully the last one enumerated is also the last one added)
  341.         var deviceNode = null;
  342.         for each (let deviceInfo in this._deviceInfoList) {
  343.           deviceNode = deviceInfo.svcPaneNode;
  344.         }
  345.  
  346.         if (deviceNode) {
  347.           Cc['@mozilla.org/appshell/window-mediator;1']
  348.             .getService(Ci.nsIWindowMediator)
  349.             .getMostRecentWindow('Songbird:Main').gBrowser
  350.             .loadURI(deviceNode.url, null, null, null, "_media");
  351.         }
  352.         else {
  353.           // CD is probably not recognized yet, don't do anything - the view
  354.           // will switch to it anyway once it is.
  355.         }
  356.  
  357.         return true;
  358.  
  359.       default:
  360.         return false;
  361.     }
  362.   },
  363.  
  364.   /**
  365.    * \brief Load the CD Rip media view as response to a device event
  366.    * \param aDeviceEvent - Device event being acted upon.
  367.    */
  368.   _loadCDViewFromEvent:
  369.       function sbCDRipServicePaneService_loadCDViewFromEvent(aDeviceEvent) {
  370.  
  371.     // We only want to do this if it is not during a device scan (usually at
  372.     // startup).
  373.     if (this._deviceScanInProgress)
  374.       return;
  375.  
  376.     var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
  377.     var url = this._cfg.devMgrURL + "?device-id=" + device.id;
  378.     Cc['@mozilla.org/appshell/window-mediator;1']
  379.       .getService(Ci.nsIWindowMediator)
  380.       .getMostRecentWindow('Songbird:Main').gBrowser
  381.       .loadURI(url, null, null, null, "_media");
  382.   },
  383.  
  384.   /**
  385.    * \brief Updates the state of the service pane node.
  386.    * \param aDeviceEvent - Device event of device.
  387.    */
  388.   _updateState: function sbCDRipServicePaneService__updateState(aDeviceEvent,
  389.                                                                 aForceBusy) {
  390.     // Get the device and its node.
  391.     var device = aDeviceEvent.origin.QueryInterface(Ci.sbIDevice);
  392.     var deviceId = device.id;
  393.     var deviceType = device.parameters.getProperty("DeviceType");
  394.  
  395.     // We only care about CD devices
  396.     if (deviceType != "CD")
  397.       return;
  398.  
  399.     if (device.state == Ci.sbIDevice.STATE_TRANSCODE) {
  400.       this._toggleReadOnly(true, device);
  401.     }
  402.     else if (device.state == Ci.sbIDevice.STATE_IDLE) {
  403.       this._toggleReadOnly(false, device);
  404.     }
  405.  
  406.     if (typeof(this._deviceInfoList[deviceId]) != 'undefined') {
  407.       var devNode = this._deviceInfoList[deviceId].svcPaneNode;
  408.  
  409.       // The friendly name might have changed, keep it in sync.
  410.       if (devNode.name != device.properties.friendlyName) {
  411.         devNode.name = device.properties.friendlyName;
  412.       }
  413.  
  414.       // Get the device properties and clear the busy property.
  415.       devProperties = devNode.className.split(" ");
  416.       devProperties = devProperties.filter(function(aProperty) {
  417.                                              return aProperty != "busy";
  418.                                            });
  419.  
  420.       // Set the busy property if the device is busy.
  421.       if ((device.state != Ci.sbIDevice.STATE_CANCEL) && 
  422.           (aForceBusy || device.state != Ci.sbIDevice.STATE_IDLE)) {
  423.           // Clear success state from previous rip
  424.         devProperties = devProperties.filter(function(aProperty) {
  425.                                                return aProperty != "successful" &&
  426.                                                       aProperty != "unsuccessful";
  427.                                              });
  428.         devProperties.push("busy");
  429.       } else {
  430.         if (devNode.hasAttributeNS(CDRIPNS, "LastState")) {
  431.           var lastState = devNode.getAttributeNS(CDRIPNS, "LastState");
  432.           if (lastState == Ci.sbIDevice.STATE_TRANSCODE) {
  433.             if (this._checkErrors(device)) {
  434.               devProperties.push("unsuccessful");
  435.             } else if (this._checkSuccess(device)){
  436.               devProperties.push("successful");
  437.             }
  438.             //if neither _checkErrors nor _checkSuccess is true, then the user
  439.             //cancelled before any successes or fails.  Go back to idle image.
  440.           }
  441.         }
  442.       }
  443.       devNode.setAttributeNS(CDRIPNS, "LastState", device.state);
  444.  
  445.       // Write back the device node properties.
  446.       devNode.className = devProperties.join(" ");
  447.     }
  448.   },
  449.  
  450.   /**
  451.    * Get the devices library (defaults to first library)
  452.    * \param aDevice - Device to get library from
  453.    */
  454.   _getDeviceLibrary: function sbCDRipServicePaneService__getDeviceLibrary(aDevice) {
  455.     // Get the libraries for device
  456.     var libraries = aDevice.content.libraries;
  457.     if (libraries.length < 1) {
  458.       // Oh no, we have no libraries
  459.       Cu.reportError("Device " + aDevice.id + " has no libraries!");
  460.       return null;
  461.     }
  462.  
  463.     // Get the requested library
  464.     var deviceLibrary = libraries.queryElementAt(0, Ci.sbIMediaList);
  465.     if (!deviceLibrary) {
  466.       Cu.reportError("Unable to get library for device: " + aDevice.id);
  467.       return null;
  468.     }
  469.     
  470.     return deviceLibrary;
  471.   },
  472.   
  473.   /**
  474.    * Turn the read only flag for the library on or off. This allows us to disable
  475.    * changes to the CD Device library during a rip.
  476.    * \param aReadOnly - Mark as read only (Disable changes) if true.
  477.    * \param aDevice - CD Device to toggle read only.
  478.    */
  479.   _toggleReadOnly: function sbCDRipServicePaneService__toggleReadOnly(aReadOnly,
  480.                                                                       aDevice) {
  481.     var deviceLibrary = this._getDeviceLibrary(aDevice);
  482.     if (deviceLibrary)
  483.       deviceLibrary.setProperty(SBProperties.isReadOnly,
  484.                                 (aReadOnly ? "1" : "0"));
  485.   },
  486.  
  487.   /**
  488.    * Check for any ripped tracks that failed
  489.    * \param aDevice - Device to check
  490.    * \return True if errors occured, false otherwise
  491.    */
  492.   _checkErrors: function sbCDRipServicePaneService__checkErrors(aDevice) {
  493.     // Check for any tracks that have a failed status
  494.     var deviceLibrary = this._getDeviceLibrary(aDevice);
  495.     var errorCount = 0;
  496.  
  497.     try {
  498.       // Get all the did not successfully ripped tracks
  499.       var propArray =
  500.         Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
  501.           .createInstance(Ci.sbIMutablePropertyArray);
  502.       propArray.appendProperty(SBProperties.cdRipStatus, "3|100");
  503.       propArray.appendProperty(SBProperties.shouldRip, "1");
  504.       
  505.       var rippedItems = deviceLibrary.getItemsByProperties(propArray);
  506.       errorCount = rippedItems.length;
  507.     }
  508.     catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
  509.       // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
  510.       // if there are no failed rips in the list, thus no errors.
  511.       return false;
  512.     }
  513.     catch (err) {
  514.       Cu.reportError("ERROR GETTING TRANSCODE ERROR COUNT " + err);
  515.     }
  516.     
  517.     return (errorCount > 0);
  518.   },
  519.   
  520.   /**
  521.    * Check if any tracks successfully ripped
  522.    * \param aDevice - Device to check
  523.    * \return True if at least one track was ripped, false otherwise
  524.    */
  525.   _checkSuccess: function sbCDRipServicePaneService__checkErrors(aDevice) {
  526.     // Check if any tracks were successfully ripped
  527.     var deviceLibrary = this._getDeviceLibrary(aDevice);
  528.     var successCount = 0;
  529.  
  530.     try {
  531.       var propArray = Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
  532.           .createInstance(Ci.sbIMutablePropertyArray);
  533.       propArray.appendProperty(SBProperties.cdRipStatus, "2|100");
  534.       propArray.appendProperty(SBProperties.shouldRip, "1");
  535.  
  536.       var rippedItems = deviceLibrary.getItemsByProperties(propArray);
  537.       successCount = rippedItems.length;
  538.     }
  539.     catch (err if err.result == Cr.NS_ERROR_NOT_AVAILABLE) {
  540.       // deviceLibrary.getItemsByProperties() will throw NS_ERROR_NOT_AVAILABLE
  541.       // if there are no successful rips in the list.
  542.       return false;
  543.     }
  544.     catch (err) {
  545.       Cu.reportError("ERROR GETTING TRANSCODE SUCCESS COUNT " + err);
  546.     }
  547.  
  548.     return (successCount > 0);
  549.   },
  550.   
  551.   /**
  552.    * \brief Add a device that has media from event information.
  553.    * \param aDeviceEvent - Device event of added device.
  554.    */
  555.   _addDeviceFromEvent:
  556.     function sbCDRipServicePaneService_addDeviceFromEvent(aDeviceEvent) {
  557.  
  558.     var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
  559.     var deviceType = device.parameters.getProperty("DeviceType");
  560.  
  561.     // We only care about CD devices
  562.     if (deviceType != "CD")
  563.       return false;
  564.  
  565.     try {
  566.       this._addDevice(device);
  567.     }
  568.     catch(e) {
  569.       Cu.reportError(e);
  570.       return false;
  571.     }
  572.     return true;
  573.   },
  574.   
  575.   /**
  576.    * \brief Remove a device that no longer has media from event information.
  577.    * \param aDeviceEvent - Device event of removed device.
  578.    */
  579.   _removeDeviceFromEvent:
  580.     function sbCDRipServicePaneService_removeDeviceFromEvent(aDeviceEvent) {
  581.  
  582.     var device = aDeviceEvent.data.QueryInterface(Ci.sbIDevice);
  583.     var deviceType = device.parameters.getProperty("DeviceType");
  584.  
  585.     // We only care about CD devices
  586.     if (deviceType != "CD")
  587.       return;
  588.  
  589.     try {
  590.       this._removeDevice(device);
  591.     }
  592.     catch(e) {
  593.       Cu.reportError(e);
  594.     }
  595.   },
  596.   
  597.   /**
  598.    * \brief Add a device that has media.
  599.    * \param aDevice - Device to add, this will only be added if it is of type
  600.    *        "CD".
  601.    */
  602.   _addDevice: function sbCDRipServicePaneService_addDevice(aDevice) {
  603.     var device = aDevice.QueryInterface(Ci.sbIDevice);
  604.     var devId = device.id;
  605.     
  606.     // Do nothing if device is not an CD device.
  607.     var deviceType = device.parameters.getProperty("DeviceType");
  608.     if (deviceType != "CD") {
  609.       return;
  610.     }
  611.     
  612.     // Add a cd rip node in the service pane.
  613.     var devNode = this._deviceServicePaneSvc.createNodeForDevice2(device, true);
  614.     devNode.setAttributeNS(CDRIPNS, "DeviceId", devId);
  615.     devNode.setAttributeNS(CDRIPNS, "deviceNodeType", "cd-device");
  616.     devNode.className = "cd-device";
  617.     devNode.contractid = this._cfg.contractID;
  618.     devNode.url = this._cfg.devMgrURL + "?device-id=" + devId;
  619.     devNode.editable = false;
  620.     devNode.name = device.properties.friendlyName;
  621.  
  622.     this._deviceInfoList[devId] = {svcPaneNode: devNode};
  623.   },
  624.   
  625.   /**
  626.    * \brief Remove a known CD Device from the service pane.
  627.    * \param aDevice - Device to remove.
  628.    */
  629.   _removeDevice: function sbCDRipServicePaneService_removeDevice(aDevice) {
  630.     var device = aDevice.QueryInterface(Ci.sbIDevice);
  631.     var devId = device.id;
  632.     
  633.     var devInfo = this._deviceInfoList[devId];
  634.     if (!devInfo) {
  635.       return;
  636.     }
  637.  
  638.     // Remove the device node.
  639.     devInfo.svcPaneNode.parentNode.removeChild(devInfo.svcPaneNode);
  640.  
  641.     // Remove device info list entry.
  642.     delete this._deviceInfoList[devId];
  643.   }
  644. };
  645.  
  646. // Instantiate an XPCOM module.
  647. function NSGetModule(compMgr, fileSpec) {
  648.   return XPCOMUtils.generateModule([sbCDRipServicePaneService]);
  649. }
  650.