13

I am trying to transform a XSLT using Javascript and trying to get it to work on Both Chrome and IE. Entry page is a.html. It's working fine in IE (both native/compatibility mode) but doesn't work in Chrome correctly i.e. dropdown is not created with options.

In chrome, however, if I open data.xml which has:

<?xml-stylesheet type="text/xsl" href="render.xslt" ?>

directly in chrome, it is transformed perfectly fine. But if I try to do that same using XSLTProcessor, it doesn't work. Specifically, the document function doesn't work. Could you please help?

My code is as follows.

Javascript:

var MSXML2_DOMDocument_6 = "MSXML2.DOMDocument.6.0";
function tranform(xml, xsl) {
    if (window.ActiveXObject || "ActiveXObject" in window) {
        var xmlSerializer = new XMLSerializer();
        var xmlString = xmlSerializer.serializeToString(xml);
        var xslString = xmlSerializer.serializeToString(xsl);

        var xsl = new ActiveXObject(MSXML2_DOMDocument_6);
        xsl.setProperty("AllowXsltScript", true);
        xsl.setProperty("AllowDocumentFunction", true);
        xsl.resolveExternals = true;
        xsl.async = false;
        xsl.loadXML(xslString);

        var xml = new ActiveXObject(MSXML2_DOMDocument_6);
        xml.resolveExternals = true;
        xml.preserveWhiteSpace = true;
        xml.async = false;
        xml.loadXML(xmlString);
        xml.resolveExternals = true;

        ex = xml.transformNode(xsl);
        document.getElementById("example").innerHTML = ex;
    } else if (document.implementation && document.implementation.createDocument) {
        xsltProcessor = new XSLTProcessor();
        xsltProcessor.importStylesheet(xsl);
        resultDocument = xsltProcessor.transformToFragment(xml, document);
        document.getElementById("example").appendChild(resultDocument);
    }
}

function loadXMLDoc() {

    $.ajax({
        method: "GET",
        url: "data.xml",
        dataType: "xml"
    }).then(function (xml) {
            console.log("done xml")
            $.ajax({
                method: "GET",
                url: "render.xslt",
                dataType: "xml"
            }).then(function (xsl) {
                console.log("done xsl")
                tranform(xml, xsl)
            })
        },
        function (e) {
            console.log("Got error in xml", e.status)
        })
}

$(loadXMLDoc)

foo/b.xml:

<dropdowns>
  <dropdown name="xyz">
    <option value="I">Info</option>
    <option value="C">Category</option>
  </dropdown>
</dropdowns>

data.xml:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="render.xslt" ?>
<catalog name="xyz" />

a.html:

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="jquery-3.3.1.js"></script>
    <title>Document</title>
</head>

<body>
    <div id="example"></div>
    <script src="b.js"></script>
</body>

</html>

render.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:import href="util.xslt" />
    <xsl:output method="html"></xsl:output>
    <xsl:template match="/">
        <h1>
            <xsl:value-of select="/catalog/@name"></xsl:value-of>
        </h1>
        <xsl:call-template name="dropdown">
            <xsl:with-param name="listname">xyz</xsl:with-param>
            <xsl:with-param name="value" select="/catalog/@name"/>
        </xsl:call-template>
    </xsl:template>
</xsl:stylesheet>

util.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"></xsl:output>
    <xsl:template name="dropdown">
        <xsl:param name="listname"/>
        <xsl:param name="value"/>
        <select>
            <xsl:for-each select="document('foo/b.xml')/dropdowns/dropdown[@name=$listname]/option">
                <option>
                    <xsl:attribute name="value">
                        <xsl:value-of select="@value"/>
                    </xsl:attribute>
                    <xsl:value-of select="."/>
                </option>
            </xsl:for-each>
        </select>
    </xsl:template>
</xsl:stylesheet>

I am sorry it's a bit long to be an MVE example but it's complete.

Please let me know if any clarification required.

potame
  • 7,597
  • 4
  • 26
  • 33
Gurwinder Singh
  • 38,557
  • 6
  • 51
  • 76
  • Could you try and give `document()` an absolute URI parameter (or alternatively use `xml:base` to set a context URI)? If it works with `` chances are the composition of the URI goes wrong. Does the console give error messages? – imhotap Jun 05 '18 at 12:07
  • I tried with absolute URL, but it still doesn't work. The console gives `Resource interpreted as Stylesheet but transferred with MIME type application/xslt+xml:` for both `render.xslt` and `util.xslt`. Would that be a cause of the issue? – Gurwinder Singh Jun 05 '18 at 12:09
  • I don't know. According to https://stackoverflow.com/questions/6715767/what-content-type-should-be-used-for-xmlxsl-presentation#8185070 the expected content type should be `text/xsl`, so you could try and tell your webserver to use that for delivery of `.xsl` files (though I doubt it'll help) – imhotap Jun 05 '18 at 12:59
  • @imhotap - Is it possible to set it to `text/xsl` anywhere? – Gurwinder Singh Jun 05 '18 at 13:40
  • Depends on your webserver. Before venturing into that you might want to try and run everything from a `file:` URL first (eg. copy all resources to local directory respecting the relative paths then open the main file using Chrome) to see if it helps at all; though you might need to change your Ajax call – imhotap Jun 05 '18 at 14:04
  • 1
    Chrome's security policy doesn't allow to import or document reference it seems. – Gurwinder Singh Jun 05 '18 at 14:51
  • I think the open Chromium bug https://bugs.chromium.org/p/chromium/issues/detail?id=718434 describes your problem, seems nobody is working to fix it. – Martin Honnen Jun 08 '18 at 10:18
  • @MartinHonnen - Is there a workaround that I can implement to get around this please? – Gurwinder Singh Jun 08 '18 at 11:11
  • A js hack or xslt trick? – Gurwinder Singh Jun 08 '18 at 11:42
  • I am not aware of any, at https://martin-honnen.github.io/xslt/2018/test201806080102.html I have tried to preload the secondary XML document with Javascript and XMLHttpRequest and then pass it as an `xsl:param` to XSLT, to avoid the use of the `document` function. However, while Firefox swallows that attempt just fine, Chrome doesn't like it and doesn't seem to be able to process the XML DOM document parameter passed in. – Martin Honnen Jun 08 '18 at 11:47
  • About how mime types are determined by browsers: https://stackoverflow.com/a/26303098/1019850 – David Jun 12 '18 at 17:54

3 Answers3

0

In general the whole approach is about nested templates.
This is already answered here: How to traverse a nested XML structure using templates

Below are still some issues and clarifications about the comments below the question.

Having adjusted the local server that it delivers the file util.xslt as Content-Type text/css like expected by chrome instead of application/xslt+xml, the scripts still never work like expected. Therefore there must be still another issue related to chrome.

enter image description here

The interesting point is that there are two xslt-documents while one is transferred and accepted by chrome with application/xslt+xml but the other one only as text/xml or text/xsl (yes, both are accepted by chrome, I tried).

Therefore an interesting post about versions of xml / xslt might not be useful concerning this problem.

Below are the console-messages in detail:

enter image description here

The adjustment of the server has been done with the following lines, that changes the Content-Type (mime-type) if the requested filename has the suffix xslt and the expected mime-type is text/css:

<If "%{REQUEST_FILENAME} =~ m#\.xslt$# && %{HTTP:Accept} =~ m#^text\/css#">
    Header set "Content-Type" "text/css"
</If>

FireFox

It might be noteworthy that firefox is hiding util.xslt in the network-panel completely:

enter image description here

Furthermore Firefox accepts both methods, ActiveXObject and XSLTProcessor. Depending on the logic in JavaScript the default is set to ActiveXObject, that version looks also smoother concerning the animation of the dropdown.

Firefox is complaining when the xml-version is set to 1.1, therefore playing with version numbers might not really help to increase browser-compatibility.

David
  • 5,882
  • 3
  • 33
  • 44
0

I have found a workaround[1] for this problem.

In chrome, <xsl:import> works correctly when transforming in Javascript. So, I converted my XML into XSL <xsl:choose> with <xsl:when> for each dropdown tag.

Update util.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"></xsl:output>
    <xsl:template name="dropdown">
        <xsl:param name="listname"/>
        <xsl:param name="value"/>
        <select>
            <xsl:choose>
                <xsl:when name="$listname='xyz'">
                    <option value="I">Info</option>
                    <option value="C">Category</option>
                </xsl:when>
            </xsl:choose>
        </select>
    </xsl:template>
</xsl:stylesheet>

1. I agree that this is not a very good workaround and certainly not a generalized one. If someone has a better solution, please post it as an answer.

Gurwinder Singh
  • 38,557
  • 6
  • 51
  • 76
0

This is known issue and opened since 2009. Also Chrome team don't have any plan to fix it even, they are in planning to remove it's support from chrome (Not sure when). But there is one workaround for this issue. transform your xml data into xslt and then you can import that in to stylesheet.

so you javaScript code will remain same.

foo/b.xml will become foo/b.xslt

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template name="dropdowns">
    <xsl:param name="listname"/>
    <xsl:param name="value"/>
    <xsl:choose>
        <xsl:when test="$listname='xyz'">
            <xsl:call-template name="option">
                <xsl:with-param name="value">I</xsl:with-param>
                <xsl:with-param name="label">Info</xsl:with-param>
                <xsl:with-param name="selectedValue" select="$value"/>
            </xsl:call-template>
            <xsl:call-template name="option">
                <xsl:with-param name="value">C</xsl:with-param>
                <xsl:with-param name="label">Category</xsl:with-param>
                <xsl:with-param name="selectedValue" select="$value"/>
            </xsl:call-template>
        </xsl:when>

    </xsl:choose>
 </xsl:template>

 <xsl:template name="option">
    <xsl:param name="label"/>
    <xsl:param name="value"/>
    <xsl:param name="selectedValue"/>
    <option value="{$value}">
        <xsl:if test="$value = $selectedValue">
            <xsl:attribute name="selected">selected</xsl:attribute>
        </xsl:if>
        <xsl:value-of select="$label"/>
    </option>
</xsl:template>
</xsl:stylesheet>

util.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="foo/b.xslt" />
<xsl:output method="html" />

<xsl:template name="dropdown">
    <xsl:param name="listname"/>
    <xsl:param name="value"/>
    <select>
        <xsl:call-template name="dropdowns">
            <xsl:with-param name="listname" select="$listname"/>
            <xsl:with-param name="value" select="$value"/>
        </xsl:call-template>
    </select>
</xsl:template>
</xsl:stylesheet>

render.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="util.xslt" />
<xsl:output method="html"></xsl:output>
<xsl:template match="/">
    <h1>
        <xsl:value-of select="/catalog/@name"></xsl:value-of>
    </h1>
    <xsl:call-template name="dropdown">
        <xsl:with-param name="listname">xyz</xsl:with-param>
        <xsl:with-param name="value">C</xsl:with-param>
    </xsl:call-template>
</xsl:template>
</xsl:stylesheet>

With these changes you can create drop-downs in you page which will work in both IE and chrome.

Sandeep Saroha
  • 62
  • 1
  • 10