[Bug] Greasemonkey applies script to page multiple times

1,316 views
Skip to first unread message

Fracture

unread,
Feb 23, 2010, 4:41:21 PM2/23/10
to greasemonkey-users
Firefox 3.6
Greasemonkey 0.8.20100211.5

After very specific browser navigation, a Greasemonkey script will be
applied to a page twice. There are a number of easy workarounds, but
I doubt that this is intended behavior. To reproduce the bug, I've
created a simple document:

http://vestitools.pbworks.com/f/gmbugtest.html

If you go there, you'll see a textarea, a link, a button to clear the
textarea, and a button that does nothing. I created a script to go
along with this page:

http://vestitools.pbworks.com/f/gmbugtest.user.js

What the script does is set a variable to true, set a variable in
unsafeWindow to true, append an element to the body of the page, and
add an event listener to the button that does nothing so that it posts
a message to the textarea when clicked.

To reproduce the bug, follow these steps:

1. Install the test script
2. Go to the test site
3. Click the "Link to another site" (last.fm)
4. AFTER you see the content of the window change, but BEFORE the
page fully loads, hit Backspace or click the back button. Generally,
I hit backspace immediately when I see the title of the page change.
You may need to try this a few times until you encounter the bug,
especially if the site loads quickly.

At this point, if you did step 4 correctly, the script will have been
applied to the page twice. You'll notice that there are two appended
elements instead of one. The script will have detected that the
unsafeWindow variable was already set, but the "local" variable does
not seem to be set, indicating that the two script instances are in
different sandboxes. If you click the do nothing button, two messages
will be posted to the textarea instead of one, indicating that there
are two event listeners.

If you uncomment the line at the top of the script, it will detect
when this problem occurs, let you know, and stop execution. If you
refresh the page when you have extra scripts running, the page will
refresh properly and you'll only get one script running.

I'm guessing that Firefox/Greasemonkey doesn't fully destroy the old
page environment until the next page is drawn, so when you go back to
the old page, it loads it from memory/cache, another load event is
fired, and Greasemonkey applies the script again. Perhaps this is a
bug in Firefox itself, and the extra load event should not be fired.

I originally encountered this with a larger script of my own. Users
were complaining about some actions handled by event listeners
occurring twice. I had no idea what the problem was, I just assumed
they had two copies of the script installed or something. I then
encountered the problem myself and discovered how to reproduce it.

Let me know if you can successfully reproduce this. I can do it on
another profile that has very few extensions installed.

Anthony Lieuallen

unread,
Feb 23, 2010, 5:31:54 PM2/23/10
to greasemon...@googlegroups.com
On 02/23/10 16:41, Fracture wrote:
> I'm guessing that Firefox/Greasemonkey doesn't fully destroy the old
> page environment until the next page is drawn, so when you go back to
> the old page, it loads it from memory/cache

This is called "fastback".

> another load event is
> fired, and Greasemonkey applies the script again. Perhaps this is a
> bug in Firefox itself, and the extra load event should not be fired.

Yes, it shouldn't fire DOMContentLoaded if it didn't load the DOM (but
rather restore the fastback cached version).

Fracture

unread,
Feb 23, 2010, 7:45:48 PM2/23/10
to greasemonkey-users
Thanks for the quick reply, Anthony.

I've filed a bug at bugzilla.mozilla.org about this issue.

https://bugzilla.mozilla.org/show_bug.cgi?id=548145

Fracture

unread,
Feb 26, 2010, 7:17:16 PM2/26/10
to greasemonkey-users
Some progress has been made on the bug. I looked at Greasemonkey's
code and saw that it (basically) uses
event.target.defaultView.location.href in the DomContentLoaded handler
to identify the URL of the page that fired the event. However, that
is not always accurate. In the case of this bad event, href points to
the page you're going back to, but event.target.documentURI points to
the page backed out of. This makes Greasemonkey apply the script
again when it shouldn't. So, it looks like the bug can be fixed by
comparing documentURI against the regular expressions instead.

The jury is still out on if the event should fire or not.

"During a page transition, there are two documents that are "live" (in
the sense of script
possibly running on them) at the same time; they will generally have
different
URIs, and will have different inner windows (script global objects)
but share a
single outer window (navigation context). The defaultView property
returns the
_outer_ window.

So a situation in which doc.documentURI !=
doc.defaultView.location.href is
normal during a page transition. If Greasemonkey is really trying to
tell the
"document location" for the document which fired DOMContentLoaded, it
needs to
be looking at the document, not the window."

Reply all
Reply to author
Forward
0 new messages