Bug 1100294 - Deprecate PlacesUtils.getURLAndPostDataForKeyword() and turn getShortcutOrURIAndPostData() into a task that uses the new keywords API r=mak
authorTim Taubert <ttaubert@mozilla.com>
Fri, 27 Mar 2015 09:16:04 +0100
changeset 235939 5541383f6c41ad872705f52aa7d30f7765bdfcc6
parent 235938 eda1b8828dfbd8caeafb370bb650d691135524c5
child 235940 5dce13dd431c9afbed98b5c7928b2481df46ead0
push id12029
push userttaubert@mozilla.com
push dateFri, 27 Mar 2015 11:44:49 +0000
treeherderfx-team@1ba56f4da590 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1100294
milestone39.0a1
Bug 1100294 - Deprecate PlacesUtils.getURLAndPostDataForKeyword() and turn getShortcutOrURIAndPostData() into a task that uses the new keywords API r=mak
browser/base/content/browser.js
browser/base/content/test/general/browser_getshortcutoruri.js
browser/base/content/urlbarBindings.xml
toolkit/components/places/PlacesUtils.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2095,63 +2095,91 @@ function loadURI(uri, referrer, postData
     openLinkIn(uri, "current",
                { referrerURI: referrer,
                  referrerPolicy: referrerPolicy,
                  postData: postData,
                  allowThirdPartyFixup: allowThirdPartyFixup });
   } catch (e) {}
 }
 
-function getShortcutOrURIAndPostData(aURL, aCallback) {
-  let mayInheritPrincipal = false;
-  let postData = null;
-  let shortcutURL = null;
-  let keyword = aURL;
-  let param = "";
-
-  // XXX Bug 1100294 will remove this little hack by using an async version of
-  // PlacesUtils.getURLAndPostDataForKeyword(). For now we simulate an async
-  // execution with at least a setTimeout(fn, 0).
-  let originalCallback = aCallback;
-  aCallback = data => setTimeout(() => originalCallback(data));
-
-  let offset = aURL.indexOf(" ");
-  if (offset > 0) {
-    keyword = aURL.substr(0, offset);
-    param = aURL.substr(offset + 1);
-  }
-
-  let engine = Services.search.getEngineByAlias(keyword);
-  if (engine) {
-    let submission = engine.getSubmission(param, null, "keyword");
-    postData = submission.postData;
-    aCallback({ postData: submission.postData, url: submission.uri.spec,
-                mayInheritPrincipal: mayInheritPrincipal });
-    return;
-  }
-
-  [shortcutURL, postData] =
-    PlacesUtils.getURLAndPostDataForKeyword(keyword);
-
-  if (!shortcutURL) {
-    aCallback({ postData: postData, url: aURL,
-                mayInheritPrincipal: mayInheritPrincipal });
-    return;
-  }
-
-  let escapedPostData = "";
-  if (postData)
-    escapedPostData = unescape(postData);
-
-  if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
-    let charset = "";
-    const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
-    let matches = shortcutURL.match(re);
-
-    let continueOperation = function () {
+/**
+ * Given a urlbar value, discerns between URIs, keywords and aliases.
+ *
+ * @param url
+ *        The urlbar value.
+ * @param callback (optional, deprecated)
+ *        The callback function invoked when done. This parameter is
+ *        deprecated, please use the Promise that is returned.
+ *
+ * @return Promise<{ postData, url, mayInheritPrincipal }>
+ */
+function getShortcutOrURIAndPostData(url, callback = null) {
+  if (callback) {
+    Deprecated.warning("Please use the Promise returned by " +
+                       "getShortcutOrURIAndPostData() instead of passing a " +
+                       "callback",
+                       "https://bugzilla.mozilla.org/show_bug.cgi?id=1100294");
+  }
+
+  return Task.spawn(function* () {
+    let mayInheritPrincipal = false;
+    let postData = null;
+    let shortcutURL = null;
+    let keyword = url;
+    let param = "";
+
+    let offset = url.indexOf(" ");
+    if (offset > 0) {
+      keyword = url.substr(0, offset);
+      param = url.substr(offset + 1);
+    }
+
+    let engine = Services.search.getEngineByAlias(keyword);
+    if (engine) {
+      let submission = engine.getSubmission(param, null, "keyword");
+      postData = submission.postData;
+      return { postData: submission.postData, url: submission.uri.spec,
+               mayInheritPrincipal };
+    }
+
+    let entry = yield PlacesUtils.keywords.fetch(keyword);
+    if (entry) {
+      shortcutURL = entry.url.href;
+      postData = entry.postData;
+    }
+
+    if (!shortcutURL) {
+      return { postData, url, mayInheritPrincipal };
+    }
+
+    let escapedPostData = "";
+    if (postData)
+      escapedPostData = unescape(postData);
+
+    if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
+      let charset = "";
+      const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
+      let matches = shortcutURL.match(re);
+
+      if (matches) {
+        [, shortcutURL, charset] = matches;
+      } else {
+        let uri;
+        try {
+          // makeURI() throws if URI is invalid.
+          uri = makeURI(shortcutURL);
+        } catch (ex) {}
+
+        if (uri) {
+          // Try to get the saved character-set.
+          // Will return an empty string if character-set is not found.
+          charset = yield PlacesUtils.getCharsetForURI(uri);
+        }
+      }
+
       // encodeURIComponent produces UTF-8, and cannot be used for other charsets.
       // escape() works in those cases, but it doesn't uri-encode +, @, and /.
       // Therefore we need to manually replace these ASCII characters by their
       // encodeURIComponent result, to match the behavior of nsEscape() with
       // url_XPAlphas
       let encodedParam = "";
       if (charset && charset != "UTF-8")
         encodedParam = escape(convertFromUnicode(charset, param)).
@@ -2164,50 +2192,39 @@ function getShortcutOrURIAndPostData(aUR
       if (/%s/i.test(escapedPostData)) // POST keyword
         postData = getPostDataStream(escapedPostData, param, encodedParam,
                                                "application/x-www-form-urlencoded");
 
       // This URL came from a bookmark, so it's safe to let it inherit the current
       // document's principal.
       mayInheritPrincipal = true;
 
-      aCallback({ postData: postData, url: shortcutURL,
-                  mayInheritPrincipal: mayInheritPrincipal });
-    }
-
-    if (matches) {
-      [, shortcutURL, charset] = matches;
-      continueOperation();
-    } else {
-      // Try to get the saved character-set.
-      // makeURI throws if URI is invalid.
-      // Will return an empty string if character-set is not found.
-      try {
-        PlacesUtils.getCharsetForURI(makeURI(shortcutURL))
-                   .then(c => { charset = c; continueOperation(); });
-      } catch (ex) {
-        continueOperation();
-      }
-    }
-  }
-  else if (param) {
-    // This keyword doesn't take a parameter, but one was provided. Just return
-    // the original URL.
-    postData = null;
-
-    aCallback({ postData: postData, url: aURL,
-                mayInheritPrincipal: mayInheritPrincipal });
-  } else {
+      return { postData, url: shortcutURL, mayInheritPrincipal };
+    }
+
+    if (param) {
+      // This keyword doesn't take a parameter, but one was provided. Just return
+      // the original URL.
+      postData = null;
+
+      return { postData, url, mayInheritPrincipal };
+    }
+
     // This URL came from a bookmark, so it's safe to let it inherit the current
     // document's principal.
     mayInheritPrincipal = true;
 
-    aCallback({ postData: postData, url: shortcutURL,
-                mayInheritPrincipal: mayInheritPrincipal });
-  }
+    return { postData, url: shortcutURL, mayInheritPrincipal };
+  }).then(data => {
+    if (callback) {
+      callback(data);
+    }
+
+    return data;
+  });
 }
 
 function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
   var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
                    createInstance(Ci.nsIStringInputStream);
   aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
   dataStream.data = aStringData;
 
@@ -3247,17 +3264,17 @@ var newTabButtonObserver = {
 
   onDragExit: function (aEvent)
   {
   },
 
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
-    getShortcutOrURIAndPostData(url, data => {
+    getShortcutOrURIAndPostData(url).then(data => {
       if (data.url) {
         // allow third-party services to fixup this URL
         openNewTabWith(data.url, null, data.postData, aEvent, true);
       }
     });
   }
 }
 
@@ -3267,17 +3284,17 @@ var newWindowButtonObserver = {
     browserDragAndDrop.dragOver(aEvent);
   },
   onDragExit: function (aEvent)
   {
   },
   onDrop: function (aEvent)
   {
     let url = browserDragAndDrop.drop(aEvent, { });
-    getShortcutOrURIAndPostData(url, data => {
+    getShortcutOrURIAndPostData(url).then(data => {
       if (data.url) {
         // allow third-party services to fixup this URL
         openNewWindowWith(data.url, null, data.postData, true);
       }
     });
   }
 }
 
@@ -5603,17 +5620,17 @@ function middleMousePaste(event) {
   // if it's not the current tab, we don't need to do anything because the 
   // browser doesn't exist.
   let where = whereToOpenLink(event, true, false);
   let lastLocationChange;
   if (where == "current") {
     lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
   }
 
-  getShortcutOrURIAndPostData(clipboard, data => {
+  getShortcutOrURIAndPostData(clipboard).then(data => {
     try {
       makeURI(data.url);
     } catch (ex) {
       // Not a valid URI.
       return;
     }
 
     try {
@@ -5640,17 +5657,17 @@ function stripUnsafeProtocolOnPaste(past
   // LOAD_FLAGS_DISALLOW_INHERIT_OWNER for those.
   return pasteData.replace(/^(?:\s*javascript:)+/i, "");
 }
 
 function handleDroppedLink(event, url, name)
 {
   let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
 
-  getShortcutOrURIAndPostData(url, data => {
+  getShortcutOrURIAndPostData(url).then(data => {
     if (data.url &&
         lastLocationChange == gBrowser.selectedBrowser.lastLocationChange)
       loadURI(data.url, null, data.postData, false);
   });
 
   // Keep the event from being handled by the dragDrop listeners
   // built-in to gecko if they happen to be above us.
   event.preventDefault();
--- a/browser/base/content/test/general/browser_getshortcutoruri.js
+++ b/browser/base/content/test/general/browser_getshortcutoruri.js
@@ -95,18 +95,17 @@ add_task(function* test_getshortcutoruri
   yield setupKeywords();
 
   for (let item of testData) {
     let [data, result] = item;
 
     let query = data.keyword;
     if (data.searchWord)
       query += " " + data.searchWord;
-    let returnedData = yield new Promise(
-      resolve => getShortcutOrURIAndPostData(query, resolve));
+    let returnedData = yield getShortcutOrURIAndPostData(query);
     // null result.url means we should expect the same query we sent in
     let expected = result.url || query;
     is(returnedData.url, expected, "got correct URL for " + data.keyword);
     is(getPostDataString(returnedData.postData), result.postData, "got correct postData for " + data.keyword);
     is(returnedData.mayInheritPrincipal, !result.isUnsafe, "got correct mayInheritPrincipal for " + data.keyword);
   }
 
   yield cleanupKeywords();
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -445,17 +445,17 @@
               } else {
                 url = url + suffix;
               }
 
               url = "http://www." + url;
             }
           }
 
-          getShortcutOrURIAndPostData(url, data => {
+          getShortcutOrURIAndPostData(url).then(data => {
             aCallback([data.url, data.postData, data.mayInheritPrincipal]);
           });
         ]]></body>
       </method>
 
       <field name="_contentIsCropped">false</field>
 
       <method name="_initURLTooltip">
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -927,18 +927,24 @@ this.PlacesUtils = {
       stmt.finalize();
     }
   },
 
   /**
    * Get the URI (and any associated POST data) for a given keyword.
    * @param aKeyword string keyword
    * @returns an array containing a string URL and a string of POST data
+   *
+   * @deprecated
    */
   getURLAndPostDataForKeyword(aKeyword) {
+    Deprecated.warning("getURLAndPostDataForKeyword() is deprecated, please " +
+                       "use PlacesUtils.keywords.fetch() instead",
+                       "https://bugzilla.mozilla.org/show_bug.cgi?id=1100294");
+
     let stmt = PlacesUtils.history.DBConnection.createStatement(
       `SELECT h.url, k.post_data
        FROM moz_keywords k
        JOIN moz_places h ON h.id = k.place_id
        WHERE k.keyword = :keyword`);
     stmt.params.keyword = aKeyword;
     try {
       if (!stmt.executeStep())