我正在尝试更新/重构一个遗留的Firefox扩展,该扩展广泛使用了XPCOM API。我们为一个专门的应用程序做了大量的XSLT转换和客户端文件IO。
但是,在处理XSLT转换时,我总是得到奇怪的错误。transformiix start
name of first node in the file file:///Users/sabrina/Documents/tmp/foo.xml: foo
name of first node in the file file:///Users/sabrina/Documents/xslt/foostyle.xslt: xsl:stylesheet
transformiix error: [Exception... "Unexpected error" nsresult: "0x8000ffff (NS_ERROR_UNEXPECTED)" location: "JS frame :: chrome://xulschoolhello/content/test.js :: ProcTest.Test.makeAndShow :: line 30" data: no]
什么是"意外错误",什么可能导致它?在我得到更令人困惑的"NS_ERROR_FAILURE (0x80004005)"之前,这相当于Firefox举手并耸耸肩。
任何想法或建议都非常欢迎!提前感谢。:)
编辑补充:我之前应该说过,因为这可能会影响答案:我在Mac OSX和Firefox 38 ESR上运行这个。接下来我将尝试调用外部xsltproc进程,不过我更喜欢使用Firefox的本地处理器。我想知道这是否可能是某种文件权限问题?请让我知道你的想法。
我已经修改了这里可用的Helloworld2扩展以包含以下代码:
ProcTest.Test = {
base: "file:///Users/sabrina/Documents/",
consoleService: Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService),
makeAndShow: function() {
var outXML = null;
ProcTest.Test.consoleService.logStringMessage("transformiix start");
try {
// load xml
var xmlDoc = ProcTest.Test.readXML(ProcTest.Test.base + "tmp/foo.xml");
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("error loading xml: " + x);
return false;
}
try {
// get xslt processor
var xsltProc = ProcTest.Test.getXSLTProc(ProcTest.Test.base + "xslt/foostyle.xslt");
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("error getting processor: " + x);
return false;
}
// send in the params
xsltProc.setParameter(null, "blah", "thingy!")
try {
// transform!
outXML = xsltProc.transformToDocument(xmlDoc);
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("transformiix error: " + x);
return false;
}
try {
// save
ProcTest.Test.saveXML(outXML, ProcTest.Test.base + "xhtml/login.xhtml");
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("error saving xml: " + x);
return false;
}
ProcTest.Test.consoleService.logStringMessage("looks like it worked!")
content.window.location.replace(ProcTest.Test.base + "xhtml/login.xhtml")
},
readXML: function(filepath) {
var myXMLHTTPRequest = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);
myXMLHTTPRequest.open("GET", filepath, false);
myXMLHTTPRequest.send(null);
ProcTest.Test.consoleService.logStringMessage("name of first node in the file " + filepath + ": " + myXMLHTTPRequest.responseXML.firstChild.nodeName)
return myXMLHTTPRequest.responseXML;
},
getXSLTProc: function(xslt) {
var xslStylesheet = ProcTest.Test.readXML(xslt);
var xsltProcessor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"].createInstance(Components.interfaces.nsIXSLTProcessor)
xsltProcessor.importStylesheet(xslStylesheet);
return xsltProcessor
},
saveXML: function (doc, path) {
try {
var serializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Components.interfaces.nsIDOMSerializer);
// get or create a file object
var file = ProcTest.Test.fileObject(path)
// prepare the xml object
var xml = doc
xml = content.document.implementation.createDocument('', '', doc.doctype)
xml.appendChild(xml.importNode(doc.documentElement, true))
// prepare the output stream object
var output = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream)
output.init(file, 0x20 | 0x02 | 0x08, 0665, 0)
// send the file to the output stream
serializer.serializeToStream(xml, output, 'UTF-8')
output.flush()
output.close()
} catch(e) {
ProcTest.Test.consoleService.logStringMessage("error saving xml: " + e)
}
},
fileObject: function(path) {
var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile)
try {
file.initWithPath(ProcTest.Test.base)
var splitPath = path.split('/')
for (var i = 0; i < splitPath.length; ++i) {
file.appendRelativePath(splitPath[i])
if (i < splitPath.length - 1) {
if (file.exists()) {
if (!file.isDirectory)
throw "expecting directory: " + file.path
} else {
file.create(Components.interfaces.nsILocalFile.DIRECTORY_TYPE, 0775)
}
}
}
} catch (e) {
ProcTest.Test.consoleService.logStringMessage("error making file " + path + ": " + e)
throw e
}
return file
},
}
以下是我的原始文件:foo.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>apples</bar>
<bar>oranges</bar>
</foo>
foostyle.xslt
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output encoding="UTF-8" method="xml" indent="yes"/>
<xsl:param name="blah"/>
<xsl:template match="bar">
<option value="text()">
<xsl:value-of select="text()"/>
</option>
</xsl:template>
<xsl:template match="foo">
<select id="bars">
<xsl:apply-templates select="bar"/>
</select>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<title>Foo</title>
</head>
<body>
<div id="foo">
<xsl:apply-templates select="foo"/>
</div>
<div>blah: <xsl:value-of select="$blah"/></div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
编辑。请参阅下面我的答案,在这里我使用外部流程来运行转换来解决问题。当我尝试将其移植到我的"真实"代码时,我发现我仍然得到0的退出状态,但没有生成文件。当我手动运行转换时,使用代码放在一起的相同参数,文件被创建得很好。XPCOM调用的进程对文件IO是否有某种权限限制?它在我的概念验证中有效,所以我认为不是吗?但是我已经花了很多天的时间来解决这个问题,我正在努力寻找一个答案。
您执行xsltProc.setParameter(null, "blah", "thingy!")
,因此您为名为blah
的参数传递一个原始字符串值,然后您执行<xsl:apply-templates select="$blah"/>
,这意味着您尝试将apply-templates
转换为原始字符串值。这在XSLT 1.0 (Firefox支持)和当前的XSLT 2.0版本中都不起作用。我认为这在XSLT 3.0中是可能的。在XSLT 1.0中,只能将模板应用于节点集。
我不确定你想要实现什么,我认为做<xsl:value-of select="$blah"/>
是所有有意义的字符串值在一个参数
这个解决方案可以工作,但是它使用了一种完全不同的方法:它调用一个外部进程来执行转换并保存xhtml输出。与上面显示的相同的xml和(更正的)xslt。
控制台输出:
transformiix start
xsltproc exit status code: 0
looks like it worked!
它使用不同于OSX附带的xsltproc二进制文件。它自称为Using libxml 20900, libxslt 10128 and libexslt 817
ProcTest.Test = {
base: "file:///Users/sabrina/Documents/",
consoleService: Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService),
makeAndShow: function() {
var xmlPath = ProcTest.Test.base + "tmp/foo.xml"
var xsltPath = ProcTest.Test.base + "xslt/foostyle.xslt"
var outPath = ProcTest.Test.base + "xhtml/foo.xhtml"
var xsltProc = ProcTest.Test.base + "osx/bin/xsltproc"
var pid = {}
var FileUtils = Cu.import("resource://gre/modules/FileUtils.jsm").FileUtils
ProcTest.Test.consoleService.logStringMessage("transformiix start");
var args = []
var proc = Components.classes['@mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess)
// initialize the external proc call
try {
var file = new FileUtils.File( "/Users/sabrina/Documents/osx/bin/xsltproc" )
proc.init(file)
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("error initializing file object for proc: " + x);
return false;
}
// set up the commandline args
args.push('--output', outPath)
args.push('--stringparam', 'blah', "thingy!")
args.push(xsltPath, xmlPath)
try {
// transform!
proc.run(true, args, args.length, pid);
} catch (x) {
ProcTest.Test.consoleService.logStringMessage("transformiix error: " + x);
return false;
}
ProcTest.Test.consoleService.logStringMessage("xsltproc exit status code: " + proc.exitValue)
if (proc.exitValue == 0) {
ProcTest.Test.consoleService.logStringMessage("looks like it worked!")
content.window.location.replace(outPath)
} else {
ProcTest.Test.consoleService.logStringMessage("xsltproc exit status code: " + proc.exitValue)
}
},
}