sbAddToPlaylist.jsm
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25  */
26 
27 Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
28 Components.utils.import("resource://app/jsmodules/DropHelper.jsm");
29 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
30 
31 const Ci = Components.interfaces;
32 const Cc = Components.classes;
33 const Cr = Components.results;
34 
35 const ADDTOPLAYLIST_MENU_TYPE = "submenu";
36 const ADDTOPLAYLIST_MENU_ID = "library_cmd_addtoplaylist";
37 const ADDTOPLAYLIST_MENU_NAME = "&command.addtoplaylist";
38 const ADDTOPLAYLIST_MENU_TOOLTIP = "&command.tooltip.addtoplaylist";
39 const ADDTOPLAYLIST_MENU_KEY = "&command.shortcut.key.addtoplaylist";
40 const ADDTOPLAYLIST_MENU_KEYCODE = "&command.shortcut.keycode.addtoplaylist";
41 const ADDTOPLAYLIST_MENU_MODIFIERS = "&command.shortcut.modifiers.addtoplaylist";
42 const DOWNLOADTOPLAYLIST_MENU_NAME = "&command.downloadtoplaylist";
43 const DOWNLOADTOPLAYLIST_MENU_TOOLTIP = "&command.tooltip.downloadtoplaylist";
44 const DOWNLOADTOPLAYLIST_MENU_KEY = "&command.shortcut.key.downloadtoplaylist";
45 const DOWNLOADTOPLAYLIST_MENU_KEYCODE = "&command.shortcut.keycode.downloadtoplaylist";
46 const DOWNLOADTOPLAYLIST_MENU_MODIFIERS = "&command.shortcut.modifiers.downloadtoplaylist";
47 
48 
49 const ADDTOPLAYLIST_COMMAND_ID = "library_cmd_addtoplaylist:";
50 const ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID = "library_cmd_addtoplaylist_createnew";
51 
52 EXPORTED_SYMBOLS = [ "addToPlaylistHelper",
53  "SBPlaylistCommand_AddToPlaylist",
54  "SBPlaylistCommand_DownloadToPlaylist" ];
55 
63 function createUnwrapper(aSelection) {
64  var unwrapper = Cc["@songbirdnest.com/Songbird/Library/EnumeratorWrapper;1"]
65  .createInstance(Ci.sbIMediaListEnumeratorWrapper);
66  unwrapper.initialize(aSelection);
67 
68  return unwrapper;
69 }
70 
71 // ----------------------------------------------------------------------------
72 // The "Add to playlist" dynamic command object
73 // ----------------------------------------------------------------------------
75 {
76  m_Context: {
77  m_Playlist: null,
78  m_Window: null
79  },
80 
81  m_addToPlaylist: null,
82 
84  {
85  m_Types: new Array
86  (
88  ),
89 
90  m_Ids: new Array
91  (
93  ),
94 
95  m_Names: new Array
96  (
98  ),
99 
100  m_Tooltips: new Array
101  (
103  ),
104 
105  m_Keys: new Array
106  (
108  ),
109 
110  m_Keycodes: new Array
111  (
113  ),
114 
115  m_Enableds: new Array
116  (
117  true
118  ),
119 
120  m_Modifiers: new Array
121  (
123  ),
124 
125  m_PlaylistCommands: new Array
126  (
127  null
128  )
129  },
130 
131  _getMenu: function(aSubMenu)
132  {
133  var cmds;
134 
135  cmds = this.m_addToPlaylist.handleGetMenu(aSubMenu);
136  if (cmds) return cmds;
137 
138  switch (aSubMenu) {
139  default:
140  cmds = this.m_root_commands;
141  break;
142  }
143  return cmds;
144  },
145 
146  getVisible: function( aHost )
147  {
148  return true;
149  },
150 
151  getNumCommands: function( aSubMenu, aHost )
152  {
153  var cmds = this._getMenu(aSubMenu);
154  return cmds.m_Ids.length;
155  },
156 
157  getCommandId: function( aSubMenu, aIndex, aHost )
158  {
159  var cmds = this._getMenu(aSubMenu);
160  if ( aIndex >= cmds.m_Ids.length ) return "";
161  return cmds.m_Ids[ aIndex ];
162  },
163 
164  getCommandType: function( aSubMenu, aIndex, aHost )
165  {
166  var cmds = this._getMenu(aSubMenu);
167  if ( aIndex >= cmds.m_Ids.length ) return "";
168  return cmds.m_Types[ aIndex ];
169  },
170 
171  getCommandText: function( aSubMenu, aIndex, aHost )
172  {
173  var cmds = this._getMenu(aSubMenu);
174  if ( aIndex >= cmds.m_Names.length ) return "";
175  return cmds.m_Names[ aIndex ];
176  },
177 
178  getCommandFlex: function( aSubMenu, aIndex, aHost )
179  {
180  var cmds = this._getMenu(aSubMenu);
181  if ( cmds.m_Types[ aIndex ] == "separator" ) return 1;
182  return 0;
183  },
184 
185  getCommandToolTipText: function( aSubMenu, aIndex, aHost )
186  {
187  var cmds = this._getMenu(aSubMenu);
188  if ( aIndex >= cmds.m_Tooltips.length ) return "";
189  return cmds.m_Tooltips[ aIndex ];
190  },
191 
192  getCommandValue: function( aSubMenu, aIndex, aHost )
193  {
194  },
195 
196  instantiateCustomCommand: function( aDocument, aId, aHost )
197  {
198  return null;
199  },
200 
201  refreshCustomCommand: function( aElement, aId, aHost )
202  {
203  },
204 
205  getCommandVisible: function( aSubMenu, aIndex, aHost )
206  {
207  return true;
208  },
209 
210  getCommandFlag: function( aSubmenu, aIndex, aHost )
211  {
212  return false;
213  },
214 
215  getCommandChoiceItem: function( aChoiceMenu, aHost )
216  {
217  return "";
218  },
219 
220  getCommandEnabled: function( aSubMenu, aIndex, aHost )
221  {
222  if (this.m_Context.m_Playlist.mediaListView.selection.count == 0)
223  return false;
224  var cmds = this._getMenu(aSubMenu);
225  return (aIndex in cmds.m_Enableds) && cmds.m_Enableds[ aIndex ];
226  },
227 
228  getCommandShortcutModifiers: function ( aSubMenu, aIndex, aHost )
229  {
230  var cmds = this._getMenu(aSubMenu);
231  if ( aIndex >= cmds.m_Modifiers.length ) return "";
232  return cmds.m_Modifiers[ aIndex ];
233  },
234 
235  getCommandShortcutKey: function ( aSubMenu, aIndex, aHost )
236  {
237  var cmds = this._getMenu(aSubMenu);
238  if ( aIndex >= cmds.m_Keys.length ) return "";
239  return cmds.m_Keys[ aIndex ];
240  },
241 
242  getCommandShortcutKeycode: function ( aSubMenu, aIndex, aHost )
243  {
244  var cmds = this._getMenu(aSubMenu);
245  if ( aIndex >= cmds.m_Keycodes.length ) return "";
246  return cmds.m_Keycodes[ aIndex ];
247  },
248 
249  getCommandShortcutLocal: function ( aSubMenu, aIndex, aHost )
250  {
251  return true;
252  },
253 
254  getCommandSubObject: function ( aSubMenu, aIndex, aHost )
255  {
256  var cmds = this._getMenu(aSubMenu);
257  if ( aIndex >= cmds.m_PlaylistCommands.length ) return null;
258  return cmds.m_PlaylistCommands[ aIndex ];
259  },
260 
261  onCommand: function( aSubMenu, aIndex, aHost, id, value )
262  {
263  if ( id )
264  {
265  // ADDTOPLAYLIST
266  if (this.m_addToPlaylist.handleCommand(id)) return;
267 
268  // ...
269  }
270  },
271 
272  // The object registered with the sbIPlaylistCommandsManager interface acts
273  // as a template for instances bound to specific playlist elements
274 
275  dupObject: function (obj) {
276  var r = {};
277  for ( var i in obj )
278  {
279  r[ i ] = obj[ i ];
280  }
281  return r;
282  },
283 
284  duplicate: function()
285  {
286  var obj = this.dupObject(this);
287  obj.m_Context = this.dupObject(this.m_Context);
288  return obj;
289  },
290 
291  initCommands: function(aHost) {
292  if (!this.m_addToPlaylist) {
293  this.m_addToPlaylist = new addToPlaylistHelper();
294  this.m_addToPlaylist.init(this);
295  }
296  },
297 
298  shutdownCommands: function() {
299  if (!this.m_addToPlaylist) {
300  dump("this.m_addToPlaylist is null in SBPlaylistCommand_AddToPlaylist ?!!\n");
301  return;
302  }
303  this.m_addToPlaylist.shutdown();
304  this.m_addToPlaylist = null;
305  this.m_Context = null;
306  },
307 
308  setContext: function( context )
309  {
310  var playlist = context.playlist;
311  var window = context.window;
312 
313  // Ah. Sometimes, things are being secure.
314 
315  if ( playlist && playlist.wrappedJSObject )
316  playlist = playlist.wrappedJSObject;
317 
318  if ( window && window.wrappedJSObject )
319  window = window.wrappedJSObject;
320 
321  this.m_Context.m_Playlist = playlist;
322  this.m_Context.m_Window = window;
323  },
324 
325  QueryInterface : function(aIID)
326  {
327  if (!aIID.equals(Components.interfaces.sbIPlaylistCommands) &&
328  !aIID.equals(Components.interfaces.nsISupportsWeakReference) &&
329  !aIID.equals(Components.interfaces.nsISupports))
330  {
331  throw Components.results.NS_ERROR_NO_INTERFACE;
332  }
333 
334  return this;
335  }
336 }; // SBPlaylistCommand_AddToPlaylist declaration
337 
338 // Same object, different display text.
339 var SBPlaylistCommand_DownloadToPlaylist = SBPlaylistCommand_AddToPlaylist.duplicate();
340 SBPlaylistCommand_DownloadToPlaylist.m_root_commands = {
341  m_Types: new Array
342  (
344  ),
345 
346  m_Ids: new Array
347  (
349  ),
350 
351  m_Names: new Array
352  (
354  ),
355 
356  m_Tooltips: new Array
357  (
359  ),
360 
361  m_Keys: new Array
362  (
364  ),
365 
366  m_Keycodes: new Array
367  (
369  ),
370 
371  m_Enableds: new Array
372  (
373  true
374  ),
375 
376  m_Modifiers: new Array
377  (
379  ),
380 
381  m_PlaylistCommands: new Array
382  (
383  null
384  )
385 };
386 
387 
389 }
390 
391 addToPlaylistHelper.prototype.constructor = addToPlaylistHelper;
392 
393 addToPlaylistHelper.prototype = {
394  m_listofplaylists: null,
395  m_commands: null,
396  m_reglist: null,
397  m_libraryManager: null,
398 
399  LOG: function(str) {
400  var consoleService = Components.classes['@mozilla.org/consoleservice;1']
401  .getService(Components.interfaces.nsIConsoleService);
402  consoleService.logStringMessage(str);
403  },
404  init: function(aCommands) {
405  this.m_libraryManager = Components.classes["@songbirdnest.com/Songbird/library/Manager;1"]
406  .getService(Components.interfaces.sbILibraryManager);
407  this.m_libraryManager.addListener(this);
408  this.m_commands = aCommands;
409  this.makeListOfPlaylists();
410  },
411 
412  shutdown: function() {
413  this.m_libraryManager.removeListener(this);
414  this.removeLibraryListeners();
415  this.m_libraryManager = null;
416  },
417 
418  removeLibraryListeners: function() {
419  if (this.m_reglist) {
420  for (var i in this.m_reglist) {
421  this.m_reglist[i].removeListener(this);
422  }
423  }
424  this.m_reglist = new Array();
425  },
426 
427  makeListOfPlaylists: function( ) {
428  // remove previous listeners
429  this.removeLibraryListeners();
430 
431  // todo: make this smarter :(
432  var typearray = new Array('simple');
433 
434  this.m_listofplaylists = {};
435  this.m_listofplaylists.m_Types = new Array();
436  this.m_listofplaylists.m_Ids = new Array();
437  this.m_listofplaylists.m_Names = new Array();
438  this.m_listofplaylists.m_Tooltips = new Array();
439  this.m_listofplaylists.m_Enableds = new Array();
440  this.m_listofplaylists.m_Modifiers = new Array();
441  this.m_listofplaylists.m_Keys = new Array();
442  this.m_listofplaylists.m_Keycodes = new Array();
443  this.m_listofplaylists.m_PlaylistCommands = new Array();
444 
445  var libs = this.m_libraryManager.getLibraries();
446  while (libs.hasMoreElements()) {
447  var library = libs.getNext().QueryInterface(
448  Components.interfaces.sbILibrary);
449  library.addListener(this, false);
450  this.m_reglist.push(library);
451  this.makePlaylistsForLibrary(library, typearray);
452  }
453 
454  if (this.m_listofplaylists.m_Types.length == 0) {
455  this.m_listofplaylists.m_Types.push("action");
456  this.m_listofplaylists.m_Ids.push("noplaylist");
457  this.m_listofplaylists.m_Names.push("&command.addtoplaylist.noexistingplaylist");
458  this.m_listofplaylists.m_Tooltips.push("&command.tooltip.addtoplaylist.noexistingplaylist");
459  this.m_listofplaylists.m_Enableds.push(false);
460  this.m_listofplaylists.m_Modifiers.push("");
461  this.m_listofplaylists.m_Keys.push("");
462  this.m_listofplaylists.m_Keycodes.push("");
463  this.m_listofplaylists.m_PlaylistCommands.push(null);
464  }
465 
466  this.m_listofplaylists.m_Types.push("separator");
467  this.m_listofplaylists.m_Ids.push("separator");
468  this.m_listofplaylists.m_Names.push("separator");
469  this.m_listofplaylists.m_Tooltips.push("separator");
470  this.m_listofplaylists.m_Enableds.push(true);
471  this.m_listofplaylists.m_Modifiers.push("");
472  this.m_listofplaylists.m_Keys.push("");
473  this.m_listofplaylists.m_Keycodes.push("");
474  this.m_listofplaylists.m_PlaylistCommands.push(null);
475 
476  this.m_listofplaylists.m_Types.push("action");
477  this.m_listofplaylists.m_Ids.push(ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID);
478  this.m_listofplaylists.m_Names.push("&command.addtoplaylist.createnew");
479  this.m_listofplaylists.m_Tooltips.push("&command.addtoplaylist.createnew");
480  this.m_listofplaylists.m_Enableds.push(true);
481  this.m_listofplaylists.m_Modifiers.push("");
482  this.m_listofplaylists.m_Keys.push("");
483  this.m_listofplaylists.m_Keycodes.push("");
484  this.m_listofplaylists.m_PlaylistCommands.push(null);
485  },
486 
487  makePlaylistsForLibrary: function(aLibrary, typearray) {
488  this._makingList = true;
489  var listener = {
490  obj: this,
491  items: [],
492  _downloadListGUID: null,
493  onEnumerationBegin: function() {
494  var ddh =
495  Components.classes["@songbirdnest.com/Songbird/DownloadDeviceHelper;1"]
496  .getService(Components.interfaces.sbIDownloadDeviceHelper);
497  var downloadMediaList = ddh.getDownloadMediaList();
498  if (downloadMediaList)
499  this._downloadListGUID = downloadMediaList.guid;
500 
501  this._libraryServicePane =
502  Components.classes['@songbirdnest.com/servicepane/library;1']
503  .getService(Components.interfaces.sbILibraryServicePaneService);
504  },
505  onEnumerationEnd: function() { },
506  onEnumeratedItem: function(list, item) {
507  var hidden = item.getProperty("http://songbirdnest.com/data/1.0#hidden");
508  if (hidden == "1") {
509  return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
510  }
511  var goodtype = false;
512  for (var i in typearray) {
513  if (typearray[i] == item.type) {
514  goodtype = true;
515  break;
516  }
517  }
518  if (!goodtype) {
519  return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
520  }
521 
522  // XXXsteve Prevent the download playlist from appearing in the list.
523  // this should be fixable once we close bug 4017 and have a way to
524  // interrogate the policy on each playlist to see if it should be
525  // put in this menu
526  if (item.guid == this._downloadListGUID) {
527  return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
528  }
529 
530  // XXXlone also prevent playlists that do not have a corresponding node in
531  // the service pane from appearing (or those whose node, or parent nodes are
532  // hidden). This filters out remote playlists, as well as 'utility' extension
533  // playlists. This should be a fairly good test for discriminating which
534  // playlists are useful as sento targets, since it mirror the user's ability
535  // to drag and drop to them. Eventually this should be fixed by testing the
536  // policy on the playlist once we close bug 4017.
537  function isHidden(node) {
538  while (node) {
539  if (node.hidden) return true;
540  node = node.parentNode;
541  }
542  return false;
543  }
544  var node = this._libraryServicePane.getNodeForLibraryResource(item);
545  if (!node || isHidden(node)) {
546  return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
547  }
548 
549  this.obj.m_listofplaylists.m_Types.push("action");
550  this.obj.m_listofplaylists.m_Ids.push(ADDTOPLAYLIST_COMMAND_ID + item.library.guid + ";" + item.guid);
551  this.obj.m_listofplaylists.m_Names.push(item.name ? item.name : "Unnamed Playlist");
552  this.obj.m_listofplaylists.m_Tooltips.push(item.name ? item.name : "Unnamed Playlist");
553  this.obj.m_listofplaylists.m_Enableds.push(item.userEditable);
554  this.obj.m_listofplaylists.m_Modifiers.push("");
555  this.obj.m_listofplaylists.m_Keys.push("");
556  this.obj.m_listofplaylists.m_Keycodes.push("");
557  this.obj.m_listofplaylists.m_PlaylistCommands.push(null);
558 
559  return Components.interfaces.sbIMediaListEnumerationListener.CONTINUE;
560  }
561  };
562 
563  try {
564 
565  // Enumerate all lists in this library
566  aLibrary.enumerateItemsByProperty("http://songbirdnest.com/data/1.0#isList", "1",
567  listener );
568  } catch (e) {
569  // this may happen if a playlist was leaked, and is still there
570  // in the aether refreshing its commands, ignore failure, this list
571  // will never show up anymore anyway
572  }
573  this._makingList = false;
574  },
575 
576  handleGetMenu: function(aSubMenu) {
577  if (this.m_listofplaylists == null) {
578  // handleGetMenu called before makeListOfPlaylists, this would cause infinite recursion :
579  // the command object would not find the menu either, would return null to getMenu which
580  // corresponds to the root menu, and it'd recurse infinitly.
581  throw Components.results.NS_ERROR_FAILURE;
582  }
583  if (aSubMenu == ADDTOPLAYLIST_MENU_ID) return this.m_listofplaylists;
584  return null;
585  },
586 
587  handleCommand: function(id) {
588  try {
589  var context = this.m_commands.m_Context;
591  var newMediaList = context.m_Window.makeNewPlaylist("simple");
592  this.addToPlaylist(newMediaList.library.guid, newMediaList.guid, context.m_Playlist);
593  return true;
594  }
595  var addtoplstr = ADDTOPLAYLIST_COMMAND_ID;
596  if ( id.slice(0, addtoplstr.length) == addtoplstr) {
597  var r = id.slice(addtoplstr.length);
598  var guids = r.split(';');
599  if (guids.length >= 2) {
600  var libraryguid = guids[0];
601  var playlistguid = guids[1];
602 
603  this.addToPlaylist(libraryguid, playlistguid, context.m_Playlist);
604 
605  return true;
606  }
607  }
608  } catch (e) {
609  Components.utils.reportError(e);
610  }
611  return false;
612  },
613 
614  addToPlaylist: function(libraryguid, playlistguid, sourceplaylist) {
615  var library = this.m_libraryManager.getLibrary(libraryguid);
616  var medialist;
617  if (libraryguid == playlistguid)
618  medialist = library;
619  else
620  medialist = library.getMediaItem(playlistguid);
621 
622  if (medialist) {
623 
624  var oldLength = medialist.length;
625  var selection = sourceplaylist.mediaListView.selection.selectedIndexedMediaItems;
626 
627  // Create an enumerator that wraps the enumerator we were handed since
628  // the enumerator we get hands back sbIIndexedMediaItem, not just plain
629  // 'ol sbIMediaItems
630  var unwrapper = createUnwrapper(selection);
631 
632  var asyncListener = {
633  onProgress: function(aItemsProcessed, aComplete) {
634  DNDUtils.reportAddedTracks(aItemsProcessed,
635  0, /* no duplicate reporting */
636  0, /* no unsupported reporting */
637  medialist.name);
638  },
639  onItemAdded: function(aMediaItem) {},
640  onComplete: function() {},
641  QueryInterface: XPCOMUtils.generateQI([Ci.sbIMediaListAsyncListener])
642  }
643 
644  medialist.addMediaItems(unwrapper, asyncListener, true);
645  }
646  },
647 
648  //-----------------------------------------------------------------------------
649  _inbatch : false,
650  _deferredevent : false,
651  _makingList : false,
652 
653  refreshCommands: function() {
654 
655  // Bug fixers beware! This code gets called very frequently and can have
656  // difficult-to-debug side effects. Proceed with caution.
657 
658  var self = this;
659  function ensureRefreshExists() {
660  // Explicitly return true or false. Otherwise, JS will complain to the
661  // error console if any of the checks finds an undefined property.
662  return (self.m_commands &&
663  self.m_commands.m_Context &&
664  self.m_commands.m_Context.m_Playlist &&
665  self.m_commands.m_Context.m_Playlist.refreshCommands) ?
666  true : false;
667  }
668 
669  /* We need to ensure that the context and playlist have been initialized
670  * and are ready to display commands before calling refreshCommands.
671  *
672  * It is possible for an event handler to fire before the binding
673  * is created or after it is destroyed, so it is possible for this to
674  * be triggered before m_Playlist is fully instantiated or after pieces
675  * of it have gone away. Thus, we need to also check that refreshCommands
676  * is present to ensure the playlist binding is in a good state. */
677 
678  if (ensureRefreshExists()) {
679  this.makeListOfPlaylists();
680  // Check again, as the playlist binding can be destroyed during the
681  // previous function call.
682  if (ensureRefreshExists()) {
683  this.m_commands.m_Context.m_Playlist.refreshCommands();
684  }
685  }
686  },
687 
688  onUpdateEvent: function(item) {
689  if (this._makingList) return;
690  if (item instanceof Components.interfaces.sbIMediaList) {
691  if (this._inbatch) {
692  // if we are in a batch, remember that we saw a playlist event in it
693  this._deferredevent = true;
694  } else {
695  // if we're not in a batch, proceed with refreshing the commands
696  this.refreshCommands();
697  }
698  }
699  },
700 
701  QueryInterface: function QueryInterface(iid) {
702  if (!iid.equals(Components.interfaces.sbIMediaListListener) &&
703  !iid.equals(Components.interfaces.sbILibraryManagerListener) &&
704  !iid.equals(Components.interfaces.nsISupports))
705  throw Components.results.NS_ERROR_NO_INTERFACE;
706  return this;
707  },
708 
709  onItemAdded: function onItemAdded(list, item, index) {
710  // If we are in a batch, ignore future notifications
711  if (!this._inbatch) {
712  this.onUpdateEvent(item);
713  }
714  return true;
715  },
716 
717  onBeforeItemRemoved: function onBeforeItemRemoved(list, item, index) {
718  return true;
719  },
720 
721  onAfterItemRemoved: function onAfterItemRemoved(list, item, index) {
722  // If we are in a batch, ignore future notifications
723  if (!this._inbatch) {
724  this.onUpdateEvent(item);
725  }
726  return true;
727  },
728 
729  onItemUpdated: function onItemUpdated(list, item, properties) {
730  // If we are in a batch, ignore future notifications
731  if (!this._inbatch) {
732  this.onUpdateEvent(item);
733  }
734  return true;
735  },
736 
737  onItemMoved: function onItemMoved(list, fromIndex, toIndex) {
738  // XXXsteve Do we need to do anything here?
739  return true;
740  },
741 
742  onBeforeListCleared: function onBeforeListCleared(list, excludeLists) {
743  return true;
744  },
745 
746  onListCleared: function onListCleared(list, excludeLists) {
747  // If we are in a batch, ignore future notifications
748  if (!this._inbatch) {
749  this.onUpdateEvent(list);
750  }
751  return true;
752  },
753 
754  onBatchBegin: function onBatchBegin(list) {
755  // start deferring the events
756  this._inbatch = true;
757  this._deferredevent = false;
758  },
759 
760  onBatchEnd: function onBatchEnd(list) {
761  // stop deferring the events
762  this._inbatch = false;
763  // if an event was deferred, handle it
764  if (this._deferredevent) {
765  // since we're no longer in a batch, this does call refreshCommands
766  this.onUpdateEvent(list);
767  }
768  this._deferredevent = false;
769  },
770 
771  onLibraryRegistered: function onLibraryRegistered(library) {
772  this.onUpdateEvent(library);
773  },
774 
775  onLibraryUnregistered: function onLibraryUnregistered(library) {
776  this.onUpdateEvent(library);
777  }
778 
779 };
const Cc
const Ci
var SBPlaylistCommand_DownloadToPlaylist
const ADDTOPLAYLIST_MENU_KEY
const DOWNLOADTOPLAYLIST_MENU_KEY
#define LOG(args)
menuItem id
Definition: FeedWriter.js:971
function onComplete(job)
Definition: test_bug7406.js:85
const ADDTOPLAYLIST_MENU_ID
_updateCookies aHost
var DNDUtils
Definition: DropHelper.jsm:80
function createUnwrapper(aSelection)
Creates a new unwrapper helper object which ensures downloadStatusTarget is always set when adding it...
var SBPlaylistCommand_AddToPlaylist
sbOSDControlService prototype QueryInterface
const ADDTOPLAYLIST_NEWPLAYLIST_COMMAND_ID
sbDownloadDeviceServicePaneModule prototype shutdown
let window
const ADDTOPLAYLIST_MENU_KEYCODE
_window init
Definition: FeedWriter.js:1144
function addToPlaylistHelper()
_collectFormDataForFrame aDocument
const DOWNLOADTOPLAYLIST_MENU_NAME
const DOWNLOADTOPLAYLIST_MENU_TOOLTIP
EXPORTED_SYMBOLS
const Cr
const ADDTOPLAYLIST_MENU_NAME
const DOWNLOADTOPLAYLIST_MENU_MODIFIERS
return null
Definition: FeedWriter.js:1143
const ADDTOPLAYLIST_MENU_TYPE
const DOWNLOADTOPLAYLIST_MENU_KEYCODE
let node
countRef value
Definition: FeedWriter.js:1423
SBPlaylistCommand_DownloadToPlaylist m_root_commands
const ADDTOPLAYLIST_MENU_MODIFIERS
var hidden
const ADDTOPLAYLIST_MENU_TOOLTIP
_getSelectedPageStyle s i
const ADDTOPLAYLIST_COMMAND_ID