mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-08-30 01:20:43 +02:00
400 lines
14 KiB
JavaScript
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;
|
|
}
|