Bug 477620: Enable addition of search plugins []
--- 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 */