Bug 766654 - Allow pasting of a URL into the Downloads Panel to initiate a download. r=mak.
authorMike Conley <mconley@mozilla.com>
Tue, 13 Nov 2012 14:46:56 -0500
changeset 117764 242334181a9d57a50b11e412d0ec7a7aa6eeb964
parent 117763 d78db2d4195f2b41cc3d466e1aa445bae875e8e6
child 117765 e5324de8c39841c2775c3e950200d0ca0854bde9
push idunknown
push userunknown
push dateunknown
reviewersmak
bugs766654
milestone19.0a1
Bug 766654 - Allow pasting of a URL into the Downloads Panel to initiate a download. r=mak.
browser/components/downloads/content/downloads.js
browser/components/downloads/jar.mn
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -103,16 +103,17 @@ const DownloadsPanel = {
     Services.downloads;
 
     // Now that data loading has eventually started, load the required XUL
     // elements and initialize our views.
     DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
                                                function DP_I_callback() {
       DownloadsViewController.initialize();
       DownloadsCommon.data.addView(DownloadsView);
+      DownloadsPanel._attachEventListeners();
       aCallback();
     });
   },
 
   /**
    * Closes the downloads panel and frees the internal resources related to the
    * downloads.  The downloads panel can be reopened later, even after this
    * function has been called.
@@ -125,16 +126,17 @@ const DownloadsPanel = {
 
     window.removeEventListener("unload", this.onWindowUnload, false);
 
     // Ensure that the panel is closed before shutting down.
     this.hidePanel();
 
     DownloadsViewController.terminate();
     DownloadsCommon.data.removeView(DownloadsView);
+    this._unattachEventListeners();
 
     this._state = this.kStateUninitialized;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Panel interface
 
   /**
@@ -270,32 +272,97 @@ const DownloadsPanel = {
 
     BrowserDownloadsUI();
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// Internal functions
 
   /**
+   * Attach event listeners to a panel element. These listeners should be
+   * removed in _unattachEventListeners. This is called automatically after the
+   * panel has successfully loaded.
+   */
+  _attachEventListeners: function DP__attachEventListeners()
+  {
+    this.panel.addEventListener("keydown", this._onKeyDown.bind(this), false);
+  },
+
+  /**
+   * Unattach event listeners that were added in _attachEventListeners. This
+   * is called automatically on panel termination.
+   */
+  _unattachEventListeners: function DP__unattachEventListeners()
+  {
+    this.panel.removeEventListener("keydown", this._onKeyDown.bind(this),
+                                   false);
+  },
+
+  /**
+   * Keydown listener that listens for the accel-V "paste" event. Initiates a
+   * file download if the pasted item can be resolved to a URI.
+   */
+  _onKeyDown: function DP__onKeyDown(aEvent)
+  {
+    let pasting = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_V &&
+#ifdef XP_MACOSX
+                  aEvent.metaKey;
+#else
+                  aEvent.ctrlKey;
+#endif
+
+    if (!pasting) {
+      return;
+    }
+
+    let trans = Cc["@mozilla.org/widget/transferable;1"]
+                  .createInstance(Ci.nsITransferable);
+    trans.init(null);
+    let flavors = ["text/x-moz-url", "text/unicode"];
+    flavors.forEach(trans.addDataFlavor);
+    Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
+    // Getting the data or creating the nsIURI might fail
+    try {
+      let data = {};
+      trans.getAnyTransferData({}, data, {});
+      let [url, name] = data.value
+                            .QueryInterface(Ci.nsISupportsString)
+                            .data
+                            .split("\n");
+      if (!url) {
+        return;
+      }
+
+      let uri = Services.io.newURI(url, null, null);
+      saveURL(uri.spec, name || uri.spec, null, true, true,
+              undefined, document);
+    } catch (ex) {}
+  },
+
+  /**
    * Move focus to the main element in the downloads panel, unless another
    * element in the panel is already focused.
    */
   _focusPanel: function DP_focusPanel()
   {
     // We may be invoked while the panel is still waiting to be shown.
     if (this._state != this.kStateShown) {
       return;
     }
 
     let element = document.commandDispatcher.focusedElement;
     while (element && element != this.panel) {
       element = element.parentNode;
     }
     if (!element) {
-      DownloadsView.richListBox.focus();
+      if (DownloadsView.richListBox.itemCount > 0) {
+        DownloadsView.richListBox.focus();
+      } else {
+        DownloadsView.downloadsHistory.focus();
+      }
     }
   },
 
   /**
    * Opens the downloads panel when data is ready to be displayed.
    */
   _openPopupIfDataReady: function DP_openPopupIfDataReady()
   {
--- a/browser/components/downloads/jar.mn
+++ b/browser/components/downloads/jar.mn
@@ -1,11 +1,11 @@
 # 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/.
 
 browser.jar:
         content/browser/downloads/download.xml           (content/download.xml)
         content/browser/downloads/downloads.css          (content/downloads.css)
-        content/browser/downloads/downloads.js           (content/downloads.js)
+*       content/browser/downloads/downloads.js           (content/downloads.js)
 *       content/browser/downloads/downloadsOverlay.xul   (content/downloadsOverlay.xul)
         content/browser/downloads/indicator.js           (content/indicator.js)
         content/browser/downloads/indicatorOverlay.xul   (content/indicatorOverlay.xul)