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

fix(xhr): fix #1072, should set scheduled flag to target #1074

Merged
merged 1 commit into from
Jul 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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