Open Bug 1420239 Opened 7 years ago Updated 2 years ago

[apz] Jittery scrolling on sites using AtmosphereJS

Categories

(Core :: Panning and Zooming, defect, P3)

57 Branch
Desktop
All
defect

Tracking

()

Tracking Status
firefox-esr52 --- wontfix
firefox-esr60 --- wontfix
firefox66 --- wontfix
firefox67 --- wontfix
firefox67.0.1 --- wontfix
firefox68 --- wontfix

People

(Reporter: ian, Unassigned)

Details

(Keywords: parity-chrome, parity-edge)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:57.0) Gecko/20100101 Firefox/57.0 Build ID: 20171112125346 Steps to reproduce: Scrolling down a webpage (eg: https://atmospherejs.com/ostrio/files) Actual results: The page would jitter up and down. (note: I'm using a Logitech MX Master mouse). There's no jitter on the same sites using Chrome. Expected results: It should have been smooth.
Component: Untriaged → Layout
Product: Firefox → Core
Priority: -- → P3
This is a critical bug as it prevents the browser from being used in production - e.g. for Amazon Cloud9 IDE development, ACE, and other web-based editors. Scrolling is so jittery that it makes impossible to comprehend text.

I can reproduce the issue on Nightly68.0a1 Windows10.

Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Unspecified → All
Hardware: Unspecified → Desktop
Component: Layout → Panning and Zooming
Flags: needinfo?(botond)

The page is setting the scroll offset from JS, often overwriting dozens of pixels of scrolling by APZ and scrolling by a single pixel instead.

Here, for example, is a JS stack trace of the page changing the scroll offset from 600 to 601, while APZ has already scrolled past 650:

0 jquery.js/</</ie.fn[e]/<(e = [object HTMLDivElement], r = "scrollTop", i = 601) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":17:95594]
    this = [object Window]
1 jquery.js/</ie.access(e = [object Object], t = [function], n = "scrollTop", r = 601, i = true, o = null) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":17:44887]
    this = [object Window]
2 jquery.js/</</ie.fn[e](r = 601) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":17:95485]
    this = [object Object]
3 anonymous() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":303:848]
    this = [object Object]
4 s/<() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:11717]
    this = [object Window]
5 r.withValue(t = [object Object], n = [function]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7622]
    this = [object Object]
6 s() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:11671]
    this = [object Object]
7 _.prototype.runHooks("onAfterAction", "after") ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:2366]
    this = [object Object]
8 _.prototype._runRoute(o = [function], r = "https://atmospherejs.com/ostrio/files", n = [function]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:5188]
    this = [object Object]
9 m.prototype.dispatch(t = "https://atmospherejs.com/ostrio/files", o = [object Object], e = [function]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:8286]
    this = function(t,o,e){var r=this;this.request=t,this.response=o,i.dispatch(t.url,this,e)}
10 i(t = [object Object], o = [object Object], e = [function]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:6638]
    this = [object Object]
11 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:4048]
    this = [object Window]
12 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
13 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
14 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
15 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
16 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
17 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
18 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
19 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
20 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
21 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
22 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
23 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
24 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
25 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
26 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
27 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
28 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
29 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
30 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
31 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
32 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
33 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
34 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
35 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
36 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
37 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
38 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
39 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
40 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
41 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
42 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
43 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
44 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
45 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
46 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
47 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
48 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
49 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
50 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
51 n(r = undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
52 n.bindEnvironment/<(undefined) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
53 n() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:3694]
    this = [object Window]
54 n.bindEnvironment/<() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:7877]
    this = [object Window]
55 r(i = "/ostrio/files", o = [object Object], a = [function]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":79:4144]
    this = [object Object]
56 _.prototype.dispatch/</<(e = [object Object]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":111:3671]
    this = [object Window]
57 e.Computation.prototype._compute() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":9:2358]
    this = [object Object]
58 e.Computation.prototype._recompute() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":9:2603]
    this = [object Object]
59 e._runFlush() ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":9:3797]
    this = [object Object]
60 e(e = [object MessageEvent]) ["https://atmospherejs.com/ef36ca8fc7095239688f45fcebdf6b7071cd6ef9.js?meteor_js_resource=true":1:2472]
    this = [object Window]

It's hard to tell why the page is doing this with the JS being minified, or why Chrome is not exhibiting the same symptoms.

Flags: needinfo?(botond)

(In reply to Botond Ballo [:botond] from comment #4)

or why Chrome is not exhibiting the same symptoms.

Oh, it's a subframe that's scrolling, and as far as I know Chrome doesn't have subframe APZ on desktop.

So, it could just be a case of the site doing something that behaves poorly with async scrolling in general, and they haven't noticed the problem due to only testing with Chrome.

Indeed, scrolling is not jittery with APZ disabled. (It's still pretty slow, which could be a different problem, or also an artifact of whatever the page is trying to do with its JS code that sets the scroll offset.)

Summary: Jittery scrolling → [apz] Jittery scrolling

Adding some webcompat folks in case they might have any idea of what the site is trying to do by setting scrollTop in this way.

They're using AtmosphereJS (MeteorJS), and are adding three scroll handlers using its Scrolling module:

(function () {
var n = [
],
t,
i = !1,
o = null,
e = !1,
r = function () {
i && (window.requestAnimationFrame(r), _.each(n, function (n) {
n(t)
}))
};
Scrolling = {
addHandler: function (t) {
n.push(t)
},
start: function (n) {
if (!e) {
e = !0;
var a = this;
$(n).scroll(function (n) {
t = n.target.scrollTop,
i || window.requestAnimationFrame(r),
i = !0,
o && Meteor.clearTimeout(o),
o = Meteor.setTimeout(function () {
i = !1
}, 300)
})
}
}
}
}).call(this);

If I disable them, scrolling is fine. Of the three, disabling this one makes the problem go away:

function () {
          var l = new Date;
          c || !1 !== r.leading || (c = l);
          var f = t - (l - c);
          return e = this,
          u = arguments,
          f <= 0 ? (clearTimeout(a), a = null, c = l, i = n.apply(e, u))  : a || !1 === r.trailing || (a = setTimeout(o, f)),
          i
        }

That turns out to be a throttling function, where this is the throttled function (added on Meteor startup):

Meteor.startup(function () {
Scrolling.addHandler(_.throttle(function (t) {
e({
lastScrollTop: t
})
}, 300)),

// where e is defined here:
e = function (t) {
var o = Iron.Location.get().options.historyState,
e = _.extend({
}, o, t);
Iron.Location.go(Iron.Location.get().href, {
historyState: e,
replaceState: !0
})
}

// and the parameter t is simply an object like this:
Object { lastScrollTop: 6 }

So all of this appears to be an attempt to store the user's scroll position in their session/history, usually done to restore it when the user revisits the page later. But funny enough, calling Iron.Location.go ultimately ends up invalidating UI components to repaint them, which triggers another handler they're adding during Meteor.startup (right after the Scrolling.addHandler call I showed above, in fact):

Router.onAfterAction(function () {
var e = r;
r = this.route && this.route.getName();
var n = this.getParams().hash;
if (this._scrolledWithHash !== n) {
this._scrolled = n;
var a = function (t) {
return _.include(t, e) && _.include(t, r)
};
// snipping some uncalled code
var i = Iron.Location.get().options.historyState,
s = i && i.lastScrollTop || 0;
$(o).scrollTop(s)

Which is where they basically try to update scrollTop to whatever they passed to Iron.Location.go. So I guess that explains why APZ wouldn't play well with this code, as all of this is going to be asynchronous.

It seems that next we'll have to check why Chrome isn't affected by this similarly; perhaps it's somehow able to know it can ignore that scrollTop change somehow? Perhaps they are fortunate and Chrome is at the same scrollTop they're setting?

Summary: [apz] Jittery scrolling → [apz] Jittery scrolling on sites using AtmosphereJS
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.