transformToFragment on webkit browsers

I just want to document a solution to a problem I was having with transformToFragment.

I was having trouble with XSLTProcessor::transformToFragment() returning null on WebKit browsers (Safari and Chrome). Having read comments about issues with the load() method and xsl:import element, I used XMLHttpRequest and a very simple stylesheet and XML document for testing. Even after having eliminated the well-known problems, I was still disappointed to get a null return value from transformToFragment().

Finally, I noticed that one example used:
<xsl:output method=“html” />
where I had been using:
<xsl:output method=“xml” … />

When I changed my method to “html”, the transformToFragment() worked.

I hope this small bit of information will help someone.

Having fixed my first problem with transformToFragment, I discovered a second: the WebKit implementation of the function [Webkit-unassigned] [Bug 28744] XSLTProcessor transformToFragment mistakenly treats source node as root one

In other words, the node you pass to transformToFragment is treated as a document element and thus causes your <xsl:template match=“/”> to run.

I can think of several ways this could defeat a stylesheet design if the stylesheet is used to process arbitrary document nodes for an XSL-driven web page.

I offer this simple function to pre-process an XSL document prior to calling XSLTProcessor::importStylesheet, assuming that the contents of the document-matching template are not needed once the page has been rendered:

function fixWebKitStylesheet(doc)
{
   // ensure output method = html:
   var node = doc.selectSingleNode("/xsl:stylesheet/xsl:output");
   if (node && node.getAttribute("method")=="xml")
      node.setAttribute("method", "html");

   // gut out document node template to deal with Webkit bug
   // Google: webkit bug 28744 "root one"
   if ((node=doc.selectSingleNode("/xsl:stylesheet/xsl:template[@match='/']")))
    {
      var child;
      while ((child=node.lastChild))
         node.removeChild(child);
      child = node.appendChild(doc.createElement("xsl:apply-templates"));
      child.setAttribute("select", "*");
   }
}

(Please forgive the extra conditional parens, emacs gripes if they’re not used)
and here is a sample if how that function might be called:

function getProcessor(doc)
{
   // detect WebKit implementation (incomplete example pulled out
   // of other code that handles cross-browser XMLDOM differences):
   var d = document.implementation.createDocument("","",null);
   var isWebKit = !(d.load);

   var proc = new XSLTProcessor();
   if (isWebKit)
      fixWebKitStylesheet(doc);

   proc.importStylesheet(doc);

   return proc;
}

It’s a relief to me that my XML-XSL methods can finally support Safari and now especially Chrome. The above code works for the ways I use transformToFragment, it may not work in every situation.

I hope this helps someone.

Chuck Jungmann