Bug 1324410 - fix dnd to the location bar for plaintext, r=mak
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 19 Dec 2016 17:34:00 +0000
changeset 374913 7261ee7e3e67c62531ca1e745da5e7b714b5d4ab
parent 374912 276202b206839a1c2563408fbcfa969272d929d8
child 374914 99e0fc974ccedc2dc00152f73ecac120b074fcb3
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1324410
milestone53.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 1324410 - fix dnd to the location bar for plaintext, r=mak MozReview-Commit-ID: 7md3hnxisDP
browser/base/content/test/urlbar/browser_dragdropURL.js
browser/base/content/urlbarBindings.xml
--- a/browser/base/content/test/urlbar/browser_dragdropURL.js
+++ b/browser/base/content/test/urlbar/browser_dragdropURL.js
@@ -1,15 +1,38 @@
 "use strict";
 
 const TEST_URL = "data:text/html,a test page";
 const DRAG_URL = "http://www.example.com/";
+const DRAG_FORBIDDEN_URL = "chrome://browser/content/aboutDialog.xul";
+const DRAG_TEXT = "Firefox is awesome";
+const DRAG_WORD = "Firefox";
 
-add_task(function* checkURLBarUpdateForDrag() {
+add_task(function* checkDragURL() {
   yield BrowserTestUtils.withNewTab(TEST_URL, function* (browser) {
     // Have to use something other than the URL bar as a source, so picking the
     // downloads button somewhat arbitrarily:
     EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
                               [[{type: "text/plain", data: DRAG_URL}]], "copy", window);
     is(gURLBar.value, TEST_URL, "URL bar value should not have changed");
     is(gBrowser.selectedBrowser.userTypedValue, null, "Stored URL bar value should not have changed");
   });
 });
+
+add_task(function* checkDragForbiddenURL() {
+  yield BrowserTestUtils.withNewTab(TEST_URL, function* (browser) {
+    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+                              [[{type: "text/plain", data: DRAG_FORBIDDEN_URL}]], "copy", window);
+    isnot(gURLBar.value, DRAG_FORBIDDEN_URL, "Shouldn't be allowed to drop forbidden URL on URL bar");
+  });
+});
+
+add_task(function* checkDragText() {
+  yield BrowserTestUtils.withNewTab(TEST_URL, function* (browser) {
+    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+                              [[{type: "text/plain", data: DRAG_TEXT}]], "copy", window);
+    is(gURLBar.value, DRAG_TEXT, "Dragging normal text should replace the URL bar value");
+
+    EventUtils.synthesizeDrop(document.getElementById("downloads-button"), gURLBar,
+                              [[{type: "text/plain", data: DRAG_WORD}]], "copy", window);
+    is(gURLBar.value, DRAG_WORD, "Dragging a single word should replace the URL bar value");
+  });
+});
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -696,65 +696,91 @@ file, You can obtain one at http://mozil
       </method>
 
       <method name="_hideURLTooltip">
         <body><![CDATA[
           this.inputField.removeAttribute("tooltiptext");
         ]]></body>
       </method>
 
-      <method name="_getDroppableLink">
+      <!-- Returns:
+           null if there's a security issue and we should do nothing.
+           a URL object if there is one that we're OK with loading,
+           a text value otherwise.
+           -->
+      <method name="_getDroppableItem">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          let links = browserDragAndDrop.dropLinks(aEvent);
+          let links;
+          try {
+            links = browserDragAndDrop.dropLinks(aEvent);
+          } catch (ex) {
+            // this is possibly a security exception, in which case we should return
+            // null. Always return null because we can't *know* what exception is
+            // being returned.
+            return null;
+          }
           // The URL bar automatically handles inputs with newline characters,
           // so we can get away with treating text/x-moz-url flavours as text/plain.
           if (links.length > 0 && links[0].url) {
             aEvent.preventDefault();
             let url = links[0].url;
             let strippedURL = stripUnsafeProtocolOnPaste(url);
             if (strippedURL != url) {
               aEvent.stopImmediatePropagation();
               return null;
             }
+            let urlObj;
             try {
-              urlSecurityCheck(url,
-                               gBrowser.contentPrincipal,
-                               Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+              // If this throws, urlSecurityCheck would also throw, as that's what it
+              // does with things that don't pass the IO service's newURI constructor
+              // without fixup. It's conceivable we may want to relax this check in
+              // the future (so e.g. www.foo.com gets fixed up), but not right now.
+              urlObj = new URL(url);
+              // If we succeed, try to pass security checks. If this works, return the
+              // URL object. If the *security checks* fail, return null.
+              try {
+                urlSecurityCheck(url,
+                                 gBrowser.contentPrincipal,
+                                 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+                return urlObj;
+              } catch (ex) {
+                return null;
+              }
             } catch (ex) {
-              return null;
+              // We couldn't make a URL out of this. Continue on, and return text below.
             }
-            return url;
           }
-          return null;
+          return aEvent.dataTransfer.getData("text/unicode");
         ]]></body>
       </method>
 
       <method name="onDragOver">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          // We don't need the link here, so we ignore the return value.
-          if (!this._getDroppableLink(aEvent)) {
+          if (!this._getDroppableItem(aEvent)) {
             aEvent.dataTransfer.dropEffect = "none";
           }
         ]]></body>
       </method>
 
       <method name="onDrop">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          let url = this._getDroppableLink(aEvent);
-          if (url) {
-            this.value = url;
+          let droppedItem = this._getDroppableItem(aEvent);
+          if (droppedItem) {
+            this.value = droppedItem instanceof URL ? droppedItem.href : droppedItem;
             SetPageProxyState("invalid");
             this.focus();
-            this.handleCommand();
-            // Force not showing the dropped URI immediately.
-            gBrowser.userTypedValue = null;
-            URLBarSetURI();
+            if (droppedItem instanceof URL) {
+              this.handleCommand();
+              // Force not showing the dropped URI immediately.
+              gBrowser.userTypedValue = null;
+              URLBarSetURI();
+            }
           }
         ]]></body>
       </method>
 
       <method name="_getSelectedValueForClipboard">
         <body><![CDATA[
           // Grab the actual input field's value, not our value, which could include moz-action:
           var inputVal = this.inputField.value;