Bug 335691: better support for OpenSearch parameters, r=mconnor

This commit is contained in:
gavin%gavinsharp.com 2006-06-28 16:09:26 +00:00
parent bea6a3f15c
commit d2ff93ee21

View File

@ -89,8 +89,8 @@ const NEW_LINES = /(\r\n|\r|\n)/;
// cause big delays when loading them at startup.
const MAX_ICON_SIZE = 10000;
// Default charset to use for sending search parameters. This is used to match
// previous nsInternetSearchService behavior.
// Default charset to use for sending search parameters. ISO-8859-1 is used to
// match previous nsInternetSearchService behavior.
const DEFAULT_QUERY_CHARSET = "ISO-8859-1";
const SEARCH_BUNDLE = "chrome://browser/locale/search.properties";
@ -102,9 +102,12 @@ const OPENSEARCH_NS_11 = "http://a9.com/-/spec/opensearch/1.1/";
// Although the specification at http://opensearch.a9.com/spec/1.1/description/
// gives the namespace names defined above, many existing OpenSearch engines
// are using the following versions. We therefore allow either.
const OPENSEARCH_NAMESPACES = [ OPENSEARCH_NS_11, OPENSEARCH_NS_10,
"http://a9.com/-/spec/opensearchdescription/1.1/",
"http://a9.com/-/spec/opensearchdescription/1.0/"];
const OPENSEARCH_NAMESPACES = [
OPENSEARCH_NS_11, OPENSEARCH_NS_10,
"http://a9.com/-/spec/opensearchdescription/1.1/",
"http://a9.com/-/spec/opensearchdescription/1.0/"
];
const OPENSEARCH_LOCALNAME = "OpenSearchDescription";
const MOZSEARCH_NS_10 = "http://www.mozilla.org/2006/browser/search/";
@ -122,15 +125,43 @@ const EMPTY_DOC = "<?xml version=\"1.0\"?>\n" +
const BROWSER_SEARCH_PREF = "browser.search.";
// Unsupported search parameters, which will be replaced with blanks.
// XXX We do use inputEncoding - should consider having it available. This
// would require doing multiple parameter substitution, so just having
// searchTerms is sufficient for now.
const kInvalidWords = /(\{count\})|(\{startIndex\})|(\{startPage\})|(\{language\})|(\{outputEncoding\})|(\{inputEncoding\})/;
const USER_DEFINED = "{searchTerms}";
// Supported search parameters.
const kValidWords = /\{searchTerms\}/gi;
const kUserDefined = "{searchTerms}";
// Supported OpenSearch parameters
// See http://opensearch.a9.com/spec/1.1/querysyntax/#core
const OS_PARAM_USER_DEFINED = /\{searchTerms\??\}/g;
const OS_PARAM_INPUT_ENCODING = /\{inputEncoding\??\}/g;
const OS_PARAM_LANGUAGE = /\{language\??\}/g;
const OS_PARAM_OUTPUT_ENCODING = /\{outputEncoding\??\}/g;
// Default values
const OS_PARAM_LANGUAGE_DEF = "*";
const OS_PARAM_OUTPUT_ENCODING_DEF = "UTF-8";
// "Unsupported" OpenSearch parameters. For example, we don't support
// page-based results, so if the engine requires that we send the "page index"
// parameter, we'll always send "1".
const OS_PARAM_COUNT = /\{count\??\}/g;
const OS_PARAM_START_INDEX = /\{startIndex\??\}/g;
const OS_PARAM_START_PAGE = /\{startPage\??\}/g;
// Default values
const OS_PARAM_COUNT_DEF = "20"; // 20 results
const OS_PARAM_START_INDEX_DEF = "1"; // start at 1st result
const OS_PARAM_START_PAGE_DEF = "1"; // 1st page
// Optional parameter
const OS_PARAM_OPTIONAL = /\{\w+\?\}/g;
// A array of arrays containing parameters that we don't fully support, and
// their default values. We will only send values for these parameters if
// required, since our values are just really arbitrary "guesses" that should
// give us the output we want.
var OS_UNSUPPORTED_PARAMS = [
[OS_PARAM_COUNT, OS_PARAM_COUNT_DEF],
[OS_PARAM_START_INDEX, OS_PARAM_START_INDEX_DEF],
[OS_PARAM_START_PAGE, OS_PARAM_START_PAGE_DEF],
];
// Returns false for whitespace-only or commented out lines in a
// Sherlock file, true otherwise.
@ -138,16 +169,6 @@ function isUsefulLine(aLine) {
return !(/^\s*($|#)/i.test(aLine));
}
/**
* Used to determine whether an "input" line from a Sherlock file is a "user
* defined" input. That is, check for the string "user", preceded by either
* whitespace or a quote, followed by any of ">", "=", """, "'", whitespace,
* a slash, "+", or EOL.
*/
const kIsUserInput = /(\s|["'=])user(\s|[>="'\/\\+]|$)/i;
var MozStorageStatementWrapper = null;
/**
* Prefixed to all search debug output.
*/
@ -536,6 +557,23 @@ function sherlockBytesToLines(aBytes, aCharsetCode) {
return dataString.split(NEW_LINES).filter(isUsefulLine);
}
/**
* Gets the current value of the locale. It's possible for this preference to
* be localized, so we have to do a little extra work here. Similar code
* exists in nsHttpHandler.cpp when building the UA string.
*/
function getLocale() {
const localePref = "general.useragent.locale";
var locale = getLocalizedPref(localePref);
if (locale)
return locale;
// Not localized
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
return prefs.getCharPref(localePref);
}
/**
* Wrapper for nsIPrefBranch::getComplexValue.
* @param aPrefName
@ -647,21 +685,54 @@ function notifyAction(aEngine, aVerb) {
/**
* Simple object representing a name/value pair.
* @throws NS_ERROR_NOT_IMPLEMENTED if the provided value includes unsupported
* parameters.
* @see kInvalidWords.
*/
function QueryParameter(aName, aValue) {
ENSURE_ARG(aName && (aValue != null), "missing name or value for QueryParameter!");
ENSURE(!kInvalidWords.test(aValue),
"Illegal value while creating a QueryParameter",
Cr.NS_ERROR_NOT_IMPLEMENTED);
ENSURE_ARG(aName && (aValue != null),
"missing name or value for QueryParameter!");
this.name = aName;
this.value = aValue;
}
/**
* Perform OpenSearch parameter substitution on aParamValue.
*
* @param aParamValue
* A string containing OpenSearch search parameters.
* @param aSearchTerms
* The user-provided search terms. This string will inserted into
* aParamValue as the value of the OS_PARAM_USER_DEFINED parameter.
* This value must already be escaped appropriately - it is inserted
* as-is.
* @param aQueryEncoding
* The value to use for the OS_PARAM_INPUT_ENCODING parameter. See
* definition in the OpenSearch spec.
*
* @see http://opensearch.a9.com/spec/1.1/querysyntax/#core
*/
function ParamSubstitution(aParamValue, aSearchTerms, aQueryEncoding) {
var value = aParamValue;
// Insert the parameters we're confident about
value = value.replace(OS_PARAM_USER_DEFINED, aSearchTerms);
value = value.replace(OS_PARAM_INPUT_ENCODING, aQueryEncoding);
value = value.replace(OS_PARAM_LANGUAGE,
getLocale() || OS_PARAM_LANGUAGE_DEF);
value = value.replace(OS_PARAM_OUTPUT_ENCODING,
OS_PARAM_OUTPUT_ENCODING_DEF);
// Replace any optional parameters
value = value.replace(OS_PARAM_OPTIONAL, "");
// Insert any remaining required params with our default values
for (var i = 0; i < OS_UNSUPPORTED_PARAMS.length; ++i) {
value = value.replace(OS_UNSUPPORTED_PARAMS[i][0],
OS_UNSUPPORTED_PARAMS[i][1]);
}
return value;
}
/**
* Creates a mozStorage statement that can be used to access the database we
* use to hold metadata.
@ -671,7 +742,8 @@ function QueryParameter(aName, aValue) {
*/
function createStatement (dbconn, sql) {
var stmt = dbconn.createStatement(sql);
var wrapper = new MozStorageStatementWrapper();
var wrapper = Cc["@mozilla.org/storage/statement-wrapper;1"].
createInstance(Ci.mozIStorageStatementWrapper);
wrapper.initialize(stmt);
return wrapper;
@ -693,10 +765,7 @@ function createStatement (dbconn, sql) {
*
* @see http://opensearch.a9.com/spec/1.1/querysyntax/#urltag
*
* @throws NS_ERROR_NOT_IMPLEMENTED if aType is unsupported. If invalid
* (unsupported) parameters are included in aTemplate, they will be
* replaced with blanks in the final query, so no error needs to be
* returned here.
* @throws NS_ERROR_NOT_IMPLEMENTED if aType is unsupported.
*/
function EngineURL(aType, aMethod, aTemplate) {
ENSURE_ARG(aType && aMethod && aTemplate,
@ -735,41 +804,23 @@ EngineURL.prototype = {
this.params.push(new QueryParameter(aName, aValue));
},
getSubmission: function SRCH_EURL_getSubmission(aData) {
/**
* From an array of QueryParameter objects, generates a string in the
* application/x-www-form-urlencoded format:
* name=value&name=value&name=value...
* Any invalid or unimplemented query fields will be replaced with empty
* strings.
* @param aParams
* An array of QueryParameter objects
* @param aData
* Data to be substituted into parameter values using the
* |kValidWords| regexp
* @returns A string of encoded param names and values in
* application/x-www-form-urlencoded format.
*
* @see kInvalidWords
*/
function makeQueryString(aParams, aData) {
var str = "";
for (var i = 0; i < aParams.length; ++i) {
var param = aParams[i];
var value = param.value.replace(kValidWords, aData);
str += (i > 0 ? "&" : "") + param.name + "=" + value;
}
return str;
getSubmission: function SRCH_EURL_getSubmission(aSearchTerms, aQueryCharset) {
var url = ParamSubstitution(this.template, aSearchTerms, aQueryCharset);
// Create an application/x-www-form-urlencoded representation of our params
// (name=value&name=value&name=value)
var dataString = "";
for (var i = 0; i < this.params.length; ++i) {
var param = this.params[i];
var value = ParamSubstitution(param.value, aSearchTerms, aQueryCharset);
dataString += (i > 0 ? "&" : "") + param.name + "=" + value;
}
// Replace known fields with given parameters and clear unknown fields.
var url = this.template.replace(kValidWords, aData);
url = url.replace(kInvalidWords, "");
var postData = null;
var dataString = makeQueryString(this.params, aData);
if (this.method == "GET") {
// GET method requests have no post data, and append the encoded
// text to the url...
// query string to the url...
if (url.indexOf("?") == -1 && dataString)
url += "?";
url += dataString;
@ -1379,9 +1430,9 @@ Engine.prototype = {
/**
* Returns an array of name-value pair arrays representing the Sherlock
* file's input elements. User defined inputs return kUserDefined as the
* value. Elements are returned in the order they appear in the source
* file.
* file's input elements. User defined inputs return USER_DEFINED
* as the value. Elements are returned in the order they appear in the
* source file.
*
* Example:
* <input name="foo" value="bar">
@ -1414,6 +1465,10 @@ Engine.prototype = {
* doesn't exist.
*/
function getAttr(aAttr, aLine) {
// Used to determine whether an "input" line from a Sherlock file is a
// "user defined" input.
const userInput = /(\s|["'=])user(\s|[>="'\/\\+]|$)/i;
LOG("_parseAsSherlock::getAttr: Getting attr: \"" +
aAttr + "\" for line: \"" + aLine + "\"");
// We're not case sensitive, but we want to return the attribute value
@ -1423,12 +1478,13 @@ Engine.prototype = {
var attrStart = lLine.search(new RegExp("\\s" + attr, "i"));
if (attrStart == -1) {
// If this is the "user defined input" (i.e. contains the empty
// "user" attribute), return our special keyword
if (kIsUserInput.test(lLine) && attr == "value") {
if (userInput.test(lLine) && attr == "value") {
LOG("_parseAsSherlock::getAttr: Found user input!\nLine:\"" + lLine
+ "\"");
return kUserDefined;
return USER_DEFINED;
}
// The attribute doesn't exist - ignore
LOG("_parseAsSherlock::getAttr: Failed to find attribute:\nLine:\""
@ -1569,7 +1625,7 @@ Engine.prototype = {
var value = inputs[i][1];
if (i==0) {
if (name == "")
template += kUserDefined;
template += USER_DEFINED;
else
template += "?" + name + "=" + value;
} else if (name != "")
@ -1834,7 +1890,7 @@ Engine.prototype = {
data = textToSubURI.ConvertAndEscape(DEFAULT_QUERY_CHARSET, aData);
}
LOG("getSubmission: Out data: \"" + data + "\"");
return url.getSubmission(data);
return url.getSubmission(data, this.queryCharset);
},
// from nsISearchEngine
@ -2401,14 +2457,11 @@ var engineMetadataService = {
newTable: false,
init: function epsInit() {
MozStorageStatementWrapper =
new Components.Constructor("@mozilla.org/storage/statement-wrapper;1",
Ci.mozIStorageStatementWrapper);
var engineDataTable = "id INTEGER PRIMARY KEY, engineid STRING, name STRING, value STRING";
var file = getDir(NS_APP_USER_PROFILE_50_DIR);
file.append("search.sqlite");
var dbService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
file.append("search.sqlite");
this.mDB = dbService.openDatabase(file);
try {