Bug 332538 - add "Add a Keyword for this Search" to websites' search fields. r=Neil
authorJens Hatlak <jh@junetz.de>
Sat, 08 Jan 2011 15:02:23 +0100
changeset 6920 d31ec9269f65
parent 6919 6f67325cc686
child 6921 d93907368195
push id5301
push userjh@junetz.de
push dateSat, 08 Jan 2011 14:04:08 +0000
treeherdercomm-central@d31ec9269f65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil
bugs332538
Bug 332538 - add "Add a Keyword for this Search" to websites' search fields. r=Neil
suite/browser/navigator.js
suite/common/contentAreaContextOverlay.xul
suite/common/nsContextMenu.js
suite/locales/en-US/chrome/browser/navigator.properties
suite/locales/en-US/chrome/common/contentAreaCommands.dtd
--- a/suite/browser/navigator.js
+++ b/suite/browser/navigator.js
@@ -2857,8 +2857,63 @@ var LightWeightThemeWebInstaller = {
     return Services.perms.testPermission(uri, "install") == Services.perms.ALLOW_ACTION;
   },
 
   _getThemeFromNode: function (node) {
     return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
                                     node.baseURI);
   }
 }
+
+function AddKeywordForSearchField() {
+  var node = document.popupNode;
+  var doc = node.ownerDocument;
+  var charset = doc.characterSet;
+  var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
+                                                  [doc.title]);
+  var description = PlacesUIUtils.getDescriptionFromDocument(doc);
+  var postData = null;
+  var form = node.form;
+  var spec = form.action || doc.documentURI;
+
+  function encodeNameValuePair(aName, aValue) {
+    return encodeURIComponent(aName) + "=" + encodeURIComponent(aValue);
+  }
+
+  let el = null;
+  let type = null;
+  let formData = [];
+  for (var i = 0; i < form.elements.length; i++) {
+    el = form.elements[i];
+
+    if (!el.type) // happens with fieldsets
+      continue;
+
+    if (el == node) {
+      formData.push(encodeNameValuePair(el.name, "") + "%s");
+      continue;
+    }
+    
+    type = el.type;
+
+    if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
+        type == "hidden" || type == "textarea") ||
+        ((type == "checkbox" || type == "radio") && el.checked)) {
+      formData.push(encodeNameValuePair(el.name, el.value));
+    } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
+      for (var j = 0; j < el.options.length; j++) {
+        if (el.options[j].selected)
+          formData.push(encodeNameValuePair(el.name, el.options[j].value));
+      }
+    }
+  }
+
+  if (form.method == "post" &&
+      form.enctype == "application/x-www-form-urlencoded") {
+    postData = formData.join("&");
+  } else { // get
+    spec += spec.indexOf("?") != -1 ? "&" : "?";
+    spec += formData.join("&");
+  }
+
+  PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(spec), title, description, null,
+                                         null, null, "", postData, charset);
+}
--- a/suite/common/contentAreaContextOverlay.xul
+++ b/suite/common/contentAreaContextOverlay.xul
@@ -253,16 +253,20 @@
                 accesskey="&deleteCmd.accesskey;"
                 command="cmd_delete"/>
       <menuseparator id="context-sep-paste"/>
       <menuitem id="context-selectall"
                 label="&selectAllCmd.label;"
                 accesskey="&selectAllCmd.accesskey;"
                 command="cmd_selectAll"/>
       <menuseparator id="context-sep-selectall"/>
+      <menuitem id="context-keywordfield"
+                label="&keywordfield.label;"
+                accesskey="&keywordfield.accesskey;"
+                oncommand="AddKeywordForSearchField();"/>
       <menuitem id="context-searchselect"
                 oncommand="BrowserSearch.loadSearch(gContextMenu.searchSelected(), true);"/>
       <menuseparator id="frame-sep"/>
       <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
         <menupopup id="frame_popup">
           <menuitem id="context-showonlythisframe"
                     label="&showOnlyThisFrameCmd.label;"
                     accesskey="&showOnlyThisFrameCmd.accesskey;"
--- a/suite/common/nsContextMenu.js
+++ b/suite/common/nsContextMenu.js
@@ -52,16 +52,17 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function nsContextMenu(aXulMenu, aBrowser) {
   this.target            = null;
   this.browser           = null;
   this.menu              = null;
   this.popupURL          = null;
   this.onTextInput       = false;
+  this.onKeywordField    = false;
   this.onImage           = false;
   this.onLoadedImage     = false;
   this.onCanvas          = false;
   this.onVideo           = false;
   this.onAudio           = false;
   this.onLink            = false;
   this.onMailtoLink      = false;
   this.onSaveableLink    = false;
@@ -243,16 +244,17 @@ nsContextMenu.prototype = {
 
   initMiscItems: function() {
     // Use "Bookmark This Link" if on a link.
     this.showItem("context-bookmarkpage",
                   !(this.isContentSelected || this.onTextInput ||
                     this.onStandaloneImage || this.onVideo || this.onAudio));
     this.showItem("context-bookmarklink", this.onLink && !this.onMailtoLink);
     this.showItem("context-searchselect", this.isTextSelected && !this.onTextInput);
+    this.showItem("context-keywordfield", this.onTextInput && this.onKeywordField);
     this.showItem("frame", this.inFrame);
     this.showItem("frame-sep", this.inFrame);
     if (this.inFrame)
       goSetMenuValue("context-saveframe",
                      this.autoDownload ? "valueSave" : "valueSaveAs");
 
     var blocking = true;
       if (this.popupURL)
@@ -407,16 +409,17 @@ nsContextMenu.prototype = {
     this.onImage               = false;
     this.onLoadedImage         = false;
     this.onStandaloneImage     = false;
     this.onCanvas              = false;
     this.onVideo               = false;
     this.onAudio               = false;
     this.onMetaDataItem        = false;
     this.onTextInput           = false;
+    this.onKeywordField        = false;
     this.mediaURL              = "";
     this.onLink                = false;
     this.linkURL               = "";
     this.linkURI               = null;
     this.linkProtocol          = "";
     this.onMathML              = false;
     this.inFrame               = false;
     this.hasBGImage            = false;
@@ -488,16 +491,17 @@ nsContextMenu.prototype = {
         this.onTextInput = this.isTargetATextBox(this.target);
         // allow spellchecking UI on all writable text boxes except passwords
         if (!this.target.readOnly && !this.target.disabled &&
             this.target.mozIsTextField(true)) {
           this.possibleSpellChecking = true;
           InlineSpellCheckerUI.init(this.target.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor);
           InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
         }
+        this.onKeywordField = this.isTargetAKeywordField(this.target);
       }
       else if (this.target instanceof HTMLTextAreaElement) {
         this.onTextInput = true;
         if (!this.target.readOnly && !this.target.disabled) {
           this.possibleSpellChecking = true;
           InlineSpellCheckerUI.init(this.target.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor);
           InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
         }
@@ -1292,16 +1296,28 @@ nsContextMenu.prototype = {
 
   isTargetATextBox: function(aNode) {
     if (aNode instanceof HTMLInputElement)
       return aNode.mozIsTextField(false);
 
     return (aNode instanceof HTMLTextAreaElement);
   },
 
+  isTargetAKeywordField: function(aNode) {
+    if (!(aNode instanceof HTMLInputElement))
+      return false;
+
+    var form = aNode.form;
+    if (!form || !aNode.mozIsTextField(true))
+      return false;
+
+    return form.method == "get" || (form.method == "post" &&
+           form.enctype == "application/x-www-form-urlencoded");
+  },
+  
   // Determines whether or not the separator with the specified ID should be
   // shown or not by determining if there are any non-hidden items between it
   // and the previous separator.
   shouldShowSeparator: function(aSeparatorID) {
     var separator = document.getElementById(aSeparatorID);
     if (separator) {
       var sibling = separator.previousSibling;
       while (sibling && sibling.localName != "menuseparator") {
--- a/suite/locales/en-US/chrome/browser/navigator.properties
+++ b/suite/locales/en-US/chrome/browser/navigator.properties
@@ -79,8 +79,13 @@ editBookmarkPanel.bookmarkedRemovedTitle
 editBookmarkPanel.editBookmarkTitle=Edit This Bookmark
 
 # LOCALIZATION NOTE (editBookmark.removeBookmarks.label)
 # Semi-colon list of plural forms. Replacement for #1 is
 # the number of bookmarks to be removed.
 # If this causes problems with localization you can also do "Remove Bookmarks (#1)"
 # instead of "Remove #1 Bookmarks".
 editBookmark.removeBookmarks.label=Remove Bookmark;Remove #1 Bookmarks
+
+# LOCALIZATION NOTE (addKeywordTitleAutoFill): %S will be replaced by the page's title
+# Used as the bookmark name when saving a keyword for a search field.
+addKeywordTitleAutoFill=Search %S
+
--- a/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
+++ b/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
@@ -8,16 +8,18 @@
 <!ENTITY openLinkCmdInTab.label       "Open Link in New Tab">
 <!ENTITY openLinkCmdInTab.accesskey   "T">
 <!ENTITY openLinkInWindowCmd.label    "Open">
 <!ENTITY openLinkInWindowCmd.accesskey "p">
 <!ENTITY openFrameCmd.label           "Open Frame in New Window">
 <!ENTITY openFrameCmd.accesskey       "W">
 <!ENTITY openFrameCmdInTab.label      "Open Frame in New Tab">
 <!ENTITY openFrameCmdInTab.accesskey  "T">
+<!ENTITY keywordfield.label           "Add a Keyword for this Search…">
+<!ENTITY keywordfield.accesskey       "K">
 <!ENTITY showOnlyThisFrameCmd.label     "Show Only This Frame">
 <!ENTITY showOnlyThisFrameCmd.accesskey "S">
 <!ENTITY goBackCmd.label              "Back">
 <!ENTITY goBackCmd.accesskey          "B">
 <!ENTITY goForwardCmd.label           "Forward">
 <!ENTITY goForwardCmd.accesskey       "F">
 <!ENTITY goUpCmd.label                "Up">
 <!ENTITY goUpCmd.accesskey            "U">