Odd behavior with "history.back" in Ember app using "replaceWith" running in iframe in FF
Asked Answered
K

1

9

Update: This is a bug not in Ember but in Firefox. See https://bugzilla.mozilla.org/show_bug.cgi?id=301307. I will be deleting this question once the bounty expires (unless someone comes up with a workaround).

I have an Ember app running in an iframe. Page A has a link to page B, and page B has a link to page C implemented using 'Route#replaceWith', so that B does not remain in the history stack. Page C has a link which invokes history.back(), which should return to Page A. It does return to Page A, but in Firefox only after reloading the page. There is no reason to be reloading the page, and this behavior is not observed in Chrome. This behavior also does not occur unless the app is running in an iframe.

Here's the app:

// router.js
var Router = Ember.Router.extend({location:'hash'});
Router.map(function() { 'abc'.split('').forEach(x => this.route(x));});
export default Router;

// a/route.js
export default Ember.Route.extend({actions:{b:function(){this.transitionTo('b');}}});

// a/template.js
This is A.
<a href="#" {{action 'b'}}>Goto B!</a>

// b/route.js
import Ember from 'ember';
export default Ember.Route.extend({ actions: { link: function() { this.replaceWith('c'); } } });

// b/template.js
This is B.
<a href="#" {{action 'link'}}>GOTO C (no history entry)</a>

// c/route.js
export default Ember.Route.extend({ actions: { back: function() { history.back(); } } });

// c/template.hbs
This is C
<a href="#" {{action 'back'}}>Go back</a>

If I change the replaceWith in B with a transitionTo, and the history.back() in C with a history.go(-2) everything works fine and no reload occurs. But this is not a viable solution since the browser back button must also take the user back from C to A.

I am using locationType: 'hash' and cannot change this easily. The problem does not occur with locationType: 'history'.

The only reason I can think of why Firefox might be reloading the page when trying to return to A is due to more aggressive cache management. If that is in fact the problem, then I wonder if there's some way to tell Firefox to relax and take pages further back in the history from the cache instead of going back to the server again.

FYI, this is a fresh little Ember app running with the latest versions of everything in the stack.

Any and all ideas appreciated.

Kasten answered 1/6, 2015 at 13:43 Comment(3)
i really appreciate to see question like yours where the op have made that much research about his question, i am looking at it to see if we could use a workaroundIntervale
replaceWith() function stand for transitionTo(...arguments).method('replace'); and this function is depreciated on ember, you can try to work with transitionToRoute, may be you will need to update ember, also you try to force caching on your current script ;) ... any way Ma3x wrote an alternative function ;)Intervale
@Intervale Not exactly. transitionTo is deprecated as a method on controllers, not on routes, where it's still absolutely the standard way to transition.Kasten
P
2

One way to keep the hash location type and perform a workaround for the Firefox bug is to make a custom HashLocation implementation by extending the built-in Ember HashLocation and rewriting the replaceURL function to use the history.replaceState instead of location.replace.

HashWithFFWorkaroundLocation = Ember.HashLocation.extend({
  implementation: 'hash-with-ff-workaround',

  replaceURL(path) {
    var history = this.get('history') || window.history;

    var useFixOnlyForFF = true; // Use the fix only in Firefox?
    var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;

    var useFix = useFixOnlyForFF && isFF || !useFixOnlyForFF;

    if (useFix && 'object' === typeof history && 'function' === typeof history.replaceState)
    {
        var state = { path: path };
        history.replaceState(state, null, '#' + path);
    }
    else
    {
        // Next line is the original implementation which triggers a bug in Firefox
        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=301307
        this.get('location').replace('#' + path);
    }

    this.set('lastSetUrl', path);
  }
});

Then you register the custom implementation as a new location type with an initializer

Ember.Application.initializer({
  name: 'hash-with-ff-workaround-location',

  initialize: function(container, application) {
    // register the custom implementation for locationType: hash-with-ff-workaround
    application.register('location:hash-with-ff-workaround', HashWithFFWorkaroundLocation);
  }
});

After you add the above code to your project the only line that you need to change in your existing code is

// router.js
var Router = Ember.Router.extend({ location: 'hash-with-ff-workaround' });

and you can normally call this.replaceWith('c');.

Here is the jsFiddle demo: http://jsfiddle.net/Ma3x/hs39vbqg/6/

In the jsFiddle I enabled detailed logging of all routing steps. If you check the console output you can see that the transitions are taking place normally from c back to a and forward back to c skipping b as expected when using Back/Forward buttons or history.back/forward.

Prostitution answered 10/6, 2015 at 23:38 Comment(4)
Nice answer ! i'll try to add mine ^^ it took you a long time to figure it out ?Intervale
Thanks. It took me a few hours to figure out a proper way to do this in order not to skip any Ember internals. This is the bottom of the call stack so all route and router calls are performed in the same way as with the original built-in HashLocation implementation.Prostitution
i just up voted you :) started testing and debugging 2 days ago but i did not took the time to write an answer for a workaround but yeah your solution is good :) did not test it yetIntervale
I figured out it was you. Thanks again :) By all means upload yours as well. It could give OP more ideas to work with.Prostitution

© 2022 - 2024 — McMap. All rights reserved.