devote / HTML5-History-API

HTML5 History API expansion for browsers not supporting pushState, replaceState
http://spb-piksel.ru
MIT License
1.02k stars 182 forks source link

bind addEventListener, removeEventListener, and dispatchEvent for browsers that support EventTarget #106

Closed cvazac closed 6 years ago

cvazac commented 6 years ago

In supporting browsers (Chrome, Safari, Edge, Firefox), third-party script loaded in an IFRAME that wraps EventTarget.prototype.addEventListener will lose track of the context of this, and will incorrectly bind the handler to its own IFRAME (instead of top).

Reproducible Case

To reproduce the issue, go here with the console open. Without the fix, you will only see only 2 handlers fire:

first load handler fired
second load handler fired

To see the fix in action, go here. You will see:

first load handler fired
second load handler fired
third load handler fired

Why this happens

in history.js:

window.addEventListener = addEventListener

in an IFRAME:

top.EventTarget.prototype.addEventListener = (function(_addEventListener){
  return function(){
    return _addEventListener.apply(this, arguments)
  }
})(top.EventTarget.prototype.addEventListener)

developer code in top:

window.addEventListener('load', callback)

Because history.js basically unbinds the window.addEventListener method, this passed to the _addEventListener.apply call will be the inner IFRAME (should be === top), and the load listener will be bound to the wrong window.