RetroZilla/extensions/cck/browser/resources/content/cckwizard/search.js
2015-10-20 23:03:22 -04:00

400 lines
14 KiB
JavaScript

# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Browser Search Service.
#
# The Initial Developer of the Original Code is
# Google Inc.
# Portions created by the Initial Developer are Copyright (C) 2005-2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <beng@google.com> (Original author)
# Gavin Sharp <gavin@gavinsharp.com>
# Joe Hughes <joe@retrovirus.com>
# Pamela Greene <pamg.bugs@gmail.com>
# Michael Kaply
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# This code is a subset of nsSearchService.js from the current trunk
# It is used to retrieve the name from a Sherlock SRC file
const PERMS_FILE = 0644;
const MODE_RDONLY = 0x01;
const SEARCH_DATA_XML = 1;
const SEARCH_DATA_TEXT = 2;
const SEARCH_TYPE_MOZSEARCH = 1;
const SEARCH_TYPE_SHERLOCK = 2;
const SEARCH_TYPE_OPENSEARCH = 3;
const NEW_LINES = /(\r\n|\r|\n)/;
const OPENSEARCH_NS_10 = "http://a9.com/-/spec/opensearch/1.0/";
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_LOCALNAME = "OpenSearchDescription";
const MOZSEARCH_NS_10 = "http://www.mozilla.org/2006/browser/search/";
const MOZSEARCH_LOCALNAME = "SearchPlugin";
// Returns false for whitespace-only or commented out lines in a
// Sherlock file, true otherwise.
function isUsefulLine(aLine) {
return !(/^\s*($|#)/i.test(aLine));
}
function getSearchEngineName(searchenginepath)
{
var data;
var type;
var dataType = SEARCH_DATA_TEXT;
var name;
var ext = searchenginepath.substring(searchenginepath.lastIndexOf('.') + 1,searchenginepath.length);
if (ext.toLowerCase() == "xml") {
dataType = SEARCH_DATA_XML;
}
var sourcefile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
sourcefile.initWithPath(searchenginepath);
var fileInStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
fileInStream.init(sourcefile, MODE_RDONLY, PERMS_FILE, false);
switch (dataType) {
case SEARCH_DATA_XML:
var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
var doc = domParser.parseFromStream(fileInStream, "UTF-8",
sourcefile.fileSize,
"text/xml");
data = doc.documentElement;
if (checkNameSpace(data, [MOZSEARCH_LOCALNAME], [MOZSEARCH_NS_10])) {
type = SEARCH_TYPE_MOZSEARCH;
name = parseAsOpenSearch(data);
} else if (checkNameSpace(data, [OPENSEARCH_LOCALNAME], OPENSEARCH_NAMESPACES)) {
type = SEARCH_TYPE_OPENSEARCH;
name = parseAsOpenSearch(data);
}
break;
case SEARCH_DATA_TEXT:
var binaryInStream = Components.classes["@mozilla.org/binaryinputstream;1"]
.createInstance(Components.interfaces.nsIBinaryInputStream);
binaryInStream.setInputStream(fileInStream);
data = binaryInStream.readByteArray(binaryInStream.available());
type = SEARCH_TYPE_SHERLOCK;
name = parseAsSherlock(data);
break;
}
fileInStream.close();
return name;
}
function fileCharsetFromCode(aCode) {
const codes = [
"x-mac-roman", // 0
"Shift_JIS", // 1
"Big5", // 2
"EUC-KR", // 3
"X-MAC-ARABIC", // 4
"X-MAC-HEBREW", // 5
"X-MAC-GREEK", // 6
"X-MAC-CYRILLIC", // 7
"X-MAC-DEVANAGARI" , // 9
"X-MAC-GURMUKHI", // 10
"X-MAC-GUJARATI", // 11
"X-MAC-ORIYA", // 12
"X-MAC-BENGALI", // 13
"X-MAC-TAMIL", // 14
"X-MAC-TELUGU", // 15
"X-MAC-KANNADA", // 16
"X-MAC-MALAYALAM", // 17
"X-MAC-SINHALESE", // 18
"X-MAC-BURMESE", // 19
"X-MAC-KHMER", // 20
"X-MAC-THAI", // 21
"X-MAC-LAOTIAN", // 22
"X-MAC-GEORGIAN", // 23
"X-MAC-ARMENIAN", // 24
"GB2312", // 25
"X-MAC-TIBETAN", // 26
"X-MAC-MONGOLIAN", // 27
"X-MAC-ETHIOPIC", // 28
"X-MAC-CENTRALEURROMAN", // 29
"X-MAC-VIETNAMESE", // 30
"X-MAC-EXTARABIC" // 31
];
// Sherlock files have always defaulted to x-mac-roman, so do that here too
return codes[aCode] || codes[0];
}
/**
* Returns a string interpretation of aBytes using aCharset, or null on
* failure.
*/
function bytesToString(aBytes, aCharset) {
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
try {
converter.charset = aCharset;
return converter.convertFromByteArray(aBytes, aBytes.length);
} catch (ex) {}
return null;
}
/**
* Converts an array of bytes representing a Sherlock file into an array of
* lines representing the useful data from the file.
*
* @param aBytes
* The array of bytes representing the Sherlock file.
* @param aCharsetCode
* An integer value representing a character set code to be passed to
* fileCharsetFromCode, or null for the default Sherlock encoding.
*/
function sherlockBytesToLines(aBytes, aCharsetCode) {
// fileCharsetFromCode returns the default encoding if aCharsetCode is null
var charset = fileCharsetFromCode(aCharsetCode);
var dataString = bytesToString(aBytes, charset);
// Split the string into lines, and filter out comments and
// whitespace-only lines
return dataString.split(NEW_LINES).filter(isUsefulLine);
}
/**
* Extract search engine information from the collected data to initialize
* the engine object.
*/
function parseAsSherlock(data) {
/**
* Trims leading and trailing whitespace from aStr.
*/
function sTrim(aStr) {
return aStr.replace(/^\s+/g, "").replace(/\s+$/g, "");
}
/**
* Extracts one Sherlock "section" from aSource. A section is essentially
* an HTML element with attributes, but each attribute must be on a new
* line, by definition.
*
* @param aLines
* An array of lines from the sherlock file.
* @param aSection
* The name of the section (e.g. "search" or "browser"). This value
* is not case sensitive.
* @returns an object whose properties correspond to the section's
* attributes.
*/
function getSection(aLines, aSection) {
var lines = aLines;
var startMark = new RegExp("^\\s*<" + aSection.toLowerCase() + "\\s*",
"gi");
var endMark = /\s*>\s*$/gi;
var foundStart = false;
var startLine, numberOfLines;
// Find the beginning and end of the section
for (var i = 0; i < lines.length; i++) {
if (foundStart) {
if (endMark.test(lines[i])) {
numberOfLines = i - startLine;
// Remove the end marker
lines[i] = lines[i].replace(endMark, "");
// If the endmarker was not the only thing on the line, include
// this line in the results
if (lines[i])
numberOfLines++;
break;
}
} else {
if (startMark.test(lines[i])) {
foundStart = true;
// Remove the start marker
lines[i] = lines[i].replace(startMark, "");
startLine = i;
// If the line is empty, don't include it in the result
if (!lines[i])
startLine++;
}
}
}
lines = lines.splice(startLine, numberOfLines);
var section = {};
for (var i = 0; i < lines.length; i++) {
var line = sTrim(lines[i]);
var els = line.split("=");
var name = sTrim(els.shift().toLowerCase());
var value = sTrim(els.join("="));
if (!name || !value)
continue;
// Strip leading and trailing whitespace, remove quotes from the
// value, and remove any trailing slashes or ">" characters
value = value.replace(/^["']/, "")
.replace(/["']\s*[\\\/]?>?\s*$/, "") || "";
value = sTrim(value);
// Don't clobber existing attributes
if (!(name in section))
section[name] = value;
}
return section;
}
// First try converting our byte array using the default Sherlock encoding.
// If this fails, or if we find a sourceTextEncoding attribute, we need to
// reconvert the byte array using the specified encoding.
var sherlockLines, searchSection, sourceTextEncoding;
try {
sherlockLines = sherlockBytesToLines(data);
searchSection = getSection(sherlockLines, "search");
sourceTextEncoding = parseInt(searchSection["sourcetextencoding"]);
if (sourceTextEncoding) {
// Re-convert the bytes using the found sourceTextEncoding
sherlockLines = sherlockBytesToLines(data, sourceTextEncoding);
searchSection = getSection(sherlockLines, "search");
}
} catch (ex) {
// The conversion using the default charset failed. Remove any non-ascii
// bytes and try to find a sourceTextEncoding.
var asciiBytes = data.filter(function (n) {return !(0x80 & n);});
var asciiString = String.fromCharCode.apply(null, asciiBytes);
sherlockLines = asciiString.split(NEW_LINES).filter(isUsefulLine);
searchSection = getSection(sherlockLines, "search");
sourceTextEncoding = parseInt(searchSection["sourcetextencoding"]);
if (sourceTextEncoding) {
sherlockLines = sherlockBytesToLines(data, sourceTextEncoding);
searchSection = getSection(sherlockLines, "search");
} else {
// ERROR("Couldn't find a working charset", Cr.NS_ERROR_FAILURE);
}
}
return searchSection["name"];
}
/**
* Used to verify a given DOM node's localName and namespaceURI.
* @param aElement
* The element to verify.
* @param aLocalNameArray
* An array of strings to compare against aElement's localName.
* @param aNameSpaceArray
* An array of strings to compare against aElement's namespaceURI.
*
* @returns false if aElement is null, or if its localName or namespaceURI
* does not match one of the elements in the aLocalNameArray or
* aNameSpaceArray arrays, respectively.
* @throws NS_ERROR_INVALID_ARG if aLocalNameArray or aNameSpaceArray are null.
*/
function checkNameSpace(aElement, aLocalNameArray, aNameSpaceArray) {
return (aElement &&
(aLocalNameArray.indexOf(aElement.localName) != -1) &&
(aNameSpaceArray.indexOf(aElement.namespaceURI) != -1));
}
/**
* Extract search engine information from the collected data to initialize
* the engine object.
*/
function parseAsOpenSearch(data) {
var doc = data;
for (var i = 0; i < doc.childNodes.length; ++i) {
var child = doc.childNodes[i];
switch (child.localName) {
case "ShortName":
return child.textContent;
break;
case "Image":
// this._parseImage(child);
break;
}
}
}
function getSearchEngineImage(searchenginepath) {
var ext = searchenginepath.substring(searchenginepath.lastIndexOf('.') + 1,searchenginepath.length);
if (ext.toLowerCase() != "xml") {
return;
}
// assume we're already open search
var data;
var sourcefile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
sourcefile.initWithPath(searchenginepath);
var fileInStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
fileInStream.init(sourcefile, MODE_RDONLY, PERMS_FILE, false);
var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
var doc = domParser.parseFromStream(fileInStream, "UTF-8",
sourcefile.fileSize,
"text/xml");
data = doc.documentElement;
for (var i = 0; i < data.childNodes.length; ++i) {
var child = data.childNodes[i];
switch (child.localName) {
case "Image":
fileInStream.close();
return child.textContent;
break;
}
}
fileInStream.close();
return;
}