Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 7205295

Browse files
committed
feat: patch onclick in Chrome and Safari
- adds semi-exhaustive list of events - adds check for patching via property descriptor - adds fallback for patching browser events on capture
1 parent 46a6fbc commit 7205295

File tree

1 file changed

+82
-27
lines changed

1 file changed

+82
-27
lines changed

Diff for: zone.js

+82-27
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,11 @@ Zone.patchProperty = function (obj, prop) {
177177
Object.defineProperty(obj, prop, desc);
178178
};
179179

180-
Zone.patchProperties = function (obj) {
181-
Object.keys(obj).
180+
Zone.patchProperties = function (obj, properties) {
181+
(properties || Object.keys(obj).
182182
filter(function (propertyName) {
183183
return propertyName.substr(0,2) === 'on';
184-
}).
184+
})).
185185
forEach(function (eventName) {
186186
Zone.patchProperty(obj, eventName);
187187
});
@@ -211,36 +211,46 @@ Zone.patch = function patch () {
211211
Zone.patchableFn(window, ['alert', 'prompt']);
212212

213213
// patched properties depend on addEventListener, so this needs to come first
214-
if (EventTarget) {
215-
Zone.patchEventTargetMethods(EventTarget.prototype);
214+
if (window.EventTarget) {
215+
Zone.patchEventTargetMethods(window.EventTarget.prototype);
216216

217217
// Note: EventTarget is not available in all browsers,
218218
// if it's not available, we instead patch the APIs in the IDL that inherit from EventTarget
219219
} else {
220-
[ ApplicationCache.prototype,
221-
EventSource.prototype,
222-
FileReader.prototype,
223-
InputMethodContext.prototype,
224-
MediaController.prototype,
225-
MessagePort.prototype,
226-
Node.prototype,
227-
Performance.prototype,
228-
SVGElementInstance.prototype,
229-
SharedWorker.prototype,
230-
TextTrack.prototype,
231-
TextTrackCue.prototype,
232-
TextTrackList.prototype,
233-
WebKitNamedFlow.prototype,
234-
Window.prototype,
235-
Worker.prototype,
236-
WorkerGlobalScope.prototype,
237-
XMLHttpRequestEventTarget.prototype,
238-
XMLHttpRequestUpload.prototype
239-
].forEach(patchEventTargetMethods);
220+
[ 'ApplicationCache',
221+
'EventSource',
222+
'FileReader',
223+
'InputMethodContext',
224+
'MediaController',
225+
'MessagePort',
226+
'Node',
227+
'Performance',
228+
'SVGElementInstance',
229+
'SharedWorker',
230+
'TextTrack',
231+
'TextTrackCue',
232+
'TextTrackList',
233+
'WebKitNamedFlow',
234+
'Window',
235+
'Worker',
236+
'WorkerGlobalScope',
237+
'XMLHttpRequestEventTarget',
238+
'XMLHttpRequestUpload'
239+
].
240+
filter(function (thing) {
241+
return window[thing];
242+
}).
243+
map(function (thing) {
244+
return window[thing].prototype;
245+
}).
246+
forEach(Zone.patchEventTargetMethods);
240247
}
241248

242-
Zone.patchProperties(HTMLElement.prototype);
243-
Zone.patchProperties(XMLHttpRequest.prototype);
249+
if (Zone.canPatchViaPropertyDescriptor()) {
250+
Zone.patchViaPropertyDescriptor();
251+
} else {
252+
Zone.patchViaCapturingAllTheEvents();
253+
}
244254

245255
// patch promises
246256
if (window.Promise) {
@@ -251,6 +261,51 @@ Zone.patch = function patch () {
251261
}
252262
};
253263

264+
//
265+
Zone.canPatchViaPropertyDescriptor = function () {
266+
Object.defineProperty(HTMLElement.prototype, 'onclick', {
267+
get: function () {
268+
return true;
269+
}
270+
});
271+
var elt = document.createElement('div');
272+
var result = !!elt.onclick;
273+
Object.defineProperty(HTMLElement.prototype, 'onclick', {});
274+
return result;
275+
};
276+
277+
// for browsers that we can patch the descriptor:
278+
// - eventually Chrome once this bug gets resolved
279+
// - Firefox
280+
Zone.patchViaPropertyDescriptor = function () {
281+
Zone.patchProperties(HTMLElement.prototype, Zone.eventNames.map(function (property) {
282+
return 'on' + property;
283+
}));
284+
Zone.patchProperties(XMLHttpRequest.prototype);
285+
};
286+
287+
// Whenever any event fires, we check the event target and all parents
288+
// for `onwhatever` properties and replace them with zone-bound functions
289+
// - Chrome (for now)
290+
Zone.patchViaCapturingAllTheEvents = function () {
291+
Zone.eventNames.forEach(function (property) {
292+
var onproperty = 'on' + property;
293+
document.addEventListener(property, function (event) {
294+
var elt = event.target, bound;
295+
while (elt) {
296+
if (elt[onproperty] && !elt[onproperty]._unbound) {
297+
bound = zone.bind(elt[onproperty]);
298+
bound._unbound = elt[onproperty];
299+
elt[onproperty] = bound;
300+
}
301+
elt = elt.parentElement;
302+
}
303+
}, true);
304+
});
305+
};
306+
307+
Zone.eventNames = 'copy cut paste abort blur focus canplay canplaythrough change click contextmenu dblclick drag dragend dragenter dragleave dragover dragstart drop durationchange emptied ended input invalid keydown keypress keyup load loadeddata loadedmetadata loadstart mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup pause play playing progress ratechange reset scroll seeked seeking select show stalled submit suspend timeupdate volumechange waiting mozfullscreenchange mozfullscreenerror mozpointerlockchange mozpointerlockerror error'.split(' ');
308+
254309
Zone.init = function init () {
255310
window.zone = new Zone();
256311
Zone.patch();

0 commit comments

Comments
 (0)