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

Commit a733688

Browse files
JiaLiPassionmhevery
authored andcommitted
feat(onProperty): fix #875, can disable patch specified onProperties (#877)
* feat(onProperty): fix #875, can disable patch specified onProperties * update module.md * export interface * add prototype init * add test fake polyfill to karma.dist config * remove console.log
1 parent f27ff14 commit a733688

File tree

5 files changed

+137
-24
lines changed

5 files changed

+137
-24
lines changed

MODULE.md

+21
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,27 @@ Below is the full list of current support modules.
5656
|handleUnhandledPromiseRejection|NodeJS handle unhandledPromiseRejection from ZoneAwarePromise|__Zone_disable_handleUnhandledPromiseRejection = true|
5757
|crypto|NodeJS patch crypto function as macroTask|__Zone_disable_crypto = true|
5858

59+
- on_property
60+
61+
you can also disable specified on_property by setting `__Zone_ignore_on_properties`, for example,
62+
if you want to disable `window.onmessage` and `HTMLElement.prototype.onclick` from zone.js patching,
63+
you can do like this.
64+
65+
```
66+
<script>
67+
__Zone_ignore_on_properties = [
68+
{
69+
target: window,
70+
ignoreProperties: ['message']
71+
}, {
72+
target: HTMLElement.prototype,
73+
ignoreProperties: ['click']
74+
}
75+
];
76+
</script>
77+
<script src="../dist/zone.js"></script>
78+
```
79+
5980
- Angular(2+)
6081

6182
Angular use zone.js to manage async operations and decide when to perform change detection, so in Angular,

karma-dist.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
module.exports = function (config) {
1010
require('./karma-base.conf.js')(config);
1111
config.files.push('build/test/wtf_mock.js');
12+
config.files.push('build/test/test_fake_polyfill.js');
1213
config.files.push('build/test/custom_error.js');
1314
config.files.push('dist/zone.js');
1415
config.files.push('dist/async-test.js');

lib/browser/property-descriptor.ts

+54-21
Original file line numberDiff line numberDiff line change
@@ -230,53 +230,86 @@ export const eventNames = globalEventHandlersEventNames.concat(
230230
webglEventNames, formEventNames, detailEventNames, documentEventNames, windowEventNames,
231231
htmlElementEventNames, ieElementEventNames);
232232

233+
export interface IgnoreProperty {
234+
target: any;
235+
ignoreProperties: string[];
236+
}
237+
238+
function filterProperties(
239+
target: any, onProperties: string[], ignoreProperties: IgnoreProperty[]): string[] {
240+
if (!ignoreProperties) {
241+
return onProperties;
242+
}
243+
244+
const tip: IgnoreProperty[] = ignoreProperties.filter(ip => ip.target === target);
245+
if (!tip || tip.length === 0) {
246+
return onProperties;
247+
}
248+
249+
const targetIgnoreProperties: string[] = tip[0].ignoreProperties;
250+
return onProperties.filter(op => targetIgnoreProperties.indexOf(op) === -1);
251+
}
252+
253+
export function patchFilteredProperties(
254+
target: any, onProperties: string[], ignoreProperties: IgnoreProperty[], prototype?: any) {
255+
const filteredProperties: string[] = filterProperties(target, onProperties, ignoreProperties);
256+
patchOnProperties(target, filteredProperties, prototype);
257+
}
258+
233259
export function propertyDescriptorPatch(api: _ZonePrivate, _global: any) {
234260
if (isNode && !isMix) {
235261
return;
236262
}
237263

238264
const supportsWebSocket = typeof WebSocket !== 'undefined';
239265
if (canPatchViaPropertyDescriptor()) {
266+
const ignoreProperties: IgnoreProperty[] = _global.__Zone_ignore_on_properties;
240267
// for browsers that we can patch the descriptor: Chrome & Firefox
241268
if (isBrowser) {
242269
// in IE/Edge, onProp not exist in window object, but in WindowPrototype
243270
// so we need to pass WindowPrototype to check onProp exist or not
244-
patchOnProperties(window, eventNames.concat(['messageerror']), Object.getPrototypeOf(window));
245-
patchOnProperties(Document.prototype, eventNames);
271+
patchFilteredProperties(
272+
window, eventNames.concat(['messageerror']), ignoreProperties,
273+
Object.getPrototypeOf(window));
274+
patchFilteredProperties(Document.prototype, eventNames, ignoreProperties);
246275

247276
if (typeof(<any>window)['SVGElement'] !== 'undefined') {
248-
patchOnProperties((<any>window)['SVGElement'].prototype, eventNames);
277+
patchFilteredProperties(
278+
(<any>window)['SVGElement'].prototype, eventNames, ignoreProperties);
249279
}
250-
patchOnProperties(Element.prototype, eventNames);
251-
patchOnProperties(HTMLElement.prototype, eventNames);
252-
patchOnProperties(HTMLMediaElement.prototype, mediaElementEventNames);
253-
patchOnProperties(HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames));
254-
patchOnProperties(HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames));
255-
patchOnProperties(HTMLFrameElement.prototype, frameEventNames);
256-
patchOnProperties(HTMLIFrameElement.prototype, frameEventNames);
280+
patchFilteredProperties(Element.prototype, eventNames, ignoreProperties);
281+
patchFilteredProperties(HTMLElement.prototype, eventNames, ignoreProperties);
282+
patchFilteredProperties(HTMLMediaElement.prototype, mediaElementEventNames, ignoreProperties);
283+
patchFilteredProperties(
284+
HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames),
285+
ignoreProperties);
286+
patchFilteredProperties(
287+
HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames), ignoreProperties);
288+
patchFilteredProperties(HTMLFrameElement.prototype, frameEventNames, ignoreProperties);
289+
patchFilteredProperties(HTMLIFrameElement.prototype, frameEventNames, ignoreProperties);
257290

258291
const HTMLMarqueeElement = (window as any)['HTMLMarqueeElement'];
259292
if (HTMLMarqueeElement) {
260-
patchOnProperties(HTMLMarqueeElement.prototype, marqueeEventNames);
293+
patchFilteredProperties(HTMLMarqueeElement.prototype, marqueeEventNames, ignoreProperties);
261294
}
262295
}
263-
patchOnProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames);
296+
patchFilteredProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames, ignoreProperties);
264297
const XMLHttpRequestEventTarget = _global['XMLHttpRequestEventTarget'];
265298
if (XMLHttpRequestEventTarget) {
266-
patchOnProperties(
299+
patchFilteredProperties(
267300
XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype,
268-
XMLHttpRequestEventNames);
301+
XMLHttpRequestEventNames, ignoreProperties);
269302
}
270303
if (typeof IDBIndex !== 'undefined') {
271-
patchOnProperties(IDBIndex.prototype, IDBIndexEventNames);
272-
patchOnProperties(IDBRequest.prototype, IDBIndexEventNames);
273-
patchOnProperties(IDBOpenDBRequest.prototype, IDBIndexEventNames);
274-
patchOnProperties(IDBDatabase.prototype, IDBIndexEventNames);
275-
patchOnProperties(IDBTransaction.prototype, IDBIndexEventNames);
276-
patchOnProperties(IDBCursor.prototype, IDBIndexEventNames);
304+
patchFilteredProperties(IDBIndex.prototype, IDBIndexEventNames, ignoreProperties);
305+
patchFilteredProperties(IDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
306+
patchFilteredProperties(IDBOpenDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
307+
patchFilteredProperties(IDBDatabase.prototype, IDBIndexEventNames, ignoreProperties);
308+
patchFilteredProperties(IDBTransaction.prototype, IDBIndexEventNames, ignoreProperties);
309+
patchFilteredProperties(IDBCursor.prototype, IDBIndexEventNames, ignoreProperties);
277310
}
278311
if (supportsWebSocket) {
279-
patchOnProperties(WebSocket.prototype, websocketEventNames);
312+
patchFilteredProperties(WebSocket.prototype, websocketEventNames, ignoreProperties);
280313
}
281314
} else {
282315
// Safari, Android browsers (Jelly Bean)

test/browser/browser.spec.ts

+23-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {patchFilteredProperties} from '../../lib/browser/property-descriptor';
910
import {isBrowser, isIEOrEdge, isMix, zoneSymbol} from '../../lib/common/utils';
1011
import {ifEnvSupports, ifEnvSupportsWithDone} from '../test-util';
1112

@@ -129,9 +130,6 @@ describe('Zone', function() {
129130
for (let prop in target) {
130131
if (prop.substr(0, 2) === 'on' && prop.length > 2) {
131132
target[prop] = noop;
132-
if (!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]) {
133-
console.log('prop', prop);
134-
}
135133
expect(target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]).toBeTruthy();
136134
target[prop] = null;
137135
expect(!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]).toBeTruthy();
@@ -175,6 +173,28 @@ describe('Zone', function() {
175173
checkIsOnPropertiesPatched(new XMLHttpRequest());
176174
});
177175

176+
it('should not patch ignored on properties', function() {
177+
const TestTarget: any = (window as any)['TestTarget'];
178+
patchFilteredProperties(
179+
TestTarget.prototype, ['prop1', 'prop2'], global['__Zone_ignore_on_properties']);
180+
const testTarget = new TestTarget();
181+
Zone.current.fork({name: 'test'}).run(() => {
182+
testTarget.onprop1 = function() {
183+
// onprop1 should not be patched
184+
expect(Zone.current.name).toEqual('test1');
185+
};
186+
testTarget.onprop2 = function() {
187+
// onprop2 should be patched
188+
expect(Zone.current.name).toEqual('test');
189+
};
190+
});
191+
192+
Zone.current.fork({name: 'test1'}).run(() => {
193+
testTarget.dispatchEvent('prop1');
194+
testTarget.dispatchEvent('prop2');
195+
});
196+
});
197+
178198
it('window onclick should be in zone',
179199
ifEnvSupports(canPatchOnProperty(window, 'onmousedown'), function() {
180200
zone.run(function() {

test/test_fake_polyfill.ts

+38
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,42 @@
2626
};
2727

2828
global.cordova = fakeCordova;
29+
30+
const TestTarget = global.TestTarget = function() {};
31+
32+
Object.defineProperties(TestTarget.prototype, {
33+
'onprop1': {configurable: true, writable: true},
34+
'onprop2': {configurable: true, writable: true},
35+
'addEventListener': {
36+
configurable: true,
37+
writable: true,
38+
value: function(eventName: string, callback: Function) {
39+
if (!this.events) {
40+
this.events = {};
41+
}
42+
this.events.eventName = {zone: Zone.current, callback: callback};
43+
}
44+
},
45+
'removeEventListener': {
46+
configurable: true,
47+
writable: true,
48+
value: function(eventName: string, callback: Function) {
49+
if (!this.events) {
50+
return;
51+
}
52+
this.events.eventName = null;
53+
}
54+
},
55+
'dispatchEvent': {
56+
configurable: true,
57+
writable: true,
58+
value: function(eventName: string) {
59+
const zoneCallback = this.events && this.events.eventName;
60+
zoneCallback && zoneCallback.zone.run(zoneCallback.callback, this, [{type: eventName}]);
61+
}
62+
}
63+
});
64+
65+
global['__Zone_ignore_on_properties'] =
66+
[{target: TestTarget.prototype, ignoreProperties: ['prop1']}];
2967
})(typeof window === 'object' && window || typeof self === 'object' && self || global);

0 commit comments

Comments
 (0)