Adds support for the OpenSearch suggestions extension, and updates the

Google and Yahoo plugins to use it.

bug=340857
r=gavin
sr/branch=mconnor
This commit is contained in:
joe%retrovirus.com 2006-06-16 19:31:40 +00:00
Родитель bc1d4eb4d1
Коммит fc207b6130
7 изменённых файлов: 128 добавлений и 59 удалений

Просмотреть файл

@ -3057,7 +3057,14 @@ const BrowserSearch = {
else
engine = ss.defaultEngine;
var submission = engine.getSubmission(searchText);
var submission = engine.getSubmission(searchText, null); // HTML response
// getSubmission can return null if the engine doesn't have a URL
// with a text/html response type. This is unlikely (since
// SearchService._addEngineToStore() should fail for such an engine),
// but let's be on the safe side.
if (!submission)
return;
if (useNewTab) {
getBrowser().loadOneTab(submission.uri.spec, null, null,

Просмотреть файл

@ -430,20 +430,22 @@
<parameter name="aData"/>
<parameter name="aInNewTab"/>
<body><![CDATA[
var postData = { value: null };
var submission = this.currentEngine.getSubmission(aData);
var postData = null;
// null parameter below specifies HTML response for search
var submission = this.currentEngine.getSubmission(aData, null);
if (submission) {
var url = submission.uri.spec;
postData.value = submission.postData;
postData = submission.postData;
}
if (aInNewTab) {
content.focus();
getBrowser().loadOneTab(url, null, null, postData.value, false, false);
getBrowser().loadOneTab(url, null, null, postData, false, false);
if (gURLBar)
gURLBar.value = url;
} else
loadURI(url, null, postData.value, false);
loadURI(url, null, postData, false);
content.focus();
]]></body>
</method>

Просмотреть файл

@ -21,6 +21,7 @@
* Contributor(s):
* Ben Goodger <beng@google.com> (Original author)
* Gavin Sharp <gavin@gavinsharp.com>
* Joe Hughes <joe@retrovirus.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,7 +57,7 @@ interface nsISearchSubmission : nsISupports
readonly attribute nsIURI uri;
};
[scriptable, uuid(d8ceea19-d004-4206-a614-bef9849a2cc9)]
[scriptable, uuid(803e510b-4b27-4ba2-a16d-bb5fa953c166)]
interface nsISearchEngine : nsISupports
{
/**
@ -67,10 +68,15 @@ interface nsISearchEngine : nsISupports
* Data to add to the submission object.
* i.e. the search terms.
*
* @param responseType
* The MIME type that we'd like to receive in response
* to this submission. If null, will default to "text/html".
*
* @returns A nsISearchSubmission object that contains information about what
* to send to the search engine.
* to send to the search engine. If no submission can be
* obtained for the given responseType, returns null.
*/
nsISearchSubmission getSubmission(in AString data);
nsISearchSubmission getSubmission(in AString data, in AString responseType);
/**
* Adds a parameter to the search engine's submission data. This should only
@ -84,10 +90,26 @@ interface nsISearchEngine : nsISupports
* substituted with the user-entered data when retrieving the
* submission. Must not be null.
*
* @param responseType
* Since an engine can have several different request URLs,
* differentiated by response types, this parameter selects
* a request to add parameters to. If null, will default
* to "text/html"
*
* @throws NS_ERROR_FAILURE if the search engine is read-only.
* @throws NS_ERROR_INVALID_ARG if name or value are null.
*/
void addParam(in AString name, in AString value);
void addParam(in AString name, in AString value, in AString responseType);
/**
* Determines whether the engine can return responses in the given
* MIME type. Returns true if the engine spec has a URL with the
* given responseType, false otherwise.
*
* @param responseType
* The MIME type to check for
*/
boolean supportsResponseType(in AString responseType);
/**
* Supported search engine types.
@ -132,11 +154,6 @@ interface nsISearchEngine : nsISupports
*/
readonly attribute AString searchForm;
/**
* A URI object for retrieving suggested searches.
*/
readonly attribute nsIURI suggestionURI;
/**
* The search engine type.
*/

Просмотреть файл

@ -21,6 +21,7 @@
# Contributor(s):
# Ben Goodger <beng@google.com> (Original author)
# Gavin Sharp <gavin@gavinsharp.com>
# Joe Hughes <joe@retrovirus.com
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -106,6 +107,9 @@ const OPENSEARCH_LOCALNAME = "OpenSearchDescription";
const MOZSEARCH_NS_10 = "http://www.mozilla.org/2006/browser/search/";
const MOZSEARCH_LOCALNAME = "SearchPlugin";
const URLTYPE_SUGGEST_JSON = "application/x-suggestions+json";
const URLTYPE_SEARCH_HTML = "text/html";
// Empty base document used to serialize engines to file.
const EMPTY_DOC = "<?xml version=\"1.0\"?>\n" +
"<" + MOZSEARCH_LOCALNAME +
@ -697,9 +701,6 @@ function EngineURL(aType, aMethod, aTemplate) {
ENSURE_ARG(method == "GET" || method == "POST",
"method passed to EngineURL must be \"GET\" or \"POST\"");
ENSURE(type == "text/html", "EngineURLs must be of type text/html!",
Cr.NS_ERROR_NOT_IMPLEMENTED);
this.type = type;
this.method = method;
this.template = aTemplate;
@ -936,6 +937,24 @@ Engine.prototype = {
chan.asyncOpen(listener, null);
},
/**
* Attempts to find an EngineURL object in the set of EngineURLs for
* this Engine that has the given type string. (This corresponds to the
* "type" attribute in the "Url" node in the OpenSearch spec.)
* This method will return the first matching URL object found, or null
* if no matching URL is found.
*
* @param aType string to match the EngineURL's type attribute
*/
_getURLOfType: function SRCH_ENG__getURLOfType(aType) {
for (var i = 0; i < this._urls.length; ++i) {
if (this._urls[i].type == aType)
return this._urls[i];
}
return null;
},
/**
* Handle the successful download of an engine. Initializes the engine and
* triggers parsing of the data. The engine is then flushed to disk. Notifies
@ -1238,9 +1257,6 @@ Engine.prototype = {
case "Alias":
this._alias = child.textContent;
break;
case "SuggestionUrl":
this._createSuggestionURI(child.textContent);
break;
case "SearchForm":
this._searchForm = child.textContent;
break;
@ -1637,13 +1653,6 @@ Engine.prototype = {
closeSafeOutputStream(fos);
},
_createSuggestionURI: function SRCH_ENG_createSuggestionURI (aURLString) {
var suggestionURI = makeURI(aURLString, this._queryCharset);
if (suggestionURI &&
(suggestionURI.schemeIs("http") || suggestionURI.schemeIs("https")))
this._suggestionURI = suggestionURI;
},
/**
* Remove the engine's file from disk. The search service calls this once it
* removes the engine from its internal store. This function will throw if
@ -1692,10 +1701,6 @@ Engine.prototype = {
}
},
get suggestionURI() {
return this._suggestionURI;
},
get iconURI() {
return this._iconURI;
},
@ -1731,7 +1736,9 @@ Engine.prototype = {
if (!this._searchForm) {
// No searchForm specified in the engine definition file, use the prePath
// (e.g. https://foo.com for https://foo.com/search.php?q=bar).
this._searchForm = makeURI(this._urls[0].template).prePath;
var htmlUrl = this._getURLOfType(URLTYPE_SEARCH_HTML);
ENSURE_WARN(htmlUrl, "Engine has no HTML URL!", Cr.NS_ERROR_UNEXPECTED);
this._searchForm = makeURI(htmlUrl.template).prePath;
}
return this._searchForm;
@ -1743,19 +1750,33 @@ Engine.prototype = {
return this._queryCharset = queryCharsetFromCode(/* get the default */);
},
addParam: function SRCH_ENG_addParam(aName, aValue) {
// from nsISearchEngine
addParam: function SRCH_ENG_addParam(aName, aValue, aResponseType) {
ENSURE_ARG(aName && (aValue != null),
"missing name or value for nsISearchEngine::addParam!");
ENSURE_WARN(!this._readOnly,
"called nsISearchEngine::addParam on a read-only engine!",
Cr.NS_ERROR_FAILURE);
if (!aResponseType)
aResponseType = URLTYPE_SEARCH_HTML;
this._urls[0].addParam(aName, aValue);
var url = this._getURLOfType(aResponseType);
ENSURE(url, "Engine object has no URL for response type " + aResponseType,
Cr.NS_ERROR_FAILURE);
url.addParam(aName, aValue);
},
getSubmission: function SRCH_ENG_getSubmission(aData) {
ENSURE_WARN(this._urls[0], "engine object has no URL!",
Cr.NS_ERROR_UNEXPECTED);
// from nsISearchEngine
getSubmission: function SRCH_ENG_getSubmission(aData, aResponseType) {
if (!aResponseType)
aResponseType = URLTYPE_SEARCH_HTML;
var url = this._getURLOfType(aResponseType);
if (!url)
return null;
if (!aData) {
// Return a dummy submission object with our searchForm attribute
@ -1773,7 +1794,12 @@ Engine.prototype = {
data = textToSubURI.ConvertAndEscape(DEFAULT_QUERY_CHARSET, aData);
}
LOG("getSubmission: Out data: \"" + data + "\"");
return this._urls[0].getSubmission(data); //XXX only support one URL?
return url.getSubmission(data);
},
// from nsISearchEngine
supportsResponseType: function SRCH_ENG_supportsResponseType(type) {
return (this._getURLOfType(type) != null);
},
// nsISupports
@ -1850,6 +1876,12 @@ SearchService.prototype = {
// might want to prompt the user in the case where the engine is being
// added through a user action
}
if (!aEngine.supportsResponseType(URLTYPE_SEARCH_HTML)) {
LOG("_addEngineToStore: Won't add engines that have no HTML output!");
return;
}
this._engines[aEngine.name] = aEngine;
this._sortedEngines.push(aEngine);
notifyAction(aEngine, SEARCH_ENGINE_ADDED);

Просмотреть файл

@ -36,6 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */
const SEARCH_RESPONSE_SUGGESTION_JSON = "application/x-suggestions+json";
/**
* Metadata describing the Web Search suggest mode
*/
@ -276,10 +278,10 @@ SuggestAutoComplete.prototype = {
_nextRequestTime: 0,
/**
* The last URI we requested against (so that we can tell if the
* The last engine we requested against (so that we can tell if the
* user switched engines).
*/
_serverErrorURI: null,
_serverErrorEngine: null,
/**
* The XMLHttpRequest object.
@ -348,6 +350,11 @@ SuggestAutoComplete.prototype = {
}
},
/**
* This is the URI that the last suggest request was sent to.
*/
_suggestURI: null,
/**
* Autocomplete results from the form history service get stored here.
*/
@ -421,19 +428,19 @@ SuggestAutoComplete.prototype = {
},
/**
* This checks to see if the new search suggestion URI is different
* This checks to see if the new search engine is different
* from the previous one, and if so clears any error state that might
* have accumulated for the old engine/URI.
* have accumulated for the old engine.
*
* @param uri The URI that the suggestion request would be sent to.
* @param engine The engine that the suggestion request would be sent to.
* @private
*/
_checkForEngineSwitch: function SAC__checkForEngineSwitch(uri) {
if (uri == this._serverErrorURI)
_checkForEngineSwitch: function SAC__checkForEngineSwitch(engine) {
if (engine == this._serverErrorEngine)
return;
// must've switched search providers, clear old errors
this._serverErrorURI = uri;
this._serverErrorEngine = engine;
this._clearServerErrors();
},
@ -479,10 +486,7 @@ SuggestAutoComplete.prototype = {
this._clearServerErrors();
var searchString, results, queryURLs;
var searchService = Cc["@mozilla.org/browser/search-service;1"].
getService(Ci.nsIBrowserSearchService);
var sandboxHost = searchService.currentEngine.suggestionURI.prePath;
var sandbox = new Components.utils.Sandbox(sandboxHost);
var sandbox = new Components.utils.Sandbox(this._suggestURI.prePath);
var results2 = Components.utils.evalInSandbox(responseText, sandbox);
if (results2[0]) {
@ -598,10 +602,13 @@ SuggestAutoComplete.prototype = {
this._listener = listener;
var suggestionURI = searchService.currentEngine.suggestionURI;
this._checkForEngineSwitch(suggestionURI);
var engine = searchService.currentEngine;
if (!searchString || !suggestionURI || !this._okToRequest()) {
this._checkForEngineSwitch(engine);
if (!searchString ||
!engine.supportsResponseType(SEARCH_RESPONSE_SUGGESTION_JSON) ||
!this._okToRequest()) {
// We have an empty search string (user pressed down arrow to see
// history), or the current engine has no suggest functionality,
// or we're in backoff mode; so just use local history.
@ -610,18 +617,21 @@ SuggestAutoComplete.prototype = {
return;
}
var suggestionURL = suggestionURI.spec;
// Actually do the search
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
this._request.open("GET", suggestionURL + encodeURIComponent(searchString), true);
var submission = engine.getSubmission(searchString,
SEARCH_RESPONSE_SUGGESTION_JSON);
this._suggestURI = submission.uri;
var method = (submission.postData ? "POST" : "GET");
this._request.open(method, this._suggestURI.spec, true);
var self = this;
function onReadyStateChange() {
self.onReadyStateChange();
}
this._request.onreadystatechange = onReadyStateChange;
this._request.send(null);
this._request.send(submission.postData);
if (this._includeFormHistory) {
this._sentSuggestRequest = true;

Просмотреть файл

@ -3,7 +3,7 @@
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/x-icon;base64,R0lGODlhEAAQAPfLAAATVikwdA8SnxUfgAsWpAAilholjxw4jBc7kwAlvQQ2sRMsoBUqqhMzuhY/vxw4tSgmiyM1mSUztiQ6sTE3sQ4qyxMxxRoyxiAuxR1CtBxJsBxasSJuuTFguBte0Rlf2xVc9h9W9xVjzxVr0gdj6BRh4R1o5yBcyiZbyydT1i9b2Ddb1iFY6CJg2Vpor1dzvEJu20Z0yi23QDy1REi2OUy0O1WzOVC4PU+tVUe5Sk2xQU2zRUO4UE21Ula2SmKEqWWF2HyPx2+a6X6e6Xqk1m+s78sUDs4UGdEQB9YfDdwaANEfHd0YEscjAM4mAM0qANIoD9IkGdslGswuItYgL4aP0ImP2YGZ36Opzaq2wq/S+rzX/7/e8MrS1MLO/sTb48rT8snX/83c89PZ+crq+cH1/9Dl/9Ln/93r/9fy/+Hf7P/42eDm/O7u/+T29uX2/eT2/+f4/+f5/+j/9u//8+3/9u7/9ur5/+j//+n//+v//u3//+7//e7//+////b66/T/6vX/6/f/7f/07fj/4fv/4Pj/5v/45v7/4/r+7/3/6fDw+Pfx//D/9/X/8fT/8/f/8ff/8/D///H///L8/fL///P///X7//b6/ff/+/T///b9//f///v19//w9v/09P/29v/x+f/y///z///1+v/1///2///3//j79P/58/z/8/z99/z/9v7/9P7/9vn7//v6//j9//n9//j///n///v//vv////4+v/5+//6+P/4///6/P/6/v/6///7///9+P/8+v/9+v7/+Pz////8/f/9/f79///8///9//7//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAMsALAAAAAAQABAAAAj/AEn4oIFjBw8bOnrMuJGjhowZM1T8UdYJUZ5ZcNRYWjSrVK5QU0DMmtUnzRAXEy4o6FCEy6NDTkQIq1MmRgM0eZTlCXMgQJtRSE4gmgUkwh1EiZTNUiamy6NUUExcuoJgDCdDjQg9KgVL2SNFT1hwEvKglLBWuixZ+jSrlSBdRlL04bBBkTBdpZTpIqWsFaBcTEr0QaEhl6dWlswKW6poDRUPlmAUQKWMkTJLc76QMQNGUZMWgIgkCFJnlq5WXigwkFClVZQQyuRgELAlk7JBymCZGYAF0ZEPrQixgUDAihxVdPpoAZAFUZIRfThxgvPCwAILDipk+OFG2ZIVoxApERtPfvwlvZ+kQFzPvv0MJQEBADs=</Image>
<SuggestionUrl>http://www.google.com/complete/search?csv=t&amp;qu=</SuggestionUrl>
<Url type="application/x-suggestions+json" method="GET" template="http://www.google.com/complete/search?csv=t&amp;qu={searchTerms}"/>
<Url type="text/html" method="GET" template="http://www.google.com/search">
<Param name="q" value="{searchTerms}"/>
<Param name="start" value="0"/>

Просмотреть файл

@ -2,8 +2,9 @@
<ShortName>Yahoo</ShortName>
<Description>Yahoo Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<SuggestionUrl>http://ff.search.yahoo.com/gossip?output=fxjson&amp;command=</SuggestionUrl>
<Image width="16" height="16">data:image/x-icon;base64,R0lGODlhEAAQAJECAP8AAAAAAP///wAAACH5BAEAAAIALAAAAAAQABAAAAIplI+py+0NogQuyBDEnEd2kHkfFWUamEzmpZSfmaIHPHrRguUm/fT+UwAAOw==</Image>
<Url type="application/x-suggestions+json" method="GET"
template="http://ff.search.yahoo.com/gossip?output=fxjson&amp;command={searchTerms}" />
<Url type="text/html" method="GET" template="http://search.yahoo.com/search">
<Param name="p" value="{searchTerms}"/>
<Param name="ei" value="UTF-8"/>