Bug 477620: Enable addition of search plugins []
authorVivien Nicolas <21@vingtetun.org>
Wed, 26 Aug 2009 15:29:46 -0400
changeset 65481 02e02c000c0f8c0d38994ee43af516d98edc7246
parent 65480 57a27959268bc1673d2a02f482f6e4eb83b18f88
child 65482 c796c323de863ca43131d501f172821a60a5773f
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs477620
Bug 477620: Enable addition of search plugins []
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.js
mobile/chrome/content/browser.xul
mobile/locales/en-US/chrome/browser.dtd
mobile/themes/hildon/browser.css
mobile/themes/wince/browser-high.css
mobile/themes/wince/browser-low.css
mobile/themes/wince/browser.css
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -119,16 +119,25 @@ var BrowserUI = {
     if (/\bicon\b/i(link.rel)) {
       this._faviconLink = link.href;
 
       // If the link changes after pageloading, update it right away.
       // otherwise we wait until the pageload finishes
       if (this._favicon.src != "")
         this._setIcon(this._faviconLink);
     }
+    else if (/\bsearch\b/i(link.rel)) {
+      var type = link.type && link.type.toLowerCase();
+      type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
+      if (type == "application/opensearchdescription+xml" && link.title && /^(?:https?|ftp):/i.test(link.href)) {
+        var engine = { title: link.title, href: link.href };
+
+        BrowserSearch.addPageSearchEngine(engine, link.ownerDocument);
+      }
+    }
   },
 
   _updateButtons : function(aBrowser) {
     var back = document.getElementById("cmd_back");
     var forward = document.getElementById("cmd_forward");
 
     back.setAttribute("disabled", !aBrowser.canGoBack);
     forward.setAttribute("disabled", !aBrowser.canGoForward);
@@ -487,56 +496,32 @@ var BrowserUI = {
   },
 
   search : function() {
     var queryURI = "http://www.google.com/search?q=" + this._edit.value + "&hl=en&lr=&btnG=Search";
     getBrowser().loadURI(queryURI, null, null, false);
   },
 
   showAutoComplete : function(showDefault) {
-    this.updateSearchEngines();
+    BrowserSearch.updateSearchButtons();
     this._edit.showHistoryPopup();
   },
 
   doButtonSearch : function(button) {
     if (!("engine" in button) || !button.engine)
       return;
 
     var urlbar = this._edit;
     urlbar.open = false;
     var value = urlbar.value;
 
     var submission = button.engine.getSubmission(value, null);
     getBrowser().loadURI(submission.uri.spec, null, submission.postData, false);
   },
 
-  engines : null,
-  updateSearchEngines : function() {
-    if (this.engines)
-      return;
-
-    var searchService = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
-    var engines = searchService.getVisibleEngines({ });
-    this.engines = engines;
-
-    const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    var container = document.getElementById("search-buttons");
-    for (var e = 0; e < engines.length; e++) {
-      var button = document.createElementNS(kXULNS, "radio");
-      var engine = engines[e];
-      button.id = engine.name;
-      button.setAttribute("label", engine.name);
-      button.className = "searchengine";
-      if (engine.iconURI)
-        button.setAttribute("src", engine.iconURI.spec);
-      container.appendChild(button);
-      button.engine = engine;
-    }
-  },
-
   updateStar : function() {
     if (PlacesUtils.getMostRecentBookmarkForURI(Browser.selectedBrowser.currentURI) != -1)
       this.starButton.setAttribute("starred", "true");
     else
       this.starButton.removeAttribute("starred");
   },
 
   goToBookmark : function goToBookmark(aEvent) {
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1473,16 +1473,107 @@ nsBrowserAccess.prototype = {
     return newWindow;
   },
 
   isTabContentWindow: function(aWindow) {
     return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
   }
 };
 
+const BrowserSearch = {
+  engines: null,
+  _allEngines: [],
+
+  get _currentEngines() {
+    let doc = getBrowser().contentDocument;
+    return this._allEngines.filter(function(element) element.doc === doc, this);
+  },
+
+  addPageSearchEngine: function (aEngine, aDocument) {
+    // Clean the engine referenced for document that didn't exist anymore
+    let browsers = Browser.browsers;
+    this._allEngines = this._allEngines.filter(function(element) {
+       return browsers.some(function (browser) browser.contentDocument == element.doc);
+    }, this);
+
+    // Prevent duplicate
+    if (!this._allEngines.some(function (e) {
+        return (e.engine.title == aEngine.title) && (e.doc == aDocument);
+    })) this._allEngines.push( {engine:aEngine, doc:aDocument});
+  },
+
+  updatePageSearchEngines: function() {
+    // Check to see whether we've already added an engine with this title in
+    // the search list
+    let newEngines = this._currentEngines.filter(function(element) {
+      return !this.engines.some(function (e) e.name == element.engine.title);
+    }, this);
+
+    let container = document.getElementById('search-container');
+    let buttons = container.getElementsByAttribute("class", "search-engine-button button-dark");
+    for (let i=0; i<buttons.length; i++)
+      container.removeChild(buttons[i]);
+
+    if (newEngines.length == 0) {
+      container.hidden = true;
+      return;
+    }
+
+    // XXX limit to the first search engine for now
+    for (let i = 0; i<1; i++) {
+      let button = document.createElement("button");
+      button.className = "search-engine-button button-dark";
+      button.setAttribute("oncommand", "BrowserSearch.addPermanentSearchEngine(this.engine);this.parentNode.hidden=true;");
+      
+      let engine = newEngines[i];
+      button.engine = engine.engine;
+      button.setAttribute("label", engine.engine.title);
+      button.setAttribute("image", BrowserUI._favicon.src);
+
+      container.appendChild(button);
+    }
+
+    container.hidden = false;
+  },
+
+  addPermanentSearchEngine: function (aEngine) {
+    var searchService = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
+    let iconURL = BrowserUI._favicon.src;
+    searchService.addEngine(aEngine.href, Ci.nsISearchEngine.DATA_XML, iconURL, false);
+
+    this.engines = null;
+  },
+
+  updateSearchButtons: function() {
+    if (this.engines)
+      return;
+
+    var searchService = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
+    var engines = searchService.getVisibleEngines({ });
+    this.engines = engines;
+
+    // Clean the previous search engines button
+    var container = document.getElementById("search-buttons");
+    while (container.hasChildNodes())
+      container.removeChild(container.lastChild);
+
+    for (var e = 0; e < engines.length; e++) {
+      var button = document.createElement("radio");
+      var engine = engines[e];
+      button.id = engine.name;
+      button.setAttribute("label", engine.name);
+      button.className = "searchengine";
+      if (engine.iconURI)
+        button.setAttribute("src", engine.iconURI.spec);
+      container.appendChild(button);
+      button.engine = engine;
+    }
+  }
+}
+
 /**
  * Utility class to handle manipulations of the identity indicators in the UI
  */
 function IdentityHandler() {
   this._stringBundle = document.getElementById("bundle_browser");
   this._staticStrings = {};
   this._staticStrings[this.IDENTITY_MODE_DOMAIN_VERIFIED] = {
     encryption_label: this._stringBundle.getString("identity.encrypted2")
@@ -1706,16 +1797,19 @@ IdentityHandler.prototype = {
       owner = "";
     }
 
     // Push the appropriate strings out to the UI
     this._identityPopupContentHost.textContent = host;
     this._identityPopupContentOwner.textContent = owner;
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
+
+    // Update the search engines results
+    BrowserSearch.updatePageSearchEngines();
   },
 
   show: function ih_show() {
     this._identityPopup.hidden = false;
     this._identityPopup.top = BrowserUI.toolbarH;
     this._identityPopup.focus();
 
     this._identityBox.setAttribute("open", "true");
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -259,36 +259,42 @@
     </scrollbox>
 
     <!-- popup for find toolbar -->
     <hbox id="findbar-container" hidden="true" top="0" left="0">
       <findbar id="findbar" flex="1"/>
     </hbox>
 
     <!-- popup for site identity information -->
-    <hbox id="identity-container" hidden="true" class="panel-dark" top="0" left="0" align="top" mode="unknownIdentity">
-      <image id="identity-popup-icon"/>
-      <vbox id="identity-popup-content-box" flex="1">
-        <hbox flex="1">
-          <label id="identity-popup-connectedToLabel" value="&identity.connectedTo2;"/>
-          <label id="identity-popup-connectedToLabel2" flex="1">&identity.unverifiedsite2;</label>
-          <description id="identity-popup-content-host" flex="1"/>
-        </hbox>
-        <hbox flex="1">
-          <label id="identity-popup-runByLabel" value="&identity.runBy2;"/>
-          <description id="identity-popup-content-owner"/>
-          <description id="identity-popup-content-supplemental"/>
-        </hbox>
-        <description id="identity-popup-content-verifier"/>
-      </vbox>
-      <vbox align="center" pack="start">
-        <image id="identity-popup-encryption-icon"/>
-        <description id="identity-popup-encryption-label"/>
-      </vbox>
-    </hbox>
+    <vbox id="identity-container" hidden="true" class="panel-dark" top="0" left="0" mode="unknownIdentity">
+      <hbox id="identity-popup-container" flex="1" align="top">
+        <image id="identity-popup-icon"/>
+        <vbox id="identity-popup-content-box" flex="1">
+          <hbox flex="1">
+            <label id="identity-popup-connectedToLabel" value="&identity.connectedTo2;"/>
+            <label id="identity-popup-connectedToLabel2" flex="1">&identity.unverifiedsite2;</label>
+            <description id="identity-popup-content-host" flex="1"/>
+          </hbox>
+          <hbox flex="1">
+            <label id="identity-popup-runByLabel" value="&identity.runBy2;"/>
+            <description id="identity-popup-content-owner"/>
+            <description id="identity-popup-content-supplemental"/>
+          </hbox>
+          <description id="identity-popup-content-verifier"/>
+        </vbox>
+        <vbox align="center" pack="start">
+          <image id="identity-popup-encryption-icon"/>
+          <description id="identity-popup-encryption-label"/>
+        </vbox>
+      </hbox>
+
+      <hbox id="search-container" align="center" flex="1">
+        <label id="search-engine-label-add" value="&searchEngine.addSearch;"/>
+      </hbox>
+    </vbox>
 
     <vbox id="newtab-popup" hidden="true" class="panel-dark" onclick="NewTabPopup.selectTab()" align="center" left="21">
       <label/>
     </vbox>
 
     <vbox id="bookmark-popup" hidden="true" class="panel-dark" oncommand="BookmarkPopup.hide()" align="center">
       <label value="&bookmarkPopup.label;"/>
       <vbox>
--- a/mobile/locales/en-US/chrome/browser.dtd
+++ b/mobile/locales/en-US/chrome/browser.dtd
@@ -78,16 +78,18 @@
 <!ENTITY downloadCancel.label      "Cancel">
 <!ENTITY downloadPause.label       "Pause">
 <!ENTITY downloadResume.label      "Resume">
 <!ENTITY downloadRetry.label       "Retry">
 
 <!ENTITY noResults.label           "No results">
 <!ENTITY allBookmarks.label        "See all bookmarks">
 
+<!ENTITY searchEngine.addSearch     "Add Search:">
+
 <!ENTITY bookmarkPopup.label       "Page Bookmarked">
 <!ENTITY bookmarkRemove.label      "Remove">
 <!ENTITY bookmarkEdit.label        "Edit">
 
 <!ENTITY identity.unverifiedsite2 "This web site does not supply identity information.">
 <!ENTITY identity.connectedTo2 "Connected to">
 <!-- Localization note (identity.runBy2)
  The layout of the identity dialog prevents combining this into a single string with
--- a/mobile/themes/hildon/browser.css
+++ b/mobile/themes/hildon/browser.css
@@ -674,16 +674,20 @@ findbar .findbar-closebutton {
 
 #bookmark-form {
   background-color: #fff;
   padding: 2.2mm; /* core spacing */
 }
 
 /* Identity popup   -------------------------------------------------------- */
 #identity-container {
+  border-bottom: 0.2mm solid grey;
+}
+
+#identity-popup-container {
   padding: 2.2mm; /* core spacing */
 }
 
 /* Popup Icons */
 #identity-popup-icon {
   padding: 0;
   list-style-image: url("chrome://browser/skin/images/identity-64.png");
 }
@@ -730,16 +734,32 @@ findbar .findbar-closebutton {
   list-style-image: url("chrome://browser/skin/images/unlock-40.png");
 }
 
 #identity-container[mode="verifiedIdentity"] > vbox > #identity-popup-encryption-icon ,
 #identity-container[mode="verifiedDomain"] > vbox > #identity-popup-encryption-icon {
   list-style-image: url("chrome://browser/skin/images/lock-40.png");
 }
 
+/* search popup   ---------------------------------------------------------- */
+#search-container {
+  border-top: 0.1mm solid rgb(207,207,207);
+  padding: 2.2mm; /* core spacing */
+  margin-top: 2mm;
+}
+
+#search-engine-label-add {
+  font-size: 80%;
+}
+
+.search-engine-button .button-icon {
+  width: 16px;
+  height: 16px;
+}
+
 /* Preferences window   ---------------------------------------------------- */
 /* XXX should be a richlistitem */
 richpref {
   background-color: white;
   color: black;
 }
 
 /* XXX should be a richlistitem.section-header */
--- a/mobile/themes/wince/browser-high.css
+++ b/mobile/themes/wince/browser-high.css
@@ -100,16 +100,21 @@
   background: url("images/leftcapSSL-default-64.png");
 }
 
 #identity-box[mode="verifiedDomain"]:hover:active,
 #identity-box[mode="verifiedDomain"][open] {
   background: url("images/leftcapSSL-active-64.png");
 }
 
+.search-engine-button .button-icon {
+  width: 16px;
+  height: 16px;
+}
+
 #urlbar-throbber[loading] {
   list-style-image: url("chrome://browser/skin/images/throbber.png");
 }
 
 #urlbar-favicon {
   width: 30px;
   height: 30px;
 }
--- a/mobile/themes/wince/browser-low.css
+++ b/mobile/themes/wince/browser-low.css
@@ -99,16 +99,21 @@
   background: url("images/leftcapSSL-default-36.png");
 }
 
 #identity-box[mode="verifiedDomain"]:hover:active,
 #identity-box[mode="verifiedDomain"][open] {
   background: url("images/leftcapSSL-active-36.png");
 }
 
+.search-engine-button .button-icon {
+  width: 16px;
+  height: 16px;
+}
+
 #urlbar-throbber[loading] {
   list-style-image: url("chrome://browser/skin/images/throbber.png");
 }
 
 #urlbar-favicon {
   width: 24px;
   height: 24px;
 }
--- a/mobile/themes/wince/browser.css
+++ b/mobile/themes/wince/browser.css
@@ -450,16 +450,20 @@ findbar {
 
 #bookmark-form {
   background-color: #fff;
   padding: 1.1mm; /* core spacing */
 }
 
 /* Identity popup   -------------------------------------------------------- */
 #identity-container {
+  border-bottom: 0.1mm solid grey;
+}
+
+#identity-popup-container {
   padding: 1.1mm; /* core spacing */
 }
 
 /* Popup Body Text */
 #identity-popup-content-box {
   -moz-padding-start: 1.1mm; /* core spacing */
 }
 
@@ -483,16 +487,27 @@ findbar {
   font-size: 8pt !important;
 }
 
 #identity-popup-content-host,
 #identity-popup-content-owner {
   font-weight: bold;
 }
 
+/* search popup   ---------------------------------------------------------- */
+#search-container {
+  border-top: 0.1mm solid rgb(207,207,207);
+  padding: 1.1mm; /* core spacing */
+  margin-top: 1mm;
+}
+
+#search-engine-label-add {
+  font-size: 80%;
+}
+
 /* Preferences window   ---------------------------------------------------- */
 /* XXX should be a richlistitem */
 richpref {
   background-color: white;
   color: black;
 }
 
 /* XXX should be a richlistitem.section-header */