animated gif doesn't reset when .src changed with javascript in firefox 4

VERIFIED FIXED

Status

()

Core
ImageLib
VERIFIED FIXED
7 years ago
6 years ago

People

(Reporter: tex, Assigned: azakai)

Tracking

(Blocks: 1 bug, {regression})

Trunk
regression
Points:
---
Dependency tree / graph

Firefox Tracking Flags

(blocking2.0 final+)

Details

(Whiteboard: [softblocker][fx4-fixed-bugday], URL)

Attachments

(1 attachment, 8 obsolete attachments)

(Reporter)

Description

7 years ago
User-Agent:       Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b5) Gecko/20100101 Firefox/4.0b5
Build Identifier: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b5) Gecko/20100101 Firefox/4.0b5

"Reloading" gif animation through JS doesn't reset animated gifs in Firefox 4 Betas XP/Win7 (beta 5 latest tested, still doesn't work). It works in all other tested browsers including FF3/IE/Chrome/Safari etc. 

http://tex5.com/gifanimbugff4.htm

Reproducible: Always

Steps to Reproduce:
1. change img .src through javascript to same animated gif faster than the length of the animation
Actual Results:  
animated gif keeps playing instead of being reset to the start of the animation

Expected Results:  
animated gif should reset to start of animation
confirming with SM trunk - imagelib ?
Status: UNCONFIRMED → NEW
Component: General → ImageLib
Ever confirmed: true
Product: Firefox → Core
QA Contact: general → imagelib
Version: unspecified → Trunk
Almost certainly a regression from bug 359608.

Go azakai go!
Blocks: 359608
blocking2.0: --- → ?
(Assignee)

Comment 3

7 years ago
Not sure, but some investigation appears to indicate this is a caching issue. After changing .src, it looks like lmgLoader.cpp can't find the image in the cache, which leads to having an mPendingRequest for loading it in nsHTMLImageElement, because of which ResetAnimation is not called. (The part I am still unsure of is that the cache fail is what leads to the pending request. I'm continuing to investigate.)

Is there some obvious way in which the work in bug 359608 could cause this?
Cause which?  Missing the cache?  Or not resetting the animation?

Did we also miss the cache before that change?
(Assignee)

Comment 5

7 years ago
(Ignore my previous comment, this isn't a cache issue. I was mislead by other cache misses.)

After some more investigating, what is happening is that this:

  http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsHTMLImageElement.cpp#520

is not hit. In fact there *is* a pending request. (And also oldCurrentRequest != mCurrentRequest.)

mCurrentRequest is set at

  http://mxr.mozilla.org/mozilla-central/source/modules/libpr0n/src/imgLoader.cpp#695

and this seems intentional, as imgLoader::LoadImage will always end up calling CreateNewProxyForRequest,

  http://mxr.mozilla.org/mozilla-central/source/modules/libpr0n/src/imgLoader.cpp#1680

(if the value is not set, we call the function to set that value).

This was not changed in the recent patch to bug 359608, which we suspect is the cause of the current bug. So one theory is that the animation was never reset through this code path anyhow. If that is true, it might have been set by

  http://mxr.mozilla.org/mozilla-central/source/modules/libpr0n/src/imgRequest.cpp#302

This is not currently being called because |!HaveProxyWithObserver(proxy)|. But perhaps some change in the patch to bug 359608 led to different behavior here? In other words, the theory proposed above is that the 'right' place to reset the animation was not working, but it was actually being reset (mistakenly?) here for some reason.

Regardless, the right behavior seems to be, that anytime 'src' is changed, we should check if the image is used anywhere besides the current image element, and if not, to reset the animation. The change would be at the first link earlier (the 'right' place). Do we have a way to check that? Seems we could add a public way to get the # of animation consumers, and check if it is > 1...
> it might have been set by

Yes, exactly.  If it wasn't in cache, then we won't coalesce with any existing requests, and then the new request will get its first proxy for us and call ResetAnimation.  If it was in cache, then we'll reset in the image element code.  Was that logic wrong?

You're saying in AddProxy there HaveProxyWithObserver tests true?  What's the observer?

> Regardless, the right behavior seems to be, that anytime 'src' is changed, we
> should check if the image is used anywhere besides the current image element,
> and if not, to reset the animation.

Which "the image"?  This sounds like the right behavior if "the image" can be defined...
(Assignee)

Updated

7 years ago
Assignee: nobody → azakai
(Assignee)

Comment 7

7 years ago
(In reply to comment #6)
> > it might have been set by
> 
> Yes, exactly.  If it wasn't in cache, then we won't coalesce with any existing
> requests, and then the new request will get its first proxy for us and call
> ResetAnimation.  If it was in cache, then we'll reset in the image element
> code.

Thanks, now I have an idea of what logic is intended in the code.

It looks like this is what happens on each call to change .src:

 * Look in the cache. Find it there.
 * Create a new proxy.
  * As we are adding it, the request considers resetting the animation. But there is a previous proxy with an observer, so no.
 * Add image to document.
 * nsHTMLImageElement considers resetting the animation, but there is a pending request, and oldCurrentRequest != mCurrentRequest, so no.
 * Remove image from document.
 * Remove the ***previous*** proxy from the request.

So, there is a window of time in which both proxies are present, and hence no reset for the animation. (Also there is a pending request, but I guess that part is ok?)

--

It appears this is not caused by the patch in bug 359608, both because of what is actually happening in the code, and because of the timeline (this turned up before that patch landed). Have any patches landed slightly prior to that, that modified how proxies behave?
Do we need a regression range? Is this 100% reproducible?
blocking2.0: ? → final+
(Assignee)

Comment 9

7 years ago
(In reply to comment #8)
> Do we need a regression range? Is this 100% reproducible?

This is 100% reproducible. I guess a regression range would be very useful here, since it seems we don't have a good idea of which patch caused the problem (and are not sure how to fix it).
Keywords: regression, regressionwindow-wanted

Comment 10

7 years ago
Regression window:
Works:
http://hg.mozilla.org/mozilla-central/rev/5f857be14db9
Mozilla/5.0 (Windows; Windows NT 6.1; WOW64; rv:2.0b3pre) Gecko/20100727 Minefield/4.0b3pre ID:20100728145944
Fails:
http://hg.mozilla.org/mozilla-central/rev/b8b62b351c09
Mozilla/5.0 (Windows; Windows NT 6.1; WOW64; rv:2.0b3pre) Gecko/20100728 Minefield/4.0b3pre ID:20100728152620
Pushlog:
http://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=5f857be14db9&tochange=b8b62b351c09
(Assignee)

Comment 11

7 years ago
Thanks for the regression window!

> Pushlog:
> http://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=5f857be14db9&tochange=b8b62b351c09

Joe, thoughts?
We used to reset animation in AddProxy/RemoveProxy; I don't know if we still do. I do remember removing some silliness with this in NotifyProxyListener too.
(Assignee)

Comment 13

7 years ago
Imagelib people, how do we want to move forward here?

* Is there something obvious in the guilty changesets that can be altered to fix this?
* If not, then see above about the overlapping proxies. Is that in itself a bug?
* If not, then what would be the preferred way to mark the previous proxy as no longer important enough to prevent resetting the animation, and when would we want to do that?
(Reporter)

Comment 14

7 years ago
Any updates on this issue? I would hate if this bug would end up in the final! :)
(Assignee)

Updated

7 years ago
Blocks: 588975
Alon, can you trace through what happens in 1.9.2 in the same way you did in comment 7 for mozilla-central? That should make it clear where lies the difference.
(Assignee)

Comment 16

7 years ago
(In reply to comment #15)
> Alon, can you trace through what happens in 1.9.2 in the same way you did in
> comment 7 for mozilla-central? That should make it clear where lies the
> difference.

I would guess the difference would be in the overlapping proxies, as mentioned above. To manually check older builds could be done but would be time-consuming (it requires a lot of manual pinpointing of the relevant proxy, among all the irrelevant ones) - is there no other way to get answers to the 3 questions in comment #13?
I suspect that we used to reset the animation in NotifyProxyListener called from imgCacheValidator::OnStartRequest, but we now only do it in AddProxy/RemoveProxy. Since OnStartRequest was called asynchronously, but AddProxy/RemoveProxy aren't, the HTML element would have time to remove its other proxy from the request before NotifyProxyListener was called.

The fix to this might be to remove the proxy from the HTML image element before setting the new one; Boris might have some input on whether that's a good idea.
(Assignee)

Comment 18

7 years ago
Created attachment 489837 [details] [diff] [review]
an attempt

> 
> The fix to this might be to remove the proxy from the HTML image element before
> setting the new one; Boris might have some input on whether that's a good idea.

Makes sense. Where in the code did you have in mind? I've tried to do that in nsHTMLImageElement::SetAttr, it does fix the bug, but causes bizarre test failures on tryserver that I can't reproduce locally (so, hard to figure out what's wrong). Is there an obvious reason why this patch would lead to such failures?
Attachment #489837 - Flags: feedback?(joe)
(Assignee)

Comment 19

7 years ago
Created attachment 494068 [details] [diff] [review]
another try

Well, I really don't know what I'm doing here, but after a lot of trial and error, this patch fixes the bug and only fails a single (seemingly-irrelevant) reftest,

layout/tools/reftest/poster-4.html

It looks like the image is zoomed in, and doesn't always get to the 'right' zoom. Sometimes it does in a short while, sometimes it never does. I have no idea why that is.
Attachment #489837 - Attachment is obsolete: true
Attachment #494068 - Flags: feedback?(joe)
Attachment #489837 - Flags: feedback?(joe)

Updated

7 years ago
Keywords: regressionwindow-wanted
OS: Windows 7 → Windows XP
Unintentional platform change (scroll wheel and dropdowns, great); restoring previous (Win7).
OS: Windows XP → Windows 7
The attached patch is wrong, no?  It compeletely breaks the whole current/pending request thing...
(Assignee)

Comment 22

7 years ago
(In reply to comment #21)
> The attached patch is wrong, no?  It compeletely breaks the whole
> current/pending request thing...

My thinking in the patch was that if you are changing .src, then you can cancel any and all requests, as anyhow a new one will be created for the new value of .src. Is that wrong?

Btw, is the logic for the current/pending stuff documented somewhere?
The point of the current/pending stuff is that when .src is changed we'll keep showing the old image until the new one fully loads.  That's the only reason the current/pending mess exists.

With this patch, we'll immediately drop the old image on the .src set, which will lead to flicker in the src change situation while the new image is loading and makes all the current/pending stuff dead code.
(Assignee)

Comment 24

7 years ago
Thanks, that makes sense to me now.

Another issue: I noticed a discrepancy between the bug description and the linked web page. The web page actually does this:

1. Change .src to a blank gif
2. Change .src to the original gif
3. Wait 800ms

whereas the bug description only mentions steps 2&3.

Do we want both of those scenarios to work, or just one of those? (Currently in FF4 neither does.)
(Assignee)

Comment 25

7 years ago
Created attachment 501045 [details] [diff] [review]
patch for described problem

This patch fixes the problem described in the bug (changing .src to the same image, should reset animation), but does *not* make the linked page work as expected (see previous comment).

Passes tryserver ok. If fixing just the described issue and not the linked web page is enough, then this might be the way to go.
Attachment #494068 - Attachment is obsolete: true
Attachment #501045 - Flags: review?(joe)
Attachment #494068 - Flags: feedback?(joe)
If the image isn't done loading, won't that stop the load on the src set?  Shouldn't you match the condition that used to be there, so only drop the old current request if it's complete or something?

Speaking of which, why are you removing the code that used to be there?  It's still needed, no?
(Assignee)

Comment 27

7 years ago
(In reply to comment #26)
> If the image isn't done loading, won't that stop the load on the src set?

I guess I didn't understand before then. I thought that the current request is a request that finished, and is currently being shown, while the pending request is still ongoing, and not yet being shown?

That's why I thought it would be ok to cancel the current request (but not the pending one). If that's not the case, I guess some check needs to be added if the current one is complete.
 
> 
> Speaking of which, why are you removing the code that used to be there?  It's
> still needed, no?

It currently is not called in this case, and is meant to solve the exact same problem, so I believe it shouldn't fire at any other time anyhow?
> I thought that the current request is a request that finished, and is currently
> being shown

The current request is currently being shown.  It may or may not be done loading (if you noticed, progressive rendering of large images does work!).

If a new load request needs to be made and we already have a current request, the new load will become the pending request.  The pending request becomes the current request when the pending request actually finishes.

> It currently is not called in this case

Ah, because there's an mPendingRequest?  Is that due to changes in when imagelib considers its images done or something?  Basically, this code was assuming certain sync-notification behavior on the part of imagelib; did that change?

> and is meant to solve the exact same problem

The code you're removing restarts the animation no matter whether the new image is the same URI as the old image.  That's not what your replacement code does, right?
(Assignee)

Comment 29

7 years ago
(In reply to comment #28)
> > I thought that the current request is a request that finished, and is currently
> > being shown
> 
> The current request is currently being shown.  It may or may not be done
> loading (if you noticed, progressive rendering of large images does work!).
> 
> If a new load request needs to be made and we already have a current request,
> the new load will become the pending request.  The pending request becomes the
> current request when the pending request actually finishes.
> 

Thanks, now I see.

Ok, then the next question is, what is the proper behavior in this case? When an image is still loading, if we set .src - should nothing happen, or should we restart the load (by cancelling the current one)?

> > It currently is not called in this case
> 
> Ah, because there's an mPendingRequest?  Is that due to changes in when
> imagelib considers its images done or something?  Basically, this code was
> assuming certain sync-notification behavior on the part of imagelib; did that
> change?

Yes, some imagelib patch apparently changed when requests and proxies remain in action, and that led to both of the potential places where reset could be called, to not being called (more details in previous comments).

> 
> > and is meant to solve the exact same problem
> 
> The code you're removing restarts the animation no matter whether the new image
> is the same URI as the old image.  That's not what your replacement code does,
> right?

That is correct, however, the purpose of the old code was to fix bug 210001 (according to the comment), which I believe was meant to fix exactly what this bug is - when the URI is the same. Otherwise, I'm not sure what meaning there is to resetting the animation, as if it's a new URI then it'll start from the beginning anyhow. (unless this was meant to handle some complex case of having the same image in another HTML image element...? that sounds dangerous)

So my belief is that if the code did more than that, it was a bug anyhow.
> When an image is still loading, if we set .src

See comments in nsImageLoadingContent::PrepareNextRequest

> which I believe was meant to fix exactly what this bug is - when the URI is the
> same

Uh.. not if I read that bug right.

> Otherwise, I'm not sure what meaning there is to resetting the animation

In bug 210001 the images were preloaded (see bug 210001 comment 0), so were animating while not visible, no?  See also bug 210001 comment 4.

> unless this was meant to handle some complex case of having the same image in
> another HTML image element

Yes, exactly.  Image preloads via |new Image()|.

> that sounds dangerous

Welcome to the web!

> So my belief is that if the code did more than that, it was a bug anyhow.

No, it was the necessary behavior to fix bug 210001.
(Assignee)

Comment 31

7 years ago
Comment on attachment 501045 [details] [diff] [review]
patch for described problem

(patch isn't worth reviewing)
Attachment #501045 - Flags: review?(joe)
(Assignee)

Comment 32

7 years ago
Ok, I misunderstood bug 210001 then. I'll make the necessary changes in a new version of this patch, once I know what to do there.
(Assignee)

Comment 33

7 years ago
Created attachment 501422 [details] [diff] [review]
patch

Patch will cancel the current request, if it has the same .src as the new value, and the old one was complete. This causes the image animation to restart.

Note that as mentioned above, this works for the description in the bug, but not the page linked to.
Attachment #501045 - Attachment is obsolete: true
Attachment #501422 - Flags: review?(joe)
So... it still seems to me like the right thing to do is to figure out why we now have a pending request in this situation, when we didn't use to, and fix _that_.  As it is, bug 210001 is still regressed, even with this patch, no?
(Assignee)

Comment 35

7 years ago
Well, we know which patch regressed this, comment #10 and #11. But I'm not quite sure what that patch does/is intended to do, so instead I tried to find a fix for the problem instead.

Regarding bug 210001, I wasn't aware it had regressed? I thought bug 210001 was only brought up in this bug because I (mistakenly) thought it was related to this one.
Well, that was the most immediate regression.  But there was a separate regression which causes mPendingRequest to be non-null even when loading something imagelib has cached, no?  Whenever _that_ changed, that broke the fix for bug 210001, unless I'm missing something.  The AddProxy/RemoveProxy thing just hid the breakage in some (or all?) cases.
(Assignee)

Comment 37

7 years ago
I believe mPendingRequest is not null on the linked page, because it does not just change .src to the same value, instead as mentioned above, it changes it to a blank gif, then to the old value. So the blank gif becomes the pending request. This problem doesn't happen if what is described in the bug is done - which is just change .src to the same value.
Ah, and the blank gif is not loaded yet?
But shouldn't that pending request go away when we set the image back to the old value?  nsImageLoadingContent::LoadImage calls PrepareNextRequest, which if the image we have is fully loaded will call PreparePendingRequest and will blow away any existing pending request as a result.
Whiteboard: [soft blocker]
Whiteboard: [soft blocker] → [softblocker]
(Assignee)

Comment 40

7 years ago
> But shouldn't that pending request go away when we set the image back to the
old value?

I suspect it goes away right after we check for it.

In any case, the old code relied on timing, which has apparently changed, and is in general a riskier approach. The patch explicitly checks for the case we care about and handles that, so I think it's the right way to go here.
> I suspect it goes away right after we check for it.

"right after" in what sense?

> In any case, the old code relied on timing

No, it relied on certain image operations being synchronous.  I thought they still were (though we have bugs on making them asynchronous).

> The patch explicitly checks for the case we care about

Does it?  We care about the cases in bug 210001.  Do those work even without this patch?

Seriously, I'd rather actually make this work for all src sets like its supposed to, based on some actual information about what imagelib is doing, rather than special-casing some things, cargo-culting, etc.  So what's imagelib doing here?
(Assignee)

Comment 42

7 years ago
(In reply to comment #41)
> > I suspect it goes away right after we check for it.
> 
> "right after" in what sense?
> 

Not sure, I was just making a guess at what could be happening.

> 
> > The patch explicitly checks for the case we care about
> 
> Does it?  We care about the cases in bug 210001.  Do those work even without
> this patch?

I still don't quite understand the difference between bug 210001 and this bug. It's described as "when changing the SRC attribute of an image, if it's an Animated GIF the animation should restart from the first frame" which sounds the same, but you mentioned that a crucial difference was preloading. I'm not sure how to test that - isn't there more than one way to preload images? (Bug 210001 doesn't have a testcase attached and the link is old and invalid.)
> Not sure, I was just making a guess at what could be happening.

How about just debugging what's happening instead?

> I still don't quite understand the difference between bug 210001 and this bug.

This bug is a special case of bug 210001 where the before and after URIs across the src set are equal.

> but you mentioned that a crucial difference was preloading

Preloading is only needed to guarantee that the new URI is already in the image cache.  That's guaranteed trivially in the case when the URIs are equal.

> I'm not sure how to test that

Come up with two URIs to animated images; call them url1 and url2.

  <script>
    var img1 = new Image();
    img1.src = url1;
    var img2 = new Image();
    img2.src = url2;
    window.onload = function() { document.getElementById("x").visibility = ""; }
  </script>
  <img src="url1" onclick="if (src == url1) src = url2; else src = url1;"
       style="visibility: hidden" id="x";>
(Assignee)

Comment 44

7 years ago
(In reply to comment #43)
> > Not sure, I was just making a guess at what could be happening.
> 
> How about just debugging what's happening instead?
> 

I will try, however the reason progress on that is slow despite the time I have put into it, is that I don't have a clear picture of what the code used to do, and what joe's patches changed in that behavior (comment #10). joe, can you perhaps explain more?

> > I'm not sure how to test that
> 
> Come up with two URIs to animated images; call them url1 and url2.
> [CODE]

Thanks, ok, that works fine (with no patch). Clicking switches the image, which is animated properly after the click. (Except for the initial load, at which point it doesn't animate due to bug 601723.) So the problem is only what is described in this bug, and not 210001.
So in that case the animation is reset?  Is it reset by that block in SetAttr?  If so, then how is the case in this bug different, then?  If not, then wouldn't fixing that issue fix this bug too?
(Assignee)

Comment 46

7 years ago
I guess I still didn't understand bug 210001. Is the point that

1. Image A is shown.
2. Image B is shown instead of image A.
3. Image A is shown again, *with its animation starting exactly from the beginning*.

?
> 3. Image A is shown again, *with its animation starting exactly from the
>    beginning*.

Yes.  And if you click again, B is shown, with the animation starting from the beginning, etc.  Is that not what other browsers do?
(Assignee)

Comment 48

7 years ago
You're right, I checked on Chrome and it does that.

Ok, I did some more testing and thinking here. It seems that a proper solution would be to reset the animation when no longer animating, using the AnimationConsumers stuff - which is logically a better thing to watch than the existence of proxies. AnimationConsumers should tell us exactly when something is animating, by design.

However, the reason a patch for that doesn't work, is that just doing |x=new Image();x.src = URL| is enough to get the image to animate - even though it's unseen. In other words there is a problem with the AnimationConsumers code.
(Assignee)

Comment 49

7 years ago
One solution could be for nsImageLoadingContent::TrackImage to not call document->AddImage if the image is nonvisible/not part of the DOM. How would we detect that - existence of a parent node, or the |visibility| attribute, or something else? Depending on how we do that, we would then need to add code to check if that changes, and call AddImage then.
If that's all it takes to make the |new Image()| images not trigger animation, that sounds perfect.

The simplest solution, which should work for most cases, I think, would be to only animate images that have been bound to a document.  So you'd need to change the BindToTree/UnbindFromTree implementations of the subclasses of nsImageLoadingContent.

Another option would be to only animate once we have an nsIFrame; that would involve changing the nsImageFrame code to notify its nsIImageLoadingContent when it's created and destroyed...  We have some of that going on already, fwiw (the nsIFrame is an observer of the image loading content), so maybe we can just make use of that?  E.g. don't force animation for an nsImageLoadingContent if it itself has no observers?
Comment on attachment 501422 [details] [diff] [review]
patch

Clearing the review request because this is going to change.
Attachment #501422 - Flags: review?(joe)
(Assignee)

Comment 52

7 years ago
Created attachment 503241 [details] [diff] [review]
wip patch

Ok, I think this might be the right approach.

* Animation is reset when there are no more consumers
* Fix a bug in nsDocument with not calling DecrementAnimationConsumers
* Add mTrackedRequest. Only a single request from an image element is tracked in the document. No more tracking of pending requests. Also we are careful to never track more than one image per image element.
* Take note when bound to a tree. (Note: I couldn't find a way to do this, so I added mBoundToTree temporarily. Is there a better way?) We only track images when bound, so unbound images do not animate.

(TrackImage and UntrackImage should be cleaned up a bit. In particular Untrack doesn't need any parameters, we always untrack the mTrackedImage, if there is one.)

This fixes this bug and 210001 as well. Main problem is that there is an odd crash I haven't figured out yet, nsImageLoader::DoRedraw fails on mFrame->GetType(), complaining it is a pure virtual function, which doesn't seem directly related to the patch...
Attachment #501422 - Attachment is obsolete: true
(Assignee)

Comment 53

7 years ago
Hmm, the mFrame crash was not a serious issue. However this approach has another problem I didn't realize - it will cause animations to reset when coming out of the bfcache. This is presumably a bad thing.

The optimal solution here would be to have two types of animation consumers, one that is active and rendering, and another which is only interested in the animation being kept 'alive' while paused. However this would require a lot of rewriting. Hopefully there is a better solution.
OS: Windows 7 → All
Hardware: x86 → All
(Assignee)

Comment 54

7 years ago
My current summary of this bug:

1. The cause is that operations that were synchronous are now asynchronous, due to

http://hg.mozilla.org/mozilla-central/rev/cbd7ed81d4a8

When we load the same value in .src, we get a pending *and* a current request (both with the same URI) since validation is required. A new proxy is created, and asynchronously later it becomes the current request. However, that is too late for the check for resetting the animation.

2. We may as well remove the old code for resetting the animation - it no longer works.

3. It is simple to solve the case of setting the .src to the same value. Patch is attached to this bug. I recommend going with that.

4. It is not trivial to solve the more general case appearing in 210001. It requires significant rewriting (see previous comment). No idea what to do about that.
(Assignee)

Comment 55

7 years ago
I split off the issue of not tracking unbound images to bug 625245.
> Another option would be to only animate once we have an nsIFrame

This has issues with reframing, unless we make sure that we only reset when creating a frame for the first time or something.  I still think this is the right thing to do, but we should file a separate bug on figuring out the right way to do it.

> The cause is that operations that were synchronous are now asynchronous

Ah.  So a minimal fix could be to change the old resetting code from checking for no pending request to checking whether the current request changed, right?  Would that work?  I believe it would check the same thing we used to care about...  Or is the current request not changing across the LoadImage call in this case?

> 4. It is not trivial to solve the more general case appearing in 210001

That's the case we need to fix, imo.
(Assignee)

Comment 57

6 years ago
> > The cause is that operations that were synchronous are now asynchronous
> 
> Ah.  So a minimal fix could be to change the old resetting code from checking
> for no pending request to checking whether the current request changed, right? 
> Would that work?  I believe it would check the same thing we used to care
> about...  Or is the current request not changing across the LoadImage call in
> this case?

The current request does not change. Only a pending request is added in the LoadImage call. I don't see a way to figure out whether to reset the animation from that, as a new pending request is always added, whether the image was in the cache or not (and whether the url is the same or not, that's less important though).
OK.  So yeah, in that case we should remove the reset code in SetAttr and do the reset in some other way.  The approach in "wip patch" seems promising.  We need to change other image loading content subclasses similarly, right?  We also don't need mBoundToTree (and in particular, the patch sets it incorrectly).  Just checking nsIContent::GetCurrentDoc() will give us the information we want.  This could be centralized in the image loading content track/untrack code, so that all subclasses have to do is call TrackImage in bind and UntrackImage in unbind, right?

That said, what do track/untrack affect other than animations?

If all those notifications are really async now, we should file a bug to rip out a whole bunch of code in nsImageLoadingContent that's there to deal with them being sometimes-sync...
(Assignee)

Comment 59

6 years ago
(In reply to comment #58)
> OK.  So yeah, in that case we should remove the reset code in SetAttr and do
> the reset in some other way.  The approach in "wip patch" seems promising.  We
> need to change other image loading content subclasses similarly, right?

Not sure what you mean. The animation resetting code just appears in this class (and in the proxy code, which can also be removed). What other class would need it?

>  We
> also don't need mBoundToTree (and in particular, the patch sets it
> incorrectly).  Just checking nsIContent::GetCurrentDoc() will give us the
> information we want.

Ah, good, I hoped there was a nice way to do this that I didn't know.

> This could be centralized in the image loading content
> track/untrack code, so that all subclasses have to do is call TrackImage in
> bind and UntrackImage in unbind, right?

Not sure I understand, but hopefully when I write the patch I will.

> 
> That said, what do track/untrack affect other than animations?
> 

Nothing. So that part makes sense I think.


But, I am still not clear on how we are moving forward in this bug. The approach in the patch can be made to fix this bug, however, as mentioned above it will have the side effect of resetting animations whenever they come out of the bfcache, and there is no feasible way to prevent that without a serious effort to refactor the animation-deciding code.

Do we want such a major refactoring for 4.0? It seems to me to introduce too much risk for 4.0. Alternatively, are we ok with introducing the bug of resetting animations when coming out of the bfcache? I don't like that, but perhaps it is less bad than the current bugs (and, this would be a first step towards a proper solution for post-4.0). But if the answer to both of these questions is no, we need a different solution here.
> What other class would need it?

Your new patch does the animation resetting for all image loading content subclasses, if I read it right.  What it doesn't do for them all is only starting the animation if we're in the document.  Why do we only want that for HTML images?  Seems like all image loading content should do that...

> Nothing.

Doesn't affect discarding?

> resetting animations whenever they come out of the bfcache

Imo that's fine.

> Do we want such a major refactoring for 4.0?

Again imo, no.
(Assignee)

Comment 61

6 years ago
(In reply to comment #60)
> > Nothing.
> 
> Doesn't affect discarding?

Yes, sorry, it also locks images. Is that a problem here? Do we need to lock images even before they are bound to a tree?

> 
> > resetting animations whenever they come out of the bfcache
> 
> Imo that's fine.

Ok, I will fix up this patch then.
> Do we need to lock images even before they are bound to a tree?

Maybe, yes.  Seems like that's the right thing to do for image preloads, at least...
(Assignee)

Comment 63

6 years ago
I see your point, discarding may indeed be undesired for preloads. Then this patch is going to regress that as well (and again, there is no feasible fix without a major refactoring). Do you still want it?
Hmm.  We can't sanely say "we're not interested in the animation" without unlocking?
(Assignee)

Comment 65

6 years ago
I don't see how. Currently all image elements call AddImage, preloaded or not. That calls lock when the page is shown, and unlock when hidden. After this patch, that just happens when we are bound to the tree, breaking preloaded locking. Now, we can add a call to lock in the image element itself, but then it would remain locked for all its life, which is bad. The decision should be made in the Document, like now, but I don't see a way to do this without refactoring much of the image decision code.
Hrm.  Bobby?  Joe?
Another option.  When replacing a current request by a pending one, nsImageLoadingContent could reset the animation on the new current request.  Would that do the right thing?
If desired, we could limit that to pending requests arising from the LoadImage call in nsHTMLImageElement::SetAttr.
(Assignee)

Comment 69

6 years ago
> Another option.  When replacing a current request by a pending one,
nsImageLoadingContent could reset the animation on the new current request. 
Would that do the right thing?

It would reset the animation even if the image were already shown in the page. In other words adding another appearance of the same image on a page will reset the animation (which can be noticeable in the first appearance of the image). But it would fix this bug and 210001.

Implementing this is extremely simple. If we are willing to live with the fallout, then I am all for it.
That's basically what we used to do, right?
(Assignee)

Comment 71

6 years ago
Hmm, a test shows that right now we *do* restart the animation. I don't have an immediate idea of why.
(Assignee)

Comment 72

6 years ago
Ah, ignore my last comment. I forgot to check with a non-local page. So, we do not currently restart animations when adding additional instances of the same image on a page. So doing that would be a regression.

So, our current choices, both of which fix this bug and 210001:

1. Better version of the patch here. Downsides: Animations restart when coming out of bfcache, and we no longer lock preloaded images.
2. The idea in comment #67. Downsides: Animations restart if adding another instance of the same image on a page.
You can eliminate that downside for #2 by doing what comment 68 says, right?
(Assignee)

Comment 74

6 years ago
Well, that would limit it, but not fix it. It would remain an issue if .src is changed on one image to the URL present in another. And it would make the patch more complicated, we'd need a new interface.
> It would remain an issue if .src is changed on one image to the URL present in
> another.

That used to reset the animation, per that code in SetAttr, no?

And we wouldn't need a new interface; just a protected boolean member on nsImageLoadingContent that SetAttr would set/unset around its LoadImage call.
(Assignee)

Comment 76

6 years ago
Created attachment 503916 [details] [diff] [review]
patch for comment 67

Good points, here's a patch. I agree that this is the best way to go.

Sent to tryserver.
Attachment #503916 - Flags: review?(joe)
That doesn't look right to me.

I think the right thing to do would be to set mRestartAnimationWhenDecoded or some such _before_ calling LoadImage.  Or just add an argument to LoadImage.  Then LoadImage would set another boolean member based on that argument.  This member would be cleared at the beginning of LoadImage (or in the Prepare methods; we may need separate booleans for current and pending).

As things stand in the patch, the restart from SetAttr can get applied to the load from removing and adding an image after the SetAttr call, no?  Is that what we want?

Also, this should be next to the other booleans, I'd think.
(Assignee)

Updated

6 years ago
Attachment #503916 - Flags: review?(joe)
(Assignee)

Comment 78

6 years ago
Hmm yes, it should be set before calling LoadImage, as the decoding may be sync in some cases (local files). I do not understand the need to change anything else though, can you please clarify?
You don't want the mRestartAnimationWhenDecoded value for load A to apply to load B, right?
(Assignee)

Comment 80

6 years ago
Perhaps I don't understand how loads work. When you call LoadImage, isn't a single OnStopDecode guaranteed to occur?

Is your concern that two changes to .src can happen rapidly, and mResetAnimationWhenDecoded gets reset when the first finishes, and doesn't happen for the second?
My concern is that something sets src.  This causes us to start a load.  Then the image is removed from the document and reinserted.  This causes us to cancel the load we started and start a new one, right?  Should the boolean from the src set apply to this new load?
(Assignee)

Comment 82

6 years ago
The proper thing to do sounds like adding a property to requests, whether they should lead to animation resets. But that would require interface changes.

How bad would it be if the boolean did apply to the 'wrong' request in rare cases? We'd just reset the animation unnecessarily, which we are already accepting as a regression in other situations here.
(Assignee)

Comment 83

6 years ago
Created attachment 503970 [details] [diff] [review]
patch for comment 67 v2
Attachment #503916 - Attachment is obsolete: true
> The proper thing to do sounds like adding a property to requests

You only ever have two requests here.  You can just add two boolean members for those.

> How bad would it be if the boolean did apply to the 'wrong' request in rare
> cases?

I'm not sure. Depends on how rare they are.  I'd have to think about it, but...

> We'd just reset the animation unnecessarily

And possibly very often (think every several ms), depending on what the page script is doing....
(Assignee)

Comment 85

6 years ago
(In reply to comment #84)
> > The proper thing to do sounds like adding a property to requests
> 
> You only ever have two requests here.  You can just add two boolean members for
> those.

Well, for this to work, we'd need to add code everywhere the current and pending requests can change, scattered in nsImageLoadingContent, and also special code around LoadImage. It sounds messy and tricky to get right, and as I said above, with little benefit IMO.
> scattered in nsImageLoadingContent

It's really not that scattered.

With your patch, once I do a SetAttr call, does the animation reset on every single image insert into the page?
(Assignee)

Comment 87

6 years ago
> With your patch, once I do a SetAttr call, does the animation reset on every
> single image insert into the page?

It will reset for all img elements having that url (once the load finishes), since they all share the same Image. But this is the same in all proposals, so I suspect I didn't understand your question.
Say I have a page with only one image.  I put it in the page.  Then I change its src.  Then I wait for the load to be done.  Then I remove it from the page.  Then I put it back into the page.  With your latest patch, will that last insert reset the animation?  I don't think we want to reset in that situation, and the proposal in comment 68 was all about not resetting in this case, no?
(Assignee)

Comment 89

6 years ago
The last patch will not reset when putting an image back into the page, since .src isn't changed. So the code in that patch is not even hit. Or am I missing something?
Will it get an OnStopDecode?  Or only in cases when the URI effectively changes on insertion into the document?
(Assignee)

Comment 91

6 years ago
I am not sure I follow you here. By "insertion into the document", do you mean something like |document.write| or |some_element.innerHTML +=|?

With the latest patch, those do not reset the animation, if another element is already on the page with the same url (so no regression there).
I meant removeChild followed by appendChild (so preserving the image node's identity).
(Assignee)

Comment 93

6 years ago
To make sure I understand you, here is the source I tested:

<body>
 <div id="top" align="center"></div>
 <div id="bot" align="center"></div>
  <script>
    var top = document.getElementById("top");
    var img = new Image();
    top.appendChild(img);
    img.src = '47.gif';
    setTimeout(function() {
      top.removeChild(img);
      var bot = document.getElementById("bot");
      bot.innerHTML += 'removed and appended';
      top.appendChild(img);
    }, 600);
  </script>
</body>

It works ok - the animation is not reset (since .src is not changed).
OK.  It still worries me to apply the thing to random other requests, but I guess decrementing when we reset helps.  Fine.

Will we get OnStopDecode for requests we cancel?
(Assignee)

Comment 95

6 years ago
Created attachment 504791 [details] [diff] [review]
patch for comment 67 v3

Sadly we don't get OnStopDecode for requests we cancel.

So here is a patch with two slots of requests whose animation we will reset, which is much safer (but still not 100% safe).
Attachment #503970 - Attachment is obsolete: true
Attachment #504791 - Flags: review?(bzbarsky)
(Assignee)

Updated

6 years ago
Attachment #503241 - Attachment is obsolete: true
It seems like it would be simpler and safer to just store two booleans indicating whether to reset animations for the current and pending request.  Or does that not work for some reason?
(Assignee)

Comment 97

6 years ago
It would be slightly simpler, but it would be less safe, since the patch actually identifies the requests. So there is much less risk of confusion and resetting for the wrong request (but there still is, if in theory a request has the same address as a previous one that was free'd).

(We don't access the request* that are stored - we just use them as identifiers. So there is no risk of problems in that regard.)
Hmm.  Maybe your setup in LoadImage is equivalent to what I was thinking about anyway.  I'll need to think it though, so tomorrow.
OK, so consider this sequence of events.

1)  A LoadImage call happens (req1).
2)  Immediately (before we get the image size) another LoadImage call happens
    (req2).  This cancels req1.
3)  Partway through the load from #2, a LoadImage to a non-image URI happens
    (req3).

If I read the code correctly, after #1 we'll stick req1 in slot 0.  Then after #2 we'll stick req2 in slot 1.  Then after #3 we'll stick req3 in slot 1.  Then we'll finish loading req2 but not reset the animation (because it's not in our list).  Then we'll finish loading req3 but not show it because we didn't get image data (at least that's how I recall this working).

Maybe we should do this instead?

  mRequestsToResetAnimationFor[req != mCurrentRequest] = req;

?  That would make more sense to me.  And again, using booleans that we set here and clear when preparing a request would make a lot more sense to me (esp. in the "let's not bloat every single checkbox by two words" sense).

Other than that, the new boolean should be a PRPackedBool, should be grouped with the other packed bools, and you need to brace all your loop and if bodies...

And was the imagelib code to restart the animation when a proxy is added really there only for the benefit of <img> only?
And I'm sorry for the lag; it took me a bit to find the exact steps that broke above...
(Assignee)

Comment 101

6 years ago
Created attachment 507011 [details] [diff] [review]
patch

Ok, rewrote the patch with your approach, two bools for current and pending.
Attachment #504791 - Attachment is obsolete: true
Attachment #507011 - Flags: feedback?(bzbarsky)
Attachment #504791 - Flags: review?(bzbarsky)
Comment on attachment 507011 [details] [diff] [review]
patch

The two "NeedsResetAnimation" members can be private.

Other than that, this looks great!
Attachment #507011 - Flags: review+
Attachment #507011 - Flags: feedback?(bzbarsky)
Attachment #507011 - Flags: feedback+
(Assignee)

Updated

6 years ago
Attachment #507011 - Flags: review?(joe)
Comment on attachment 507011 [details] [diff] [review]
patch

Hooray!!!

Two requests: a test, and a followup bug for us to remove the hack and change the relevant interface post-2.0.
Attachment #507011 - Flags: review?(joe) → review+
(Assignee)

Comment 104

6 years ago
Pushed: http://hg.mozilla.org/mozilla-central/rev/13651d82b77f

Regarding a test: I tried to think about how to do that, but can't think of a good way yet. The issue is that we need a sensitive way to detect whether an animation restarts. This is harder than detecting whether an animation started at all. It seems like the obvious ways to do it would be at risk for random failures. Or, we would need some new interface to access what frame is currently being animated perhaps.

Filed followup bug 629119.
Status: NEW → RESOLVED
Last Resolved: 6 years ago
Resolution: --- → FIXED
(Assignee)

Updated

6 years ago
Blocks: 629119
It's possible, but not easy, to create a test. The key is to create a JS object that implements imgIDecoderObserver, and then count the number of frames you get. I did this in modules/libpr0n/src/test/test_async_notification_animated.js.

Alon, can you file a new bug to implement that test?
(Assignee)

Updated

6 years ago
Blocks: 629411
(Assignee)

Comment 106

6 years ago
Filed bug 629411.

Comment 107

6 years ago
Verified fixed with Mozilla/5.0 (X11; Linux i686; rv:2.0b12pre) Gecko/20110204 Firefox/4.0b12pre
as opposed to Mozilla/5.0 (X11; Linux i686; rv:2.0b10pre) Gecko/20110124 Firefox/4.0b10pre
Whiteboard: [softblocker] → [softblocker][bugday0204]
Whiteboard: [softblocker][bugday0204] → [softblocker][fx4-fixed-bugday]
Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.