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

fix(patch): fix #746, check desc get is null and only patch window.resize additionally #747

Merged
merged 7 commits into from
Apr 25, 2017
2 changes: 1 addition & 1 deletion lib/browser/property-descriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function propertyDescriptorPatch(_global: any) {
if (canPatchViaPropertyDescriptor()) {
// for browsers that we can patch the descriptor: Chrome & Firefox
if (isBrowser) {
patchOnProperties(window, eventNames);
patchOnProperties(window, eventNames.concat(['resize']));
patchOnProperties(Document.prototype, eventNames);
if (typeof(<any>window)['SVGElement'] !== 'undefined') {
patchOnProperties((<any>window)['SVGElement'].prototype, eventNames);
Expand Down
13 changes: 8 additions & 5 deletions lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,20 +126,23 @@ export function patchProperty(obj: any, prop: string) {
}
if (target.hasOwnProperty(_prop)) {
return target[_prop];
} else {
} else if (originalDescGet) {
// result will be null when use inline event attribute,
// such as <button onclick="func();">OK</button>
// because the onclick function is internal raw uncompiled handler
// the onclick will be evaluated when first time event was triggered or
// the property is accessed, https://github.com/angular/zone.js/issues/525
// so we should use original native get to retrieve the handler
let value = originalDescGet.apply(this);
value = desc.set.apply(this, [value]);
if (typeof target['removeAttribute'] === 'function') {
target.removeAttribute(prop);
if (value) {
desc.set.apply(this, [value]);
if (typeof target['removeAttribute'] === 'function') {
target.removeAttribute(prop);
}
return value;
}
return value;
}
return null;
};

Object.defineProperty(obj, prop, desc);
Expand Down
5 changes: 5 additions & 0 deletions test/browser/XMLHttpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ describe('XMLHttpRequest', function() {
req.send();
});

it('should return null when access ontimeout first time without error', function() {
let req: XMLHttpRequest = new XMLHttpRequest();
expect(req.ontimeout).toBe(null);
});

const supportsOnProgress = function() {
return 'onprogress' in new XMLHttpRequest();
};
Expand Down
183 changes: 91 additions & 92 deletions test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@ function promiseUnhandleRejectionSupport() {
}

function canPatchOnProperty(obj: any, prop: string) {
if (!obj) {
return false;
}
const desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc || !desc.configurable) {
return false;
}
return true;
}
const func = function() {
if (!obj) {
return false;
}
const desc = Object.getOwnPropertyDescriptor(obj, prop);
if (!desc || !desc.configurable) {
return false;
}
return true;
};

(canPatchOnProperty as any).message = 'patchOnProperties';
(func as any).message = 'patchOnProperties';
return func;
}

let supportsPassive = false;
try {
Expand Down Expand Up @@ -84,95 +87,91 @@ describe('Zone', function() {
expect(confirmSpy).toHaveBeenCalledWith('confirmMsg');
});

describe('DOM onProperty hooks', ifEnvSupports(canPatchOnProperty, function() {
let mouseEvent = document.createEvent('Event');
let hookSpy: Spy, eventListenerSpy: Spy;
const zone = rootZone.fork({
name: 'spy',
onScheduleTask: (parentZoneDelegate: ZoneDelegate, currentZone: Zone,
targetZone: Zone, task: Task): any => {
hookSpy();
return parentZoneDelegate.scheduleTask(targetZone, task);
}
describe(
'DOM onProperty hooks',
ifEnvSupports(canPatchOnProperty(HTMLElement.prototype, 'onclick'), function() {
let mouseEvent = document.createEvent('Event');
let hookSpy: Spy, eventListenerSpy: Spy;
const zone = rootZone.fork({
name: 'spy',
onScheduleTask: (parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task): any => {
hookSpy();
return parentZoneDelegate.scheduleTask(targetZone, task);
}
});

beforeEach(function() {
mouseEvent.initEvent('mousedown', true, true);
hookSpy = jasmine.createSpy('hook');
eventListenerSpy = jasmine.createSpy('eventListener');
});

it('window onclick should be in zone',
ifEnvSupports(canPatchOnProperty(window, 'onmousedown'), function() {
zone.run(function() {
window.onmousedown = eventListenerSpy;
});

beforeEach(function() {
mouseEvent.initEvent('mousedown', true, true);
hookSpy = jasmine.createSpy('hook');
eventListenerSpy = jasmine.createSpy('eventListener');
window.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
window.removeEventListener('mousedown', eventListenerSpy);
}));

it('window onresize should be patched',
ifEnvSupports(canPatchOnProperty(window, 'onmousedown'), function() {
window.onresize = eventListenerSpy;
const innerResizeProp: any = (window as any)[zoneSymbol('_onresize')];
expect(innerResizeProp).toBeTruthy();
innerResizeProp();
expect(eventListenerSpy).toHaveBeenCalled();
window.removeEventListener('resize', eventListenerSpy);
}));

it('document onclick should be in zone',
ifEnvSupports(canPatchOnProperty(Document.prototype, 'onmousedown'), function() {
zone.run(function() {
document.onmousedown = eventListenerSpy;
});

it('window onclick should be in zone',
ifEnvSupports(
() => {
return canPatchOnProperty(window, 'onmousedown');
},
function() {
zone.run(function() {
window.onmousedown = eventListenerSpy;
});

window.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
window.removeEventListener('mousedown', eventListenerSpy);
}));

it('document onclick should be in zone',
ifEnvSupports(
() => {
return canPatchOnProperty(Document.prototype, 'onmousedown');
},
function() {
zone.run(function() {
document.onmousedown = eventListenerSpy;
});

document.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
document.removeEventListener('mousedown', eventListenerSpy);
}));

it('SVGElement onclick should be in zone',
ifEnvSupports(
() => {
return typeof SVGElement !== 'undefined' &&
canPatchOnProperty(SVGElement.prototype, 'onmousedown');
},
function() {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
document.body.appendChild(svg);
zone.run(function() {
svg.onmousedown = eventListenerSpy;
});

svg.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
svg.removeEventListener('mouse', eventListenerSpy);
document.body.removeChild(svg);
}));

it('get window onerror should not throw error',
ifEnvSupports(
() => {
return canPatchOnProperty(window, 'onerror');
},
function() {
const testFn = function() {
let onerror = window.onerror;
window.onerror = function() {};
onerror = window.onerror;
};
expect(testFn()).not.toThrow();
}));
document.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
document.removeEventListener('mousedown', eventListenerSpy);
}));

it('SVGElement onclick should be in zone',
ifEnvSupports(
canPatchOnProperty(SVGElement && SVGElement.prototype, 'onmousedown'), function() {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
document.body.appendChild(svg);
zone.run(function() {
svg.onmousedown = eventListenerSpy;
});

svg.dispatchEvent(mouseEvent);

expect(hookSpy).toHaveBeenCalled();
expect(eventListenerSpy).toHaveBeenCalled();
svg.removeEventListener('mouse', eventListenerSpy);
document.body.removeChild(svg);
}));

it('get window onerror should not throw error',
ifEnvSupports(canPatchOnProperty(window, 'onerror'), function() {
const testFn = function() {
let onerror = window.onerror;
window.onerror = function() {};
onerror = window.onerror;
};
expect(testFn).not.toThrow();
}));

}));

describe('eventListener hooks', function() {
let button: HTMLButtonElement;
let clickEvent: Event;
Expand Down