Window.Location.replace() doesn't work in Safari or Chrome or Opera

5,611 views
Skip to first unread message

Ian Bambury

unread,
Mar 28, 2009, 1:28:37 PM3/28/09
to Google Web Toolkit
But it's OK in IE and FF

Any ideas? Workarounds?

The line is

                Window.Location.replace(Window.Location.getHref() + "#" + newToken);

And you can see it working (or not) here http://kivivi.com/

When you first go there, I'm trying to replace http://kivivi.com/ with http://kivivi.com/#Home, but Safari and Chrome aren't overwriting the http://kivivi.com/, they are adding the http://kivivi.com/#Home

Adam T

unread,
Mar 29, 2009, 2:42:57 AM3/29/09
to Google Web Toolkit
Ian, maybe it's a Windows thing as it works fine on a my Mac with
Safari and Opera (and Firefox).

I was going to suggest trying JSNI instead, but looking at
Window.Location.replace it is just a simple call to
$wnd.location.replace(newURL) with no deferred binding to make a
difference between Firefox and others, so I can't see why it doesn't
work.

If you're using it for GWT history, you could alternatively set #Home
as the token when you initialise that subsystem.....

//Adam

On 28 Mar, 19:28, Ian Bambury <ianbamb...@gmail.com> wrote:
> But it's OK in IE and FF
> Any ideas? Workarounds?
>
> The line is
>
>                 Window.Location.replace(Window.Location.getHref() + "#" +
> newToken);
>
> And you can see it working (or not) herehttp://kivivi.com/
>
> When you first go there, I'm trying to replacehttp://kivivi.com/withhttp://kivivi.com/#Home, but Safari and Chrome aren't overwriting thehttp://kivivi.com/, they are adding thehttp://kivivi.com/#Home
>
> Ian
>
> http://examples.roughian.com

Ian Bambury

unread,
Mar 29, 2009, 7:12:26 AM3/29/09
to Google-We...@googlegroups.com
Thanks for the reply,  Adam

If you're using it for GWT history, you could alternatively set #Home
as the token when you initialise that subsystem.....

Could you explain how this would help?

Thanks,

Ian 

Adam T

unread,
Mar 29, 2009, 7:25:34 AM3/29/09
to Google Web Toolkit
Well, I'm not really sure what your doing, so it may not help - I was
just guessing if you were trying to put #Home at the end of a url you
might be trying to plug into GWT's History system.

On this page http://gwtfx.adamtacy.com/EffectsExample.html I do that
to manage tabs whose content is sucked in from original html, ie. the
first thing I want to do is to use GWT to get the user from
EffectsExample.html to EffectsExample.html#intro and so do the
following:

String token = History.getToken();
if (token.length() != 0) {
onHistoryChanged(token);
} else {
History.newItem(HIS_INTRO, true);
}

(where HIS_INTRO is the constant for "intro").

If you're not trying to hook into gwt's history then it's probably not
much use.

//Adam

Ian Bambury

unread,
Mar 29, 2009, 8:02:06 AM3/29/09
to Google-We...@googlegroups.com
Hi Adam,

Your site does what mine does, i.e you end up with two history entries, one for / and one for /#intro.

I'm trying to avoid that so that if the user clicks the back button, they go back to where they came from, not back to the page without the bookmark (which in my case will redisplay the page with the bookmark), so I use 'replace', but it doesn't work in Chrome, Safari, or Opera (on Windows, anyway).

Ian Bambury

unread,
Mar 29, 2009, 10:45:12 AM3/29/09
to Google-We...@googlegroups.com
It gets more complicated:

When you replace a location, FF, Opera, Safari and Chrome fire a historyChange event, IE doesn't

Thomas Broyer

unread,
Mar 29, 2009, 11:27:11 AM3/29/09
to Google Web Toolkit

> 2009/3/29 Adam T <adam.t...@gmail.com>
>
> > String token = History.getToken();
> > if (token.length() != 0) {
> > onHistoryChanged(token);
> > } else {
> > History.newItem(HIS_INTRO, true);
> > }

On 29 mar, 14:02, Ian Bambury <ianbamb...@gmail.com> wrote:
> Hi Adam,
> Your site does what mine does, i.e you end up with two history entries, one
> for / and one for /#intro.
>
> I'm trying to avoid that so that if the user clicks the back button, they go
> back to where they came from, not back to the page without the bookmark
> (which in my case will redisplay the page with the bookmark),

How about:
String token = History.getToken();
if (token.length() != 0) {
onHistoryChanged(token);
} else {
onHistoryChanged(HIS_INTRO, true);
}
which can also be written as:
String token = History.getToken();
if (token.length() == 0) {
token = HIS_INTRO;
}
onHistoryChanged(token);

i.e. work as if the token were HIS_INTRO, even if it doesn't show in
the URL; and use HIS_INTRO with History.newItem() to navigate to your
"home page" because newItem() doesn't work reliably with an empty
token.

For my part, I've used a class to wrap all calls to History (including
addHistoryListener) to ease things like "fire current history state
only if it is equal to X" or "navigate to X, or fire current history
state if it is already equal to X" that I use in async requests'
callbacks (fire history change only if the user hasn't navigated away,
i.e. has waited the async response / ensure we display X and/or
"refresh" display of X by firing history change). My class' getToken()
could (it doesn't now) return HIS_INTRO if History.getToken() returns
the empty string.

As for your OP, that's a know bug of WebKit <https://bugs.webkit.org/
show_bug.cgi?id=19202> . I can't tell for Opera as their bug database
isn't public (but googling a bit showed that others have faced the
same problem)

Ian Bambury

unread,
Mar 29, 2009, 12:34:21 PM3/29/09
to Google-We...@googlegroups.com
Hi Thomas,

This doesn't overwrite the existing history entry (http://mydomain.com/) it just adds another entry with a bookmark. I'm trying not to have two entries added when a user first goes to my site because the back button will take them to http://mydomain.com and that will immediately take them back to http://mydomain.com#Home (or whatever the default bookmark happens to be).

Cheers,

Ian



2009/3/29 Thomas Broyer <t.br...@gmail.com>

Thomas Broyer

unread,
Mar 29, 2009, 2:40:17 PM3/29/09
to Google Web Toolkit


On 29 mar, 18:34, Ian Bambury <ianbamb...@gmail.com> wrote:
> Hi Thomas,
>
> This doesn't overwrite the existing history entry (http://mydomain.com/) it
> just adds another entry with a bookmark. I'm trying not to have two entries
> added when a user first goes to my site because the back button will take
> them to http://mydomain.com and that will immediately take them back to http://mydomain.com#Home (or whatever the default bookmark happens to be).

I understood your use case. My code snippet was about treating
http://example.com/ and http://example.com/#Home the same; i.e. do
*not* use History.newItem("Home") when History.getToken() is the empty
string but rather *behave* as if it were "Home".

...but because you cannot use History.newItem("") reliably to return
back to your "home", you cannot just use the empty-string token and
still have to use a "Home" token; so: when you get the empty string,
behave as if it is "Home", and when navigating, use History.newItem
("Home").

Am I clearer?

Ian Bambury

unread,
Mar 29, 2009, 4:43:39 PM3/29/09
to Google-We...@googlegroups.com
2009/3/29 Thomas Broyer <t.br...@gmail.com>

 Am I clearer?

I don't know. I know I'm not :-)

OK. Where do you see this bit of code living?

Thomas Broyer

unread,
Mar 30, 2009, 6:56:48 AM3/30/09
to Google Web Toolkit


On 29 mar, 22:43, Ian Bambury <ianbamb...@gmail.com> wrote:
> 2009/3/29 Thomas Broyer <t.bro...@gmail.com>
>
>
>
> >  Am I clearer?
>
> I don't know. I know I'm not :-)
>
> OK. Where do you see this bit of code living?
>
> String token = History.getToken();
> if (token.length() == 0) {
> token = HIS_INTRO;}
>
> onHistoryChanged(token);

in your onModuleLoad (assuming your EntryPoint is your
HistoryListener, and you have a single HistoryListener).

Or you can do this:
public void onHistoryChanged(String token) {
if (token.length() == 0) {
token = "Home";
}
// do your history processing
}
and in onModuleLoad just call:
History.fireCurrentHistoryState()
(assuming you only have one HistoryListener, or you'll have to include
the same "if" code in all of them that care)

It all depends where your HistoryListener is located and if you have a
single or several listeners, but the key is: whenever you "get" the
empty string (History.getToken() == ""), do as if it were "Home".

Ian Bambury

unread,
Mar 30, 2009, 7:47:39 AM3/30/09
to Google-We...@googlegroups.com
Hi Thomas,

I don't see how that helps. If I understand, all you are saying is that if the token is blank, change it to 'Home'. But right now, if it is blank, I set up the default (the first item in the menu and any nested menus) and so I get to the default page that way.

At the end of setting up the site dependent on what the token is, I still have to write out a history token - which in your example will be 'Home' and this will add a history record. So I will end up with a blank one followed by 'Home'. So if someone clicks 'Back' they will be back at the blank one. Which will create 'Home' again and lock them into my site, which is what I am trying to avoid.

This is exactly the situation I have already. So I tried to 'replace' the token if the current one is blank. But Window.Location.replace() doesn't work in Safari or Chrome or Opera.

Hence my original post.

Or am I still missing something?

Ian

http://examples.roughian.com


2009/3/30 Thomas Broyer <t.br...@gmail.com>

Adam T

unread,
Mar 30, 2009, 3:33:40 PM3/30/09
to Google Web Toolkit
ok, now I see the issue.....but, wouldn't using
window.location.replace also "lock" the user into the site in the same
way - when the user goes back to the first page the browser would then
fire them forwards again to the location.replace url?

Maybe the key is to not have the #Home token and treat the empty
history token as if the token in the url was #Home (which I think is
what Thomas is saying?), then you avoid the "lock-in" although you
don't explicitly show the user they are "home".

//Adam

On 30 Mar, 13:47, Ian Bambury <ianbamb...@gmail.com> wrote:
> Hi Thomas,
> I don't see how that helps. If I understand, all you are saying is that if
> the token is blank, change it to 'Home'. But right now, if it is blank, I
> set up the default (the first item in the menu and any nested menus) and so
> I get to the default page that way.
> At the end of setting up the site dependent on what the token is, I still
> have to write out a history token - which in your example will be 'Home' and
> this will add a history record. So I will end up with a blank one followed
> by 'Home'. So if someone clicks 'Back' they will be back at the blank one.
> Which will create 'Home' again and lock them into my site, which is what I
> am trying to avoid.
>
> This is exactly the situation I have already. So I tried to 'replace' the
> token if the current one is blank. But Window.Location.replace() doesn't
> work in Safari or Chrome or Opera.
>
> Hence my original post.
>
> Or am I still missing something?
>
> Ian
>
> http://examples.roughian.com
>
> 2009/3/30 Thomas Broyer <t.bro...@gmail.com>

Ian Bambury

unread,
Mar 30, 2009, 5:04:23 PM3/30/09
to Google-We...@googlegroups.com
Hi Adam,


ok, now I see the issue.....but, wouldn't using
window.location.replace also "lock" the user into the site in the same
way - when the user goes back to the first page the browser would then
fire them forwards again to the location.replace url?

No because say the history is SiteA, SiteB, SiteC

And SiteC has a link to http://mysite.com/ 

Before anything happens on mysite, there is a history record added

SiteA, SiteB, SiteC, http://mysite.com/

I want to replace the http://mysite.com/ with http://mysite.com/#Home

so we have 

SiteA, SiteB, SiteC, http://mysite.com/#Home

so the back button will take you back to SiteC - and this works OK with IE and FF

But...

if the replace doesn't work (Safari, Chrome, Opera), you end up with 


and the back button takes you back to http://mysite.com/ which immediately creates http://mysite.com/#Home and you end up with the same history stack again.

Maybe the key is to not have the #Home token and treat the empty
history token as if the token in the url was #Home (which I think is
what Thomas is saying?), then you avoid the "lock-in" although you
don't explicitly show the user they are "home".

The thing is, the history calls the screen set-up routine with a token, the menu system calls it, the onModuleLoad calls it with or without a bookmark.

The 'Home' token varies depending on what the menu items are called (this is where the token comes from) so it can vary depending on the language, a Spanish speaker might have 'Inicio' instead of 'Home'. It could be 'HomePage', and it might change if you log on as an admin, or a business, or a customer, or whatever. I can't hard code it because it's a framework and the menus and therefore the tokens, don't actually exist :-)

So what I really need is 'replace' and I've only just found out that GWT can't do this consistently. So I'm stuck :-(

I've got to a point where the page with a blank token doesn't refire itself into the home page, but I think that is the best I can do.

I don't too much since at least this time it's out of the control of the Google team. </bitch>

Ian

Thomas Broyer

unread,
Mar 30, 2009, 7:20:11 PM3/30/09
to Google Web Toolkit

On 30 mar, 23:04, Ian Bambury <ianbamb...@gmail.com> wrote:
>
> The thing is, the history calls the screen set-up routine with a token, the
> menu system calls it, the onModuleLoad calls it with or without a bookmark.
>
> The 'Home' token varies depending on what the menu items are called (this is
> where the token comes from) so it can vary depending on the language, a
> Spanish speaker might have 'Inicio' instead of 'Home'. It could be
> 'HomePage', and it might change if you log on as an admin, or a business, or
> a customer, or whatever. I can't hard code it because it's a framework and
> the menus and therefore the tokens, don't actually exist :-)
>
> So what I really need is 'replace' and I've only just found out that GWT
> can't do this consistently. So I'm stuck :-(
>
> I've got to a point where the page with a blank token doesn't refire itself
> into the home page, but I think that is the best I can do.

I'm not sure I follow you here, but IIUC then yes that's your best
option.

However, as I said: I'm really not sure I understood how your code is
articulated: where are you calling History.getToken()? where are you
calling History.newItem() ? where are you willing the Location.replace
() to take place?

Is your "best option" something like at the end of your "screen set-up
routine"?
if (History.getToken().length() > 0 && !tokens.home_token().equals
(tokenToSetUp)) {
History.newItem(tokenToSetUp, false);
} // else: we're on the "blank" token and asked to display the home,
page, do nothing.

If yes, then I'd say it's OK, or you could refactor things to only
"react" to onHistoryChange and make your onModuleLoad call
History.fireCurrentHistoryState() and your menu system call
History.newItem() instead of calling the "screen set-up routine"
directly.

But a few lines of code showing how your "navigation" is articulated
would help...

Ian Bambury

unread,
Mar 30, 2009, 9:03:37 PM3/30/09
to Google-We...@googlegroups.com
There are any number of menus nested within each other. They are all instances of the same widget. Eventually, you reach a non-menu, a real 'page'.

The top level menu reacts to history. It passes this to a routine in the top menu called setMenuByToken(oldToken, newToken)

It may get passed a token like Admin/Database/Reset

The '/' is a delimiter.

It splits out the 'Admin' part and shows the Admin page which is also, in this case, a menu.

So within the toplevel (site) menu, we now have the Admin menu displayed.

The site menu calls setMenuByToken(oldToken, newToken) on the Admin menu

oldToken is now "Database/Reset", newToken is "Admin"

The Admin menu does the same as the site menu (since it is an instance of the same framework widget) - it displays the Database page (also a menu) and calls the database.setMenuByToken() routine with "Reset" and "Admin/Database"

The database menu displays the Reset page. Since this is the final page and not a menu, it writes out the history token "Admin/Database/Reset"

Now I know that it looks a bit silly, writing out what we started with, but the system also reacts to menu item clicks. If you click on menu items rather than react to history, a new token will be built up from that point on.

Getting the token for a history change is done in the top-level menu. It just passes it to the setMenuByToken routine. The menus then cascade down displaying whatever is required (or a default if there is nothing specified, or the last displayed page if there has been one)

When eventually a non-menu is displayed, a new history item is created.

If at this point, the existing history item is blank, I want to replace it with the new history item.

All the processing code is in a framework project, the actual token is based on the menu text that the using project sets up, so I have no idea what the actual token will be.

I don't know if that is any clearer :-)

Ian

Thomas Broyer

unread,
Mar 31, 2009, 5:26:39 AM3/31/09
to Google Web Toolkit
I think I have a similar pattern, but it currently only "react to
history" (i.e. it always goes all the chain from the top, "clicking a
submenu" doesn't setMenuByToken for the "subbranch", it calls
History.setItem() and the onHistoryChange takes care of the rest).
Note that I'm not satisfied by this behavior and I'd like to refactor
everything into events with History.newItem(token, false) in the end
(something looking more like what you're describing here IIUC).

> If at this point, the existing history item is blank, I want to replace it
> with the new history item.
>
> All the processing code is in a framework project, the actual token is based
> on the menu text that the using project sets up, so I have no idea what the
> actual token will be.
>
> I don't know if that is any clearer :-)

So, when you first load the page without a hash/anchor, onModuleLoad
calls setMenuByToken with values ("", "") and the menu system takes
care of showing the "default" submenu and cascade the call to the
submenu, which in turn takes care of showing the "default" submenu,
etc. until a non-menu page is reached, which calls History.newItem
(...)?
(and in this case, you'd like to actually use Location.replace to
*not* create an history entry?)
If I'm correct, then you should probably pass down a "is default" flag
to setMenuByToken and only call History.newItem if !isDefault, i.e.
change your framework a bit...
Otherwise, if I were your, I'd just live with this intermediate
history entry...

Ian Bambury

unread,
Mar 31, 2009, 7:55:04 AM3/31/09
to Google-We...@googlegroups.com
2009/3/31 Thomas Broyer <t.br...@gmail.com>


So, when you first load the page without a hash/anchor, onModuleLoad
calls setMenuByToken with values ("", "") and the menu system takes
care of showing the "default" submenu

onModuleLoad calls History.fireCurrentHistoryState();

Well actually there's a routine to create the top-level menu and that does it because the menu can be recreated if the user logs on or off, but at some point all the menus are created and then History.fireCurrentHistoryState(); is called.

This is picked up by the top-level menu here:


    @Override
    public void onValueChange(ValueChangeEvent<String> event)
    {
        setMenuByToken(event.getValue(), "");
    }

and as you can see, it calls setMenuByToken, whether it has come from the back or next button, or in this case the initial setup with or without a bookmark.


and cascade the call to the
submenu, which in turn takes care of showing the "default" submenu,

Only if the token is missing or wrong

If the menu has been shown before, it shows what was shown last time. if you go here http://examples.roughian.com/#Panels~FlowPanel and then to Home, and then back to Panels, you don't really want it reset to Intro again. Especially if there are other submenus involved.

etc. until a non-menu page is reached, which calls History.newItem
(...)?

Only if the original token was not blank.
 

(and in this case, you'd like to actually use Location.replace to
*not* create an history entry?)

Only if the original token *was* blank.
 

If I'm correct, then you should probably pass down a "is default" flag
to setMenuByToken and only call History.newItem if !isDefault, i.e.
change your framework a bit...

I don't need to. I already *know* when I need to use replace (the original token is blank), and the code already does what it should (and did before I posted) - the problem is, as originally stated in my first post, that...

Window.Location.replace() doesn't work in Safari or Chrome or Opera

and I'm looking for a fix or workaround.

Ian

Thomas Broyer

unread,
Mar 31, 2009, 12:36:47 PM3/31/09
to Google Web Toolkit


On 31 mar, 13:55, Ian Bambury <ianbamb...@gmail.com> wrote:
>
> > If I'm correct, then you should probably pass down a "is default" flag
> > to setMenuByToken and only call History.newItem if !isDefault, i.e.
> > change your framework a bit...
>
> I don't need to. I already *know* when I need to use replace (the original
> token is blank), and the code already does what it should (and did before I
> posted) - the problem is, as originally stated in my first post, that...
>
> Window.Location.replace() doesn't work in Safari or Chrome or Opera
>
> and I'm looking for a fix or workaround.

Ah, OK, then just don't do anything if the original token is blank?
i.e. http://examples.roughian.com will show the exact same thing as
http://examples.roughian.com/#Home, just without showing #Home in the
URL. Or is this a problem?

(or have I totally misunderstood how your menus work?)

(note that on examples.roughian.com, I don't know if you're using
Window.Location.replace, but it creates a new history entry for #Home
in Firefox 3.0.8; and then after navigating a bit within the site,
when going back to the "blank token" with the back button, Firefox
doesn't seem to fire history listeners --or your code just does
nothing because it is already showing the #Home--, but when going back
once more --outsideexamples.roughian.com-- and then clicking next to
return back to examples.roughian.com, your onModuleLoad calls
Location.replace or History.newItem and deletes the "forward" history)

Ian Bambury

unread,
Mar 31, 2009, 1:50:59 PM3/31/09
to Google-We...@googlegroups.com
The examples site was just an, er, example :-)

This is the 1.6 replacement which will initially be used on a new site, but will eventually get used elsewhere.


2009/3/31 Thomas Broyer <t.br...@gmail.com>

Ah, OK, then just don't do anything if the original token is blank?
i.e. http://examples.roughian.com will show the exact same thing as
http://examples.roughian.com/#Home, just without showing #Home in the
URL. Or is this a problem?

(or have I totally misunderstood how your menus work?)

Not totally, but I don't think you have thought that bit through. 

If you leave it blank when it was blank when you came in, when would you ever write a new item to history?

Thomas Broyer

unread,
Mar 31, 2009, 6:35:09 PM3/31/09
to Google Web Toolkit


On 31 mar, 19:50, Ian Bambury <ianbamb...@gmail.com> wrote:
> The examples site was just an, er, example :-)
> This is the 1.6 replacement which will initially be used on a new site, but
> will eventually get used elsewhere.
>
> 2009/3/31 Thomas Broyer <t.bro...@gmail.com>
>
>
>
> > Ah, OK, then just don't do anything if the original token is blank?
> > i.e.http://examples.roughian.comwill show the exact same thing as
> >http://examples.roughian.com/#Home, just without showing #Home in the
> > URL. Or is this a problem?
>
> > (or have I totally misunderstood how your menus work?)
>
> Not totally, but I don't think you have thought that bit through.
>
> If you leave it blank when it was blank when you came in, when would you
> ever write a new item to history?

Depends what "original token" is defined to be ;-)
if it is History.getToken(), then I'm back to my earlier proposal:
propagate a "is default" flag.

Ian Bambury

unread,
Mar 31, 2009, 7:54:29 PM3/31/09
to Google-We...@googlegroups.com
2009/3/31 Thomas Broyer <t.br...@gmail.com>



Depends what "original token" is defined to be ;-)
if it is History.getToken(), then I'm back to my earlier proposal:
propagate a "is default" flag.

I'm happy for you to define 'original token' if you can get it to work.

But I don't need an isDefault flag - I already know that without it.

How does it work with your definition of 'original token'?

Thomas Broyer

unread,
Apr 1, 2009, 6:38:47 AM4/1/09
to Google Web Toolkit


On 1 avr, 01:54, Ian Bambury <ianbamb...@gmail.com> wrote:
> 2009/3/31 Thomas Broyer <t.bro...@gmail.com>
>
>
>
> > Depends what "original token" is defined to be ;-)
> > if it is History.getToken(), then I'm back to my earlier proposal:
> > propagate a "is default" flag.
>
> I'm happy for you to define 'original token' if you can get it to work.
>
> But I don't need an isDefault flag - I already know that without it.

It would help if I knew how ;-)

But yes, thinking a bit more about it, isDefault wouldn't work. What
would work is a "history initiated" flag.

> How does it work with your definition of 'original token'?

I must have missed something in your app/framework behavior...

The problem is that History.getToken() can be where you come from
(when setMenuByToken is called by the menu) or where you're going to
(in response to history change), so you cannot use an if
(History.getToken().length()==0) to condition the call to
History.newItem(), or you'll never call newItem() (you pointed it
already: the onModuleLoad without hash/anchor propagates down, the
test evaluates to false and newItem isn't called, that's what we want,
but then clicking a menu propagates down to the page and the test
still evaluates to false).

Hence the introduction of the isHistoryInitiated flag.
1. onModuleLoad without hash/anchor: the page is shown with
isHistoryInitiated set to 'true', so in the end, History.newItem isn't
called, that's what we want (URL untouched, no history entry)
2. you click on the "panels" menu which calls setMenuByToken with
isHistoryInitiated==false, the #Panels~Summary page thus call
History.newItem
3. you navigate back to the home using your browser's back button:
onHistoryChange calls setMenuByToken with isHistoryInitiated==true,
the #Home page doesn't call newItem (it does not need to, as the
current history token already matches the displayed page)

Reply all
Reply to author
Forward
0 new messages