Bug 1324410 - fix dnd to the location bar for plaintext, r=mak draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 19 Dec 2016 17:34:00 +0000
changeset 463192 b37fc117474315a50963f682636f9ea13f53e8e2
parent 462769 80eac484366ad881c6a10bf81e8d9b8f7a676c75
child 542592 c9fc95650ec6aee2633ec3916e87f17fc2e99194
push id41973
push usergijskruitbosch@gmail.com
push dateWed, 18 Jan 2017 15:59:59 +0000
reviewersmak
bugs1324410
milestone53.0a1
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;