Search code examples
javascriptfirefoxxpathfirefox-addonfirefox-addon-sdk

Error with XPath in a parsed XML document (WrongDocumentError)


I'm creating a Firefox for Android extension and I have a problem with a XML document retrieved from a XMLHttpRequest: I can't find a way to select a node. The better solution I found is this, but I got this error when selecting with xpath on the document:

WrongDocumentError: Node cannot be used in a document other than the one in which it was created

This is my code:

var parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser); 
var parsedXml = parser.parseFromString(xmlhttp.responseText, "text/xml");    
var xpathExpression = "//td[contains(.,'Raw text')]/../td[2]/pre"; 

var res = window.content.document.evaluate(xpathExpression, parsedXml, null, window.XPathResult.STRING_TYPE , null);

If I replace the "evaluate" with the next line:

var res = parsedXml.selectSingleNode(xpathExpression);

Then I get the following error:

[JavaScript Error: "parsedXml.selectSingleNode is not a function" {file: "resource://gre/modules/addons/XPIProvider.jsm -> jar:file:///data/data/org.mozilla.fennec/files/mozilla/ggz9zzjr.default/extensions/[email protected]!/bootstrap.js" line: 61}]


Solution

  • Well, the name of the exception, WrongDocumentErrort gave it away. You're trying to call .evaluate() on a DOM (Document) that does not belong to the same Document .evaluate() is bound to.

    The nsIDOMParser will actually return a new XMLDocument that has an .evaluate() itself, which you'll have to use.

    var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
                 createInstance(Ci.nsIDOMParser); 
    var parsedDoc = parser.parseFromString(
      '<?xml version="1.0"?>\n<doc><elem>Raw text</elem></doc>',
      "text/xml");    
    var xpathExpression = "//elem[contains(.,'Raw text')]"; 
    
    var res = parsedDoc.evaluate(
      xpathExpression,
      parsedDoc,
      null,
      XPathResult.STRING_TYPE,
      null);
    console.log(res, res.stringValue);
    

    Instead using nsIDOMParser, since your content seems to be originating from XHR anyway, and seems to be (X)HTML (indicated by your expression), it might be better to use XHR.responseType = "document" instead, which will parse a DOM from the response using the HTML parser.

    var req = new XMLHttpRequest();
    req.onload = function() {
      var doc = req.response;
      var h1 = doc.evaluate("//h1", doc, null, XPathResult.STRING_TYPE, null);
      console.log(h1.stringValue);
    
      // Alternative in some cases
      h1 = doc.querySelector("h1");
      console.log(h1.textContent);
    };
    req.open("GET", "http://example.org/");
    req.responseType = "document"; // Parse as text/html
    req.send();