Closed Bug 1372649 Opened 7 years ago Closed 7 years ago

With Webextensions, several javascript window functions are losing the window interface context


(WebExtensions :: General, defect)

52 Branch
Not set


(Not tracked)



(Reporter: savy.mathieu, Unassigned)



(1 file)

36.04 KB, application/zip
Attached file
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36

Steps to reproduce:

I am porting my legacy extension to web-extension. Everything is working fine in the legacy extension, but I ran into problems with Backbone.js and `javascript-detect-element-resize`.

After some digging I found out that calling the following functions throw an error:
window.addEventListener (noticed in backbone)
window.requestAnimationFrame (noticed in detect-element-resize library)
window.cancelAnimationFrame (noticed in detect-element-resize library)

(there is probably more)

I don't have a simple way to reproduce the problem for requestAnimationFrame and cancelAnimationFrame but please find attached a way to reproduce the problem with Backbone and addEventListener.

The attached addon simply calls: Backbone.history.start({pushState: true}); in a content script when loading

If you look at the remote debugger console, you will see the following error:
> throw TypeError: 'addEventListener' called on an object that does not implement interface EventTarget.

In backbone.js, look at the lines 1713-1727 to see where `addEventListener` is called.

Calling window.addEventListener = addEventListener.bind(window); before initializing the router fixes the problem.

To make my addon works with both backbone and detect-element-resize library, I have to initialize my library with:
window.requestAnimationFrame = window.requestAnimationFrame.bind(window);
window.cancelAnimationFrame = window.cancelAnimationFrame.bind(window);
window.addEventListener = window.addEventListener.bind(window);

Actual results:

The problem only occurs with Firefox with Web-extensions. The same code in a Chrome content script works perfectly, if web-extensions are supposed to be as close as Chrome as possible then this is a bug.

It also worked in legacy Firefox addons.

Expected results:

I shouldn't have to manually bind those functions.
Component: Untriaged → WebExtensions: General
Product: Firefox → Toolkit
Flags: needinfo?(lgreco)
Whiteboard: investigating
I took a look at this and it seems to be another of the content scripts "chrome incompatibilities" that are going to be fixed by Bug 1208775
("this !== window within content_scripts").

The issue can be easily reproduce by running the attached extension, and it can also be reproduced with the following minimal content script:

const testUnbindedAddEventListener = window.addEventListener;

testUnbindedAddEventListener("click", (...args) => console.log("CS Click listener", args));

The fix suggested in the content script that the attached test extension contains seems to be the best one available until Bug 1208775 has been fixed:

window.addEventListener = window.addEventListener.bind(window);
... // similar redefinition for any other of the window DOM methods that are going to be used "unbinded" 

The reason why the above workaround fixes the issue is simple:

- when we unbind `addEventListener`:
  - in a webpage, `window` is what will be used as the "fallback" for `this` in the "unbinded" method call
  - in a WebExtensions Content Script, the content script sandbox is what will be used as the "fallback" for `this` in the "unbinded" method call

By redefining that methods and explicitly binding `this` to the `window` reference, we are manually workarounding the issue that Bug 1208775 should fix for this and other issues related to this behavior.
Closed: 7 years ago
Flags: needinfo?(lgreco)
Resolution: --- → DUPLICATE
Whiteboard: investigating
Product: Toolkit → WebExtensions
You need to log in before you can comment on or make changes to this bug.