I'm trying to update/refactor a legacy Firefox extension that makes extensive use of the XPCOM API. We do a lot of XSLT transforms and client-side file IO for a specialized application.
I keep getting strange errors, though, when processing XSLT transforms.
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]
What the heck is "Unexpected error" and what could be causing it? Before I was getting the even more baffling "NS_ERROR_FAILURE (0x80004005)" which is the equivalent of Firefox throwing up its hands and shrugging.
Any ideas or suggestions are very welcome! Thanks in advance. :)
Edit to add: I should have said before, as this could affect the answer: I'm running this on Mac OSX in Firefox 38 ESR. I'm going to try calling an external xsltproc process next, though I'd prefer to use Firefox's native processor. I wonder if this could be some kind of file permissions problem? Please let me know what you think.
I've modified the Helloworld2 extension available here to include this code:
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
},
}
Here are my raw files: 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>
Edit. See my answer below, where I solved the problem using an external process to run the transform. When I tried porting this to my 'real' code, I found that I am still getting exit status of 0, but the file is not being produced. When I run the transform manually, using the very same args the code puts together, the file gets created just fine. Is there some kind of permissions limitation on file IO by processes called by XPCOM? It worked in my proof-of-concept so I think not? But I've spent days and days now trying to solve this problem and I'm grasping for an answer.