Firefox XPCOM 插件:用于 xslt transform 的"unexpected error"



我正在尝试更新/重构一个遗留的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)
        }
    },
}

相关内容

最新更新