Skip to content

Commit

Permalink
Add Event constructor polyfill (#10401)
Browse files Browse the repository at this point in the history
* Add Event polyfill

* Swap document.Create with Event constructor

* Remove global references for window and document

* Correct comment

* Replace usages of document.createEvent

* correct linting error

* Replace document.createEvent w/ Event constructor

* Replace document.createEvent in test-amp-access

* Remove document.createEvent fallback in bind-impl.js

* Implement corrections per code review comments

- Alphabetizes imports in polyfills.js
- Corrects spacing and line lengths per linting rules
- Corrects return in Event polyfill
- Renames event type parameter
- Corrects Event initialization and removes details parameter

* Add event.js to whitelist for polyfills dep-check

* Add event polyfill test and correct closure compiler error

* Correct tests and add type argument check

* Add Event constructor to fake-dom

* Construct events using proper window

* Add test for default event configuration

* Correct spacing
  • Loading branch information
jongrim authored and William Chou committed Jul 24, 2017
1 parent 08ee7cb commit 28f709f
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 112 deletions.
1 change: 1 addition & 0 deletions build-system/dep-check-config.js
Expand Up @@ -210,6 +210,7 @@ exports.rules = [
'src/polyfills.js->src/polyfills/object-assign.js',
'src/polyfills.js->src/polyfills/promise.js',
'src/polyfills.js->src/polyfills/array-includes.js',
'src/polyfills.js->src/polyfills/event.js',
'src/service/extensions-impl.js->src/polyfills/document-contains.js',
'src/service/extensions-impl.js->src/polyfills/domtokenlist-toggle.js',
],
Expand Down
12 changes: 4 additions & 8 deletions extensions/amp-access/0.1/test/test-amp-access.js
Expand Up @@ -1030,14 +1030,10 @@ describes.fakeWin('AccessService pingback', {
service.reportViewToServer_ = sandbox.spy();
const p = service.reportWhenViewed_(/* timeToView */ 2000);
return Promise.resolve().then(() => {
let clickEvent;
if (document.createEvent) {
clickEvent = document.createEvent('MouseEvent');
clickEvent.initMouseEvent('click', true, true, window, 1);
} else {
clickEvent = document.createEventObject();
clickEvent.type = 'click';
}
const clickEvent = new MouseEvent(
'click',
{bubbles: true, cancelable: true, view: window, detail: 1}
);
document.documentElement.dispatchEvent(clickEvent);
return p;
}).then(() => {}, () => {}).then(() => {
Expand Down
8 changes: 1 addition & 7 deletions extensions/amp-bind/0.1/bind-impl.js
Expand Up @@ -957,13 +957,7 @@ export class Bind {
*/
dispatchEventForTesting_(name) {
if (getMode().test) {
let event;
if (typeof this.localWin_.Event === 'function') {
event = new Event(name, {bubbles: true, cancelable: true});
} else {
event = this.localWin_.document.createEvent('Event');
event.initEvent(name, /* bubbles */ true, /* cancelable */ true);
}
const event = new Event(name, {bubbles: true, cancelable: true});
this.localWin_.dispatchEvent(event);
}
}
Expand Down
6 changes: 4 additions & 2 deletions extensions/amp-live-list/0.1/amp-live-list.js
Expand Up @@ -866,8 +866,10 @@ export class AmpLiveList extends AMP.BaseElement {
}

sendAmpDomUpdateEvent_() {
const event = this.win.document.createEvent('Event');
event.initEvent(AmpEvents.DOM_UPDATE, true, true);
const event = new Event(
AmpEvents.DOM_UPDATE,
{bubbles: true, cancelable: true}
);
this.win.document.dispatchEvent(event);
}
}
Expand Down
52 changes: 13 additions & 39 deletions extensions/amp-sidebar/0.1/test/test-amp-sidebar.js
Expand Up @@ -292,16 +292,14 @@ describes.realWin('amp-sidebar 0.1 version', {
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('keydown', true, true);
}
const eventObj = new Event(
'keydown',
{bubbles: true, cancelable: true}
);
eventObj.keyCode = KeyCodes.ESCAPE;
eventObj.which = KeyCodes.ESCAPE;
const el = iframe.doc.documentElement;
el.dispatchEvent ?
el.dispatchEvent(eventObj) : el.fireEvent('onkeydown', eventObj);
el.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.false;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('true');
expect(sidebarElement.style.display).to.equal('none');
Expand Down Expand Up @@ -409,11 +407,7 @@ describes.realWin('amp-sidebar 0.1 version', {
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -423,9 +417,7 @@ describes.realWin('amp-sidebar 0.1 version', {
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.false;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('true');
expect(sidebarElement.style.display).to.equal('none');
Expand All @@ -452,11 +444,7 @@ describes.realWin('amp-sidebar 0.1 version', {
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -467,9 +455,7 @@ describes.realWin('amp-sidebar 0.1 version', {
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand All @@ -496,11 +482,7 @@ describes.realWin('amp-sidebar 0.1 version', {
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -512,9 +494,7 @@ describes.realWin('amp-sidebar 0.1 version', {
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand All @@ -540,14 +520,8 @@ describes.realWin('amp-sidebar 0.1 version', {
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
li.dispatchEvent ?
li.dispatchEvent(eventObj) :
li.fireEvent('onkeydown', eventObj);
const eventObj = new Event('click', {bubbles: true, cancelable: true});
li.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand Down
55 changes: 16 additions & 39 deletions extensions/amp-sidebar/1.0/test/test-amp-sidebar.js
Expand Up @@ -345,16 +345,14 @@
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('keydown', true, true);
}
const eventObj = new Event(
'keydown',
{bubbles: true, cancelable: true}
);
eventObj.keyCode = KeyCodes.ESCAPE;
eventObj.which = KeyCodes.ESCAPE;
const el = iframe.doc.documentElement;
el.dispatchEvent ?
el.dispatchEvent(eventObj) : el.fireEvent('onkeydown', eventObj);
el.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.false;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('true');
expect(sidebarElement.style.display).to.equal('none');
Expand Down Expand Up @@ -462,11 +460,7 @@
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -476,9 +470,7 @@
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.false;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('true');
expect(sidebarElement.style.display).to.equal('none');
Expand Down Expand Up @@ -506,11 +498,7 @@
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -521,9 +509,7 @@
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand All @@ -550,11 +536,7 @@
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
const eventObj = new Event('click', {bubbles: true, cancelable: true});
sandbox.stub(sidebarElement, 'getAmpDoc', () => {
return {
win: {
Expand All @@ -566,9 +548,7 @@
},
};
});
anchor.dispatchEvent ?
anchor.dispatchEvent(eventObj) :
anchor.fireEvent('onkeydown', eventObj);
anchor.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand All @@ -594,14 +574,11 @@
impl.open_();
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
const eventObj = document.createEventObject ?
document.createEventObject() : document.createEvent('Events');
if (eventObj.initEvent) {
eventObj.initEvent('click', true, true);
}
li.dispatchEvent ?
li.dispatchEvent(eventObj) :
li.fireEvent('onkeydown', eventObj);
const eventObj = new Event(
'click',
{bubbles: true, cancelable: true}
);
li.dispatchEvent(eventObj);
expect(sidebarElement.hasAttribute('open')).to.be.true;
expect(sidebarElement.getAttribute('aria-hidden')).to.equal('false');
expect(sidebarElement.style.display).to.equal('');
Expand Down
5 changes: 1 addition & 4 deletions src/custom-element.js
Expand Up @@ -1103,11 +1103,8 @@ function createBaseCustomElementClass(win) {
*/
dispatchCustomEvent(name, opt_data) {
const data = opt_data || {};
// Constructors of events need to come from the correct window. Sigh.
const win = this.ownerDocument.defaultView;
const event = win.document.createEvent('Event');
const event = new Event(name, {bubbles: true, cancelable: true});
event.data = data;
event.initEvent(name, /* bubbles */ true, /* cancelable */ true);
this.dispatchEvent(event);
}

Expand Down
15 changes: 8 additions & 7 deletions src/polyfills.js
Expand Up @@ -16,17 +16,17 @@

// Importing the document-register-element module has the side effect
// of installing the custom elements polyfill if necessary.
import installCustomElements from
'document-register-element/build/document-register-element.node';
import {
install as installDOMTokenListToggle,
} from './polyfills/domtokenlist-toggle';
import installCustomElements
from 'document-register-element/build/document-register-element.node';
import {getMode} from './mode';
import {install as installArrayIncludes} from './polyfills/array-includes';
import {install as installDocContains} from './polyfills/document-contains';
import {install as installDOMTokenListToggle}
from './polyfills/domtokenlist-toggle';
import {install as installEvent} from './polyfills/event';
import {install as installMathSign} from './polyfills/math-sign';
import {install as installObjectAssign} from './polyfills/object-assign';
import {install as installPromise} from './polyfills/promise';
import {install as installArrayIncludes} from './polyfills/array-includes';
import {getMode} from './mode';

/**
Only install in closure binary and not in babel/browserify binary, since in
Expand All @@ -44,3 +44,4 @@ installObjectAssign(self);
installPromise(self);
installDocContains(self);
installArrayIncludes(self);
installEvent(self);
57 changes: 57 additions & 0 deletions src/polyfills/event.js
@@ -0,0 +1,57 @@
/**
* Copyright 2017 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @constructor
*/
function Event(name, params) {
if (!name) {
throw new TypeError(
"Failed to construct 'Event': 1 argument required but only 0 present",
'event.js',
20
);
}
params = params || {bubbles: false, cancelable: false};
const evt = self.document.createEvent('Event');
evt.initEvent(
name,
params.bubbles,
params.cancelable
);
return evt;
}

/**
* Sets the Event polyfill if it does not exist.
* @param {!Window} win
*/
export function install(win) {
// win.Event is a function on Edge, Chrome, FF, Safari but
// is an object on IE 11.
if (typeof win.Event !== 'function') {

// supports >= IE 9. Below IE 9, window.Event.prototype is undefined
Event.prototype = win.Event.prototype;

win.Object.defineProperty(win, 'Event', {
configurable: false,
enumerable: false,
value: Event,
writable: false,
});
}
}
6 changes: 2 additions & 4 deletions test/functional/test-base-element.js
Expand Up @@ -177,11 +177,9 @@ describe('BaseElement', () => {
const timer = Services.timerFor(element.win);
target = document.createElement('div');

event1 = document.createEvent('Event');
event1.initEvent('event1', false, true);
event1 = new Event('event1', {bubbles: false, cancelable: true});

event2 = document.createEvent('Event');
event2.initEvent('event2', false, true);
event2 = new Event('event2', {bubbles: false, cancelable: true});

event1Promise = listenOncePromise(element.element, 'event1');
event1Promise = timer.timeoutPromise(TIMEOUT, event1Promise);
Expand Down

0 comments on commit 28f709f

Please sign in to comment.