Safari 2, RichText, Browser History, and My Sanity

4 views
Skip to first unread message

Joel Webber

unread,
Aug 11, 2007, 2:02:06 PM8/11/07
to Google Web Toolkit Contributors
All,

 
I believe I said something yesterday about how I was "really close" to getting history working correctly on Safari 2. Remind me never to say anything about a browser feature until the *last* bug is fixed.
 
First a bit of 'history on history'. In GWT 1.3, Safari's history support was somewhat broken, as I'm sure you all know. It tried to use more or less the same iframe trick we use on IE, but had the following problems:
- The #hash part of the url could not be updated.
- The entire application  would sometimes refresh for no good reason (usually when 'backing into' the initial state).

As we were working on 1.4, the situation got worse. The extra breakage was that, when backing into the application, the last history token would be lost. To use KitchenSink as an example:
- Go to KitchenSink.html ,
- #Widgets
- #Panels
- <back>
- You're back in the '#' initial state.
- <back>
- You're now correctly in #Widgets.

 
I have not been able to track down the source of this change (it's certainly not in HistoryImplSafari), but I suspect that something about the startup optimizations may have triggered it. History in Safari is a very sensitive beast.

 
Now to the present day. What I would *like* to do is use the 'standard' implementation that works on Firefox. I've pretty much given up on the iframe solution -- even if I could figure out what went wrong in 1.4, the original behavior was pretty bad.

 
But the problem with using the standard implementation is that (a) you can't update the #hash without sending the browser into an 'infinite loading' tailspin, (b) you can't *read* an updated hash value anyway ( location.hash always returns the initial hash value, no matter how many times you update it. I managed to solve the first problem using a bizarre trick involving submitting a dummy form with the #hash as its action. But this is still pretty useless if you can't read the #hash value when it changes.

 
The only solution to this problem I was able to find or think of was to watch for changes to history.length (which *is* updated properly), maintain a copy of the history stack in memory, store the stack in a hidden text field so that it is preserved when you leave the app and come back, and live with the fact that hitting refresh breaks history (because the hidden text field gets cleared). This was my plan until late yesterday, when I discovered the straw that broke Safari's back:

 
In this scheme, having a dynamically-created iframe *anywhere* causes Safari to go completely nuts and refresh the page. This breaks history entirely. Worse, it sometimes decides to start refreshing *every time you hit the forward or back button*. This pretty much made me cry for several hours, because iframes are more than a little important (required for rich text at the very least), and the failure is very nasty.

 
I've attached a copy of a hacked up HistoryImplSafari that implements this (apparently broken) strategy (for this to work, you'll need to add a <textarea id='__gwt_historyText' style='width:0;height:0;border:0'> to your outer HTML for the moment). If anyone can think of any way around the iframe problem, please let me know. At this point, the behavior is so bad and inscrutable that I'm out of options.

---

At this point, I'm seriously considering creating a stub history implementation for Safari2, and having it work properly only on WebKit-tip/Safari3. The stub implementation would simply call onHistoryChanged() in response to newItem(), update the URL bar (I found a way to do this without affecting history), and parse the url for the initial #hash on startup. The end result of this change (for Safari2) would be:
- Links work (i.e. going to app.html#foo properly initializes the app).
- Apps that depend on onHistoryChanged() to work (e.g. KitchenSink) will be ok.
- Going out of an app (say, to about:blank) and backing into it will put it back in its previous state.
- Hitting back or forward won't do squat.

 
Thanks,
joel.

HistoryImplSafari.java

Reinier Zwitserloot

unread,
Aug 12, 2007, 3:09:02 PM8/12/07
to Google Web Toolkit Contributors
Damn, that sucks. Can you explain exactly when the reloads are caused?
Just randomly when going forward/backward, or the very moment you
create a dynamic iframe?

> (location.hashalways returns the initial hash value, no matter how

> HistoryImplSafari.java
> 8KDownload

Joel Webber

unread,
Aug 12, 2007, 3:15:53 PM8/12/07
to Google-Web-Tool...@googlegroups.com
Yes, suck it does. I haven't been able to fully characterize the reloads, but I can give some anecdotal descriptions. If you use the attached HistoryImplSafari with KitchenSink, it works fine as long as you avoid the 'text' tab. When you first go to this tab, everything seems ok. It then refreshes the whole page, though, the next time you select a tab.

After that, I've seen it get back into a semi-reasonable state, but I've also seen it go completely haywire, refreshing the whole page on every tab. To make matters worse, even a single refresh causes the history code to lose its in-memory stack. So even if we were able to live with the refreshes, it would be very difficult to keep track of the history stack.

joel.

On 8/12/07, Reinier Zwitserloot <rein...@gmail.com > wrote:

Damn, that sucks. Can you explain exactly when the reloads are caused?
Just randomly when going forward/backward, or the very moment you
create a dynamic iframe?

On Aug 11, 8:02 pm, "Joel Webber" <j...@google.com> wrote:
> All,
>
> I believe I said something yesterday about how I was "really close" to
> getting history working correctly on Safari 2. Remind me never to say
> anything about a browser feature until the *last* bug is fixed.
>
> First a bit of 'history on history'. In GWT 1.3 , Safari's history support
> - Apps that depend on onHistoryChanged() to work (e.g . KitchenSink) will be

> ok.
> - Going out of an app (say, to about:blank) and backing into it will put it
> back in its previous state.
> - Hitting back or forward won't do squat.
>
> Thanks,
> joel.
>
>  HistoryImplSafari.java
> 8KDownload


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Google Web Toolkit Contributors" group.
To post to this group, send email to Google-Web-Toolkit-Contributors...@googlegroups.com
To unsubscribe from this group, send email to Google-Web-Toolkit-Contributor s-unsu...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/Google-Web-Toolkit-Contributors?hl=en
-~----------~----~----~----~-- ----~----~------~--~---


 

Reinier Zwitserloot

unread,
Aug 12, 2007, 6:42:44 PM8/12/07
to Google Web Toolkit Contributors
I dumped a save-history-even-on-reloads solution in a proof of concept
vanilla JS file in the issue, which uses a cookie hack to keep state.
But, yes, that sucks even if the history stack survives reloads.

I'm gonna go dive in tonight, see if I can reproduce this, and then
see if there are any ways of generating dynamic iframes which does not
make the browser go haywire. Sigh. Where's leopard already?

On Aug 12, 9:15 pm, "Joel Webber" <j...@google.com> wrote:
> Yes, suck it does. I haven't been able to fully characterize the reloads,
> but I can give some anecdotal descriptions. If you use the attached
> HistoryImplSafari with KitchenSink, it works fine as long as you avoid the
> 'text' tab. When you first go to this tab, everything seems ok. It then
> refreshes the whole page, though, the next time you select a tab.
> After that, I've seen it get back into a semi-reasonable state, but I've
> also seen it go completely haywire, refreshing the whole page on every tab.
> To make matters worse, even a single refresh causes the history code to lose
> its in-memory stack. So even if we were able to live with the refreshes, it
> would be very difficult to keep track of the history stack.
>
> joel.
>

> On 8/12/07, Reinier Zwitserloot <reini...@gmail.com> wrote:
>
>
>
> > Damn, that sucks. Can you explain exactly when the reloads are caused?
> > Just randomly when going forward/backward, or the very moment you
> > create a dynamic iframe?
>
> > On Aug 11, 8:02 pm, "Joel Webber" <j...@google.com> wrote:
> > > All,
>
> > > I believe I said something yesterday about how I was "really close" to
> > > getting history working correctly on Safari 2. Remind me never to say
> > > anything about a browser feature until the *last* bug is fixed.
>

> > > First a bit of 'history on history'. In GWT 1.3, Safari's history

> > > - Apps that depend on onHistoryChanged() to work (e.g. KitchenSink) will

Ray Cromwell

unread,
Aug 13, 2007, 6:38:16 AM8/13/07
to Google-Web-Tool...@googlegroups.com

Joel,
  Have you seen this uber-hack? http://bloomd.home.mchsi.com/histapi/howitworks.html

  Talk about a rube-goldberg hack (using scrollTop to store state instead of .hash, EVIL)

-Ray

John Tamplin

unread,
Aug 13, 2007, 10:13:35 AM8/13/07
to Google-Web-Tool...@googlegroups.com
On 8/13/07, Ray Cromwell <cromw...@gmail.com> wrote:
  Have you seen this uber-hack? http://bloomd.home.mchsi.com/histapi/howitworks.html

  Talk about a rube-goldberg hack (using scrollTop to store state instead of .hash, EVIL)

Holy cow!  And I thought working around IE's bugs was a pain :).

--
John A. Tamplin
Software Engineer, Google

Joel Webber

unread,
Aug 13, 2007, 10:20:59 AM8/13/07
to Google-Web-Tool...@googlegroups.com
On 8/13/07, Ray Cromwell <cromw...@gmail.com> wrote:

  Have you seen this uber-hack? http://bloomd.home.mchsi.com/histapi/howitworks.html
  Talk about a rube-goldberg hack (using scrollTop to store state instead of .hash, EVIL)

David's actually interning out in Mountain View right now :)

We've been talking for a while about this problem, and he's definitely helped me through several of the nastier pieces of this puzzle (including a really nifty meta:refresh trick that allows us to update the #hash without creating a history entry). Unfortunately, I've not yet seen any solution that works correctly in the iframe case.

I've thought a bit about using his "scrollTop-state, document-in-iframe" hack, but (a) haven't confirmed whether or not it works correctly with iframes and (b) am *really* concerned that P(collateral-damage) ~= 1 with something this complex :P

Joel Webber

unread,
Aug 13, 2007, 10:25:18 AM8/13/07
to Google-Web-Tool...@googlegroups.com
On 8/12/07, Reinier Zwitserloot <rein...@gmail.com> wrote:

I dumped a save-history-even-on-reloads solution in a proof of concept
vanilla JS file in the issue, which uses a cookie hack to keep state.
But, yes, that sucks even if the history stack survives reloads.

I'm gonna go dive in tonight, see if I can reproduce this, and then
see if there are any ways of generating dynamic iframes which does not
make the browser go haywire. Sigh. Where's leopard already?

I also tried the state-in-cookie approach, but could never find a way to deal with the two-app problem. I.e., what happens when either
(a) you're running two copies of the app simultaneously, or
(b) your history stack looks like: [google.com, myapp#a, myapp#b, google.com, myapp#c, google.com]

In these cases, it is difficult (impossible?) to keep the two copies of the app from stepping on each-others' toes. To store history state in a cookie would require you to have a unique identifier for each 'instance' of the app. I'm not sure that's possible to do reliably (David Bloom showed me a trick for doing this once, but it couldn't survive refresh).

 

Reinier Zwitserloot

unread,
Aug 13, 2007, 12:10:01 PM8/13/07
to Google Web Toolkit Contributors
I think there are ways of making cookie-stored history work in
scenario 2 (google->myapp#a->myapp#b->google->myapp#c->google), but
running the same app in two different browser windows/tabs at the same
time is a disaster and conflicts with scenario 2 (in that I might
solve that by generating a unique ID when the nocache page gets
loaded, but this also (potentially) occurs when backing into the app
again.

Last night I toyed around but didn't get any stable result. :/

Looks like the dummy implementation that correctly sets # on the URL
and correctly loads # from URL, but doesn't react at all to back or
forward buttons unless you have Safari3, is the lesser evil. The
random nature of when safari all of a sudden decides it's time to
reload the entire page for no reason is -really- frustrating. I
remember at least 3 different times when I thought for a moment that I
found a workaround, when I didn't. I can't imagine how often you've
run into that, Joel - your own "Mission Accomplished" flub is a
testament to how annoying this is. My hat goes off to your
persistence.

With safari3 on the way it's too tempting to just toss in the towel.

On Aug 13, 4:25 pm, "Joel Webber" <j...@google.com> wrote:

Reply all
Reply to author
Forward
0 new messages