Bug #4843


js:history.pushState() has no effect?

Added by Martynas Jusevicius about 3 years ago. Updated almost 3 years ago.

IXSL extensions
Start date:
Due date:
% Done:


Estimated time:
Applies to JS Branch:
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Contact person:
Additional contact persons:


Using Saxon-JS to call history.pushState() does not seem to have any effect - window.history.length does not increment and window.history.state does not change:

<xsl:variable name="js-statement" as="element()">
    <root statement="{{ 'state': 'smth' }}"/>
<xsl:variable name="json-state" select="ixsl:eval(string($js-statement/@statement))"/>
<ixsl:sequence select="js:history.pushState($json-state, '')[current-date() lt xs:date('2000-01-01')]"/>

I've also tried ixsl:call(ixsl:get(ixsl:window(), 'history'), 'pushState', [ $json-state, '' ]), with same result.

Evaluating JS does increment the window.history.length and change the window.history.state:

<xsl:variable name="js-statement" as="element()">
    <root statement="history.pushState({{ 'state': 'smth' }}, '')"/>
<xsl:sequence select="ixsl:eval(string($js-statement/@statement))[current-date() lt xs:date('2000-01-01')]"/>
Actions #2

Updated by Michael Kay about 3 years ago

Which compiler are you using? Are you sure that the call on pushState() is actually being executed, and not optimised away? The always-false predicate here looks suspicious; although its value isn't statically known, it IS statically known that it doesn't depend on the focus, so the expression could get rewritten as

<xsl:if test="current-date() lt xs:date('2000-01-01'" select="ixsl:eval(string($js-statement/@statement))[)]"/>

If you want to be sure that the ixsl:eval() is evaluated for its side-effects, but the result is discarded, then a focus-dependent predicate might be better:

<xsl:sequence select="ixsl:eval(string($js-statement/@statement))[9999999999]"/>

Actions #3

Updated by Martynas Jusevicius about 3 years ago

Using the open-source compiler.

I have double-checked and I don't think optimization is the cause. I've made a test-case: the [PUSH STATE] button does not increment the history.length but [PUSH STATE w/ JS] does (it's printed to the console log):

Source code:

Actions #4

Updated by John Lumley about 3 years ago

The reason the [PUSH STATE] doesn't increment the history state length is from a typo I fear:

<xsl:variable name="json-state" select="ixsl:eval(string($js-statement/@statement))"/>
        <ixsl:sequence select="js:history.pushState($json-state, '')[9999999999]"/>

and whilst some ixsl: instructions are defined, at least in the XX compiler, and it appears also in the XJ compiler, the namespace isn't 'reserved'. So <ixsl:sequence.../> is a perfectly legitimate result tree element, whose @select has no special processing. Looking at the SEF confirms this. (I wonder whether there is a case for at least warning in such circumstances, especially when there is a local-name alias to an xsl: instruction.)

This doesn't help the overall issue, which, seemingly involving ixsl:schedule-action with http requests, is not an area I've tackled at all.

Actions #5

Updated by Martynas Jusevicius about 3 years ago

My bad! I fixed the typo, but now I get

SaxonJS2.rt.js:892 Uncaught TypeError: Illegal invocation
Uncaught TypeError: Illegal invocation
    at l (SaxonJS2.rt.js:892)
    at (SaxonJS2.rt.js:893)
    at d (SaxonJS2.rt.js:899)
    at a (SaxonJS2.rt.js:886)
    at SaxonJS2.rt.js:642
    at SaxonJS2.rt.js:664
    at b (SaxonJS2.rt.js:375)
    at SaxonJS2.rt.js:375
    at Array.forEach (<anonymous>)
    at SaxonJS2.rt.js:375
    at SaxonJS2.rt.js:401
    at SaxonJS2.rt.js:401
    at Object.push (SaxonJS2.rt.js:404)
    at (SaxonJS2.rt.js:918)
    at SaxonJS2.rt.js:915
    at (SaxonJS2.rt.js:238)
    at d.Uc (SaxonJS2.rt.js:915)
    at HTMLDocument.d (SaxonJS2.rt.js:937)
l @ SaxonJS2.rt.js:892
call @ SaxonJS2.rt.js:893
d @ SaxonJS2.rt.js:899
a @ SaxonJS2.rt.js:886
(anonymous) @ SaxonJS2.rt.js:642
(anonymous) @ SaxonJS2.rt.js:664
b @ SaxonJS2.rt.js:375
(anonymous) @ SaxonJS2.rt.js:375
(anonymous) @ SaxonJS2.rt.js:375
(anonymous) @ SaxonJS2.rt.js:401
(anonymous) @ SaxonJS2.rt.js:401
push @ SaxonJS2.rt.js:404
sj @ SaxonJS2.rt.js:918
(anonymous) @ SaxonJS2.rt.js:915
sa @ SaxonJS2.rt.js:238
Uc @ SaxonJS2.rt.js:915
d @ SaxonJS2.rt.js:937
Actions #6

Updated by Norm Tovey-Walsh about 3 years ago

  • Status changed from New to In Progress
  • Assignee set to Norm Tovey-Walsh

There is a bug that prevents js:history.pushState() from working. In the meantime, you can work around the bug by isolating the history object and executing the call to pushState against that object explicitly:

    <xsl:variable name="json-state" select="ixsl:eval(string($js-statement/@statement))"/>

    <xsl:variable name="history" select="ixsl:get(ixsl:window(), 'history')"/>
    <xsl:sequence select="ixsl:call($history, 'pushState', [ $json-state, ''])[9999]"/>
Actions #7

Updated by Michael Kay about 3 years ago

The XJ compiler optimises the expression EXPR[NN] to () if NN is a numeric literal other than a whole number in the range 1 to 2^31. That's because position() will never be equal to any numeric value other than a whole number in that range.

Arguably it should avoid this optimisation if EXPR is known to have side-effects. However, the optimiser isn't very systematic about checking whether subexpressions have side-effects, even in cases where we have enough information to work it out.

Actions #8

Updated by Michael Kay about 3 years ago

So how DO you discard the results of an expression that you want to be evaluated for its side effects?

One way is to do

<xsl:param name="dummySubscript" select="-1"/>

<xsl:sequence select="EXPR[$dummySubscript]"/>

But even that isn't 100% reliable: with JIT compilation we sometimes compile code after the values of parameters are known, so this is just the same as using a literal subscript. However, JIT compilation doesn't happen when generating a SEF.

I introduced saxon:do for this purpose, but I'm not sure it works with Saxon-JS.

Actions #9

Updated by Community Admin about 3 years ago

  • Applies to JS Branch 2 added
  • Applies to JS Branch deleted (2.0)
Actions #10

Updated by Norm Tovey-Walsh about 3 years ago

In addition to the previously mentioned workaround, the bug has also been fixed. Basically, methods on subordinate objects (like history) weren't being handled correctly.

Actions #12

Updated by Norm Tovey-Walsh almost 3 years ago

  • Status changed from In Progress to Resolved

Added note to change history for 2.1 docs.

Actions #13

Updated by Debbie Lockett almost 3 years ago

  • Fix Committed on JS Branch 2 added
Actions #14

Updated by Debbie Lockett almost 3 years ago

  • Status changed from Resolved to Closed
  • % Done changed from 0 to 100
  • Fixed in JS Release set to Saxon-JS 2.1

Bug fix applied in the Saxon-JS 2.1 maintenance release.

Please register to edit this issue

Also available in: Atom PDF Tracking page