Closed Bug 1547581 Opened 5 years ago Closed 5 years ago

Wrong 'this' sent to event handlers rewritten from on* event attributes to addEventListener()

Categories

(Thunderbird :: Preferences, defect)

defect
Not set
critical

Tracking

(thunderbird68 fixed, thunderbird69 fixed)

RESOLVED FIXED
Thunderbird 69.0
Tracking Status
thunderbird68 --- fixed
thunderbird69 --- fixed

People

(Reporter: aceman, Assigned: aceman)

References

(Regression)

Details

(Keywords: regression)

Attachments

(1 file, 2 obsolete files)

In the Account Settings > Synchronisation and Storage > Advanced dialog when you click Cancel, you get:
TypeError: this._rollbackMap is undefined msgSelectOfflineFolders.js:100:33

Similarly, in Tools->Options->Composition->General->Keywords, when clicking OK you get:
TypeError: this.keywordListBox is undefined in attachmentReminder.js:70:21

It it seems the handler functions that get called on these events expect a different 'this' as being passed to them from addEventListener().

Previously, 'this' was the object in which these functions (like onOK, on Cancel) were defined (e.g. gSelectOffline). Now it is seems to be 'document' or the element on which addEventListener is called.

There are some docs on this in https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener how to pass proper 'this' (e.g. using bind()), which would probably be better and more readable than rewriting the functions to name the containing global object directly.

It seems all addEventListener calls added in https://hg.mozilla.org/comm-central/rev/ea892780482c need to be audited for this problem and also maybe patches to other attributes (aside from ondialogaccept and ondialogcancel).

Keywords: regression

Maybe we should just add the .bind(object) unconditionally to every addEventListener() where we call object.method() to future proof the methods, as even if now the method() does not use this, it may start to use it in the future, as it is placed in the 'object' and any other method() in the object uses it so coders will naturally use it and assume it will work. We should prevent those future errors going unnoticed.

Attached patch 1552851.patch (obsolete) — Splinter Review

Add the .bind() calls where we call Object.function() from addEventListener.

Try run:
https://treeherder.mozilla.org/#/jobs?repo=try-comm-central&revision=da8e40a0819ddfb05572b58c7523425dec174fdb

Assignee: nobody → acelists
Status: NEW → ASSIGNED
Attachment #9066069 - Flags: review?(jorgk)
Attachment #9066069 - Flags: feedback?(geoff)
Comment on attachment 9066069 [details] [diff] [review]
1552851.patch

Review of attachment 9066069 [details] [diff] [review]:
-----------------------------------------------------------------

I think this is in the wrong bug.
Attachment #9066069 - Attachment is obsolete: true
Attachment #9066069 - Flags: review?(jorgk)
Attachment #9066069 - Flags: feedback?(geoff)
Attached patch 1547581.patch (obsolete) — Splinter Review

Right, wrong patch, sorry.

Attachment #9066162 - Flags: review?(mkmelin+mozilla)
Attachment #9066162 - Flags: review?(jorgk)
Attachment #9066162 - Flags: feedback?(geoff)

Instead of gFoo.bar.bind(gFoo), you could use () => gFoo.bar(), which I think would be cleaner, since some of these variable names are quite long.

How will that know to bind the right gFoo? Also, it may be shorter but I wonder if everybody would understand the point of it and not remove the '() =>' as redundant.

Comment on attachment 9066162 [details] [diff] [review]
1547581.patch

I'll leave the review to the JS geeks.
Attachment #9066162 - Flags: review?(jorgk)
Attachment #9066162 - Flags: review?(geoff)
Attachment #9066162 - Flags: feedback?(geoff)
Attached patch 1547581.patch v2Splinter Review

So if you like this one better. It seems to work but I do not know why.
I know '=>' functions have some differences in handling 'this' but how can it guess the right object?

Attachment #9066553 - Flags: review?(geoff)

I still don't understand the question. Where is there more than one object with the same name? There is only one gWhatever in each of these situations.

Using the arrow function makes no difference either. It's no different from function() { gFoo.bar(); } except that it's shorter, because there's no reference to this inside the function.

Comment on attachment 9066553 [details] [diff] [review]
1547581.patch v2

I like this one better.
Attachment #9066553 - Flags: review?(geoff) → review+
Attachment #9066162 - Flags: review?(mkmelin+mozilla)
Attachment #9066162 - Flags: review?(geoff)

(In reply to Geoff Lankow (:darktrojan) from comment #9)

I still don't understand the question. Where is there more than one object with the same name? There is only one gWhatever in each of these situations.

No, I do't talk about there being more than one gWhatever.

Using the arrow function makes no difference either. It's no different from function() { gFoo.bar(); } except that it's shorter, because there's no reference to this inside the function.

Inside of gFoo.bar() there is a call to 'this' and we want 'this' to be gFoo. With a plain 'document.addEventListener("event", gFoo.bar())', 'this' ended up being 'document' or something, not gFoo. Can you explain how thanks to '() =>' or 'function() { }' suddenly 'this' becomes 'gFoo' inside of gFoo.bar() ?

Please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

In essence: for arrow functions, the this value of the enclosing lexical scope is used.

Pushed by mozilla@jorgk.com:
https://hg.mozilla.org/comm-central/rev/423c3668781b
use arrow functions when calling methods from addEventListener to use 'this' from the enclosing lexical scope. r=darktrojan

Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED

I hope you're happy with the adjusted commit message.

Target Milestone: --- → Thunderbird 69.0
Attachment #9066162 - Attachment is obsolete: true
Comment on attachment 9066553 [details] [diff] [review]
1547581.patch v2

More beta material, right?
Attachment #9066553 - Flags: approval-comm-beta+

(In reply to Magnus Melin [:mkmelin] from comment #12)

Please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

In essence: for arrow functions, the this value of the enclosing lexical scope is used.

I have read that page before, but I do not see how it applies here. How is "enclosing lexical scope" determined?

(In reply to Jorg K (GMT+2) from comment #15)

More beta material, right?

Yes!

Regressions: 1558775
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: