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

Commit

Permalink
fix(xhr): fix #1072, should set scheduled flag to target (#1074)
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion authored and mhevery committed Jul 3, 2018
1 parent a86c6d5 commit 34c12e5
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 5 deletions.
2 changes: 1 addition & 1 deletion file-size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"path": "dist/zone.min.js",
"checkTarget": true,
"limit": 41500
"limit": 41600
}
]
}
20 changes: 16 additions & 4 deletions lib/browser/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
const XHR_LISTENER = zoneSymbol('xhrListener');
const XHR_SCHEDULED = zoneSymbol('xhrScheduled');
const XHR_URL = zoneSymbol('xhrURL');
const XHR_ERROR_BEFORE_SCHEDULED = zoneSymbol('xhrErrorBeforeScheduled');

interface XHROptions extends TaskData {
target: any;
Expand Down Expand Up @@ -126,9 +127,10 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
const SCHEDULED = 'scheduled';

function scheduleTask(task: Task) {
(XMLHttpRequest as any)[XHR_SCHEDULED] = false;
const data = <XHROptions>task.data;
const target = data.target;
target[XHR_SCHEDULED] = false;
target[XHR_ERROR_BEFORE_SCHEDULED] = false;
// remove existing event listener
const listener = target[XHR_LISTENER];
if (!oriAddListener) {
Expand All @@ -143,7 +145,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
if (target.readyState === target.DONE) {
// sometimes on some browsers XMLHttpRequest will fire onreadystatechange with
// readyState=4 multiple times, so we need to check task state here
if (!data.aborted && (XMLHttpRequest as any)[XHR_SCHEDULED] && task.state === SCHEDULED) {
if (!data.aborted && target[XHR_SCHEDULED] && task.state === SCHEDULED) {
// check whether the xhr has registered onload listener
// if that is the case, the task should invoke after all
// onload listeners finish.
Expand All @@ -167,6 +169,9 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
} else {
task.invoke();
}
} else if (!data.aborted && target[XHR_SCHEDULED] === false) {
// error occurs when xhr.send()
target[XHR_ERROR_BEFORE_SCHEDULED] = true;
}
}
};
Expand All @@ -177,7 +182,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
target[XHR_TASK] = task;
}
sendNative!.apply(target, data.args);
(XMLHttpRequest as any)[XHR_SCHEDULED] = true;
target[XHR_SCHEDULED] = true;
return task;
}

Expand Down Expand Up @@ -215,8 +220,15 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
} else {
const options: XHROptions =
{target: self, url: self[XHR_URL], isPeriodic: false, args: args, aborted: false};
return scheduleMacroTaskWithCurrentZone(
const task = scheduleMacroTaskWithCurrentZone(
XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask);
if (self && self[XHR_ERROR_BEFORE_SCHEDULED] === true && !options.aborted &&
task.state === SCHEDULED) {
// xhr request throw error when send
// we should invoke task instead of leaving a scheduled
// pending macroTask
task.invoke();
}
}
});

Expand Down
73 changes: 73 additions & 0 deletions test/browser/XMLHttpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,79 @@ describe('XMLHttpRequest', function() {
});
});

it('should trigger readystatechange if xhr request trigger cors error', (done) => {
const req = new XMLHttpRequest();
let err: any = null;
try {
req.open('get', 'file:///test', true);
} catch (err) {
// in IE, open will throw Access is denied error
done();
return;
}
req.addEventListener('readystatechange', function(ev) {
if (req.readyState === 4) {
const xhrScheduled = (req as any)['__zone_symbol__xhrScheduled'];
const task = (req as any)['__zone_symbol__xhrTask'];
if (xhrScheduled === false) {
expect(task.state).toEqual('scheduling');
setTimeout(() => {
if (err) {
expect(task.state).toEqual('unknown');
} else {
expect(task.state).toEqual('notScheduled');
}
done();
});
} else {
expect(task.state).toEqual('scheduled');
done();
}
}
});
try {
req.send();
} catch (error) {
err = error;
}
});

it('should invoke task if xhr request trigger cors error', (done) => {
const logs: string[] = [];
const zone = Zone.current.fork({
name: 'xhr',
onHasTask: (delegate: ZoneDelegate, curr: Zone, target: Zone, hasTask: HasTaskState) => {
logs.push(JSON.stringify(hasTask));
}
});
const req = new XMLHttpRequest();
try {
req.open('get', 'file:///test', true);
} catch (err) {
// in IE, open will throw Access is denied error
done();
return;
}
zone.run(() => {
let isError = false;
let timerId = null;
try {
timerId = (window as any)['__zone_symbol__setTimeout'](() => {
expect(logs).toEqual([
`{"microTask":false,"macroTask":true,"eventTask":false,"change":"macroTask"}`,
`{"microTask":false,"macroTask":false,"eventTask":false,"change":"macroTask"}`
]);
done();
}, 500);
req.send();
} catch (error) {
isError = true;
(window as any)['__zone_symbol__clearTimeout'](timerId);
done();
}
});
});

it('should not throw error when get XMLHttpRequest.prototype.onreadystatechange the first time',
function() {
const func = function() {
Expand Down

0 comments on commit 34c12e5

Please sign in to comment.