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

Commit

Permalink
feat(onProperty): fix #875, can disable patch specified onProperties (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
JiaLiPassion authored and mhevery committed Aug 23, 2017
1 parent f27ff14 commit a733688
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 24 deletions.
21 changes: 21 additions & 0 deletions MODULE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ Below is the full list of current support modules.
|handleUnhandledPromiseRejection|NodeJS handle unhandledPromiseRejection from ZoneAwarePromise|__Zone_disable_handleUnhandledPromiseRejection = true|
|crypto|NodeJS patch crypto function as macroTask|__Zone_disable_crypto = true|

- on_property

you can also disable specified on_property by setting `__Zone_ignore_on_properties`, for example,
if you want to disable `window.onmessage` and `HTMLElement.prototype.onclick` from zone.js patching,
you can do like this.

```
<script>
__Zone_ignore_on_properties = [
{
target: window,
ignoreProperties: ['message']
}, {
target: HTMLElement.prototype,
ignoreProperties: ['click']
}
];
</script>
<script src="../dist/zone.js"></script>
```

- Angular(2+)

Angular use zone.js to manage async operations and decide when to perform change detection, so in Angular,
Expand Down
1 change: 1 addition & 0 deletions karma-dist.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
module.exports = function (config) {
require('./karma-base.conf.js')(config);
config.files.push('build/test/wtf_mock.js');
config.files.push('build/test/test_fake_polyfill.js');
config.files.push('build/test/custom_error.js');
config.files.push('dist/zone.js');
config.files.push('dist/async-test.js');
Expand Down
75 changes: 54 additions & 21 deletions lib/browser/property-descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,53 +230,86 @@ export const eventNames = globalEventHandlersEventNames.concat(
webglEventNames, formEventNames, detailEventNames, documentEventNames, windowEventNames,
htmlElementEventNames, ieElementEventNames);

export interface IgnoreProperty {
target: any;
ignoreProperties: string[];
}

function filterProperties(
target: any, onProperties: string[], ignoreProperties: IgnoreProperty[]): string[] {
if (!ignoreProperties) {
return onProperties;
}

const tip: IgnoreProperty[] = ignoreProperties.filter(ip => ip.target === target);
if (!tip || tip.length === 0) {
return onProperties;
}

const targetIgnoreProperties: string[] = tip[0].ignoreProperties;
return onProperties.filter(op => targetIgnoreProperties.indexOf(op) === -1);
}

export function patchFilteredProperties(
target: any, onProperties: string[], ignoreProperties: IgnoreProperty[], prototype?: any) {
const filteredProperties: string[] = filterProperties(target, onProperties, ignoreProperties);
patchOnProperties(target, filteredProperties, prototype);
}

export function propertyDescriptorPatch(api: _ZonePrivate, _global: any) {
if (isNode && !isMix) {
return;
}

const supportsWebSocket = typeof WebSocket !== 'undefined';
if (canPatchViaPropertyDescriptor()) {
const ignoreProperties: IgnoreProperty[] = _global.__Zone_ignore_on_properties;
// for browsers that we can patch the descriptor: Chrome & Firefox
if (isBrowser) {
// in IE/Edge, onProp not exist in window object, but in WindowPrototype
// so we need to pass WindowPrototype to check onProp exist or not
patchOnProperties(window, eventNames.concat(['messageerror']), Object.getPrototypeOf(window));
patchOnProperties(Document.prototype, eventNames);
patchFilteredProperties(
window, eventNames.concat(['messageerror']), ignoreProperties,
Object.getPrototypeOf(window));
patchFilteredProperties(Document.prototype, eventNames, ignoreProperties);

if (typeof(<any>window)['SVGElement'] !== 'undefined') {
patchOnProperties((<any>window)['SVGElement'].prototype, eventNames);
patchFilteredProperties(
(<any>window)['SVGElement'].prototype, eventNames, ignoreProperties);
}
patchOnProperties(Element.prototype, eventNames);
patchOnProperties(HTMLElement.prototype, eventNames);
patchOnProperties(HTMLMediaElement.prototype, mediaElementEventNames);
patchOnProperties(HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames));
patchOnProperties(HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames));
patchOnProperties(HTMLFrameElement.prototype, frameEventNames);
patchOnProperties(HTMLIFrameElement.prototype, frameEventNames);
patchFilteredProperties(Element.prototype, eventNames, ignoreProperties);
patchFilteredProperties(HTMLElement.prototype, eventNames, ignoreProperties);
patchFilteredProperties(HTMLMediaElement.prototype, mediaElementEventNames, ignoreProperties);
patchFilteredProperties(
HTMLFrameSetElement.prototype, windowEventNames.concat(frameSetEventNames),
ignoreProperties);
patchFilteredProperties(
HTMLBodyElement.prototype, windowEventNames.concat(frameSetEventNames), ignoreProperties);
patchFilteredProperties(HTMLFrameElement.prototype, frameEventNames, ignoreProperties);
patchFilteredProperties(HTMLIFrameElement.prototype, frameEventNames, ignoreProperties);

const HTMLMarqueeElement = (window as any)['HTMLMarqueeElement'];
if (HTMLMarqueeElement) {
patchOnProperties(HTMLMarqueeElement.prototype, marqueeEventNames);
patchFilteredProperties(HTMLMarqueeElement.prototype, marqueeEventNames, ignoreProperties);
}
}
patchOnProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames);
patchFilteredProperties(XMLHttpRequest.prototype, XMLHttpRequestEventNames, ignoreProperties);
const XMLHttpRequestEventTarget = _global['XMLHttpRequestEventTarget'];
if (XMLHttpRequestEventTarget) {
patchOnProperties(
patchFilteredProperties(
XMLHttpRequestEventTarget && XMLHttpRequestEventTarget.prototype,
XMLHttpRequestEventNames);
XMLHttpRequestEventNames, ignoreProperties);
}
if (typeof IDBIndex !== 'undefined') {
patchOnProperties(IDBIndex.prototype, IDBIndexEventNames);
patchOnProperties(IDBRequest.prototype, IDBIndexEventNames);
patchOnProperties(IDBOpenDBRequest.prototype, IDBIndexEventNames);
patchOnProperties(IDBDatabase.prototype, IDBIndexEventNames);
patchOnProperties(IDBTransaction.prototype, IDBIndexEventNames);
patchOnProperties(IDBCursor.prototype, IDBIndexEventNames);
patchFilteredProperties(IDBIndex.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBOpenDBRequest.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBDatabase.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBTransaction.prototype, IDBIndexEventNames, ignoreProperties);
patchFilteredProperties(IDBCursor.prototype, IDBIndexEventNames, ignoreProperties);
}
if (supportsWebSocket) {
patchOnProperties(WebSocket.prototype, websocketEventNames);
patchFilteredProperties(WebSocket.prototype, websocketEventNames, ignoreProperties);
}
} else {
// Safari, Android browsers (Jelly Bean)
Expand Down
26 changes: 23 additions & 3 deletions test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

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

Expand Down Expand Up @@ -129,9 +130,6 @@ describe('Zone', function() {
for (let prop in target) {
if (prop.substr(0, 2) === 'on' && prop.length > 2) {
target[prop] = noop;
if (!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]) {
console.log('prop', prop);
}
expect(target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]).toBeTruthy();
target[prop] = null;
expect(!target[Zone.__symbol__('ON_PROPERTY' + prop.substr(2))]).toBeTruthy();
Expand Down Expand Up @@ -175,6 +173,28 @@ describe('Zone', function() {
checkIsOnPropertiesPatched(new XMLHttpRequest());
});

it('should not patch ignored on properties', function() {
const TestTarget: any = (window as any)['TestTarget'];
patchFilteredProperties(
TestTarget.prototype, ['prop1', 'prop2'], global['__Zone_ignore_on_properties']);
const testTarget = new TestTarget();
Zone.current.fork({name: 'test'}).run(() => {
testTarget.onprop1 = function() {
// onprop1 should not be patched
expect(Zone.current.name).toEqual('test1');
};
testTarget.onprop2 = function() {
// onprop2 should be patched
expect(Zone.current.name).toEqual('test');
};
});

Zone.current.fork({name: 'test1'}).run(() => {
testTarget.dispatchEvent('prop1');
testTarget.dispatchEvent('prop2');
});
});

it('window onclick should be in zone',
ifEnvSupports(canPatchOnProperty(window, 'onmousedown'), function() {
zone.run(function() {
Expand Down
38 changes: 38 additions & 0 deletions test/test_fake_polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,42 @@
};

global.cordova = fakeCordova;

const TestTarget = global.TestTarget = function() {};

Object.defineProperties(TestTarget.prototype, {
'onprop1': {configurable: true, writable: true},
'onprop2': {configurable: true, writable: true},
'addEventListener': {
configurable: true,
writable: true,
value: function(eventName: string, callback: Function) {
if (!this.events) {
this.events = {};
}
this.events.eventName = {zone: Zone.current, callback: callback};
}
},
'removeEventListener': {
configurable: true,
writable: true,
value: function(eventName: string, callback: Function) {
if (!this.events) {
return;
}
this.events.eventName = null;
}
},
'dispatchEvent': {
configurable: true,
writable: true,
value: function(eventName: string) {
const zoneCallback = this.events && this.events.eventName;
zoneCallback && zoneCallback.zone.run(zoneCallback.callback, this, [{type: eventName}]);
}
}
});

global['__Zone_ignore_on_properties'] =
[{target: TestTarget.prototype, ignoreProperties: ['prop1']}];
})(typeof window === 'object' && window || typeof self === 'object' && self || global);

0 comments on commit a733688

Please sign in to comment.