Skip to content

Commit

Permalink
fix(xhr): fix angular#1072, should set scheduled flag to target
Browse files Browse the repository at this point in the history
  • Loading branch information
JiaLiPassion committed Apr 11, 2018
1 parent 1ba8519 commit 44d6cf9
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 4 deletions.
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,8 +145,11 @@ 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) {
task.invoke();
} else if (!data.aborted && target[XHR_SCHEDULED] === false) {
// error occurs when xhr.send()
target[XHR_ERROR_BEFORE_SCHEDULED] = true;
}
}
};
Expand All @@ -155,7 +160,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 @@ -191,8 +196,15 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
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
69 changes: 69 additions & 0 deletions test/browser/XMLHttpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,75 @@ 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(() => {
try {
(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) {
done();
}
});
});

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

0 comments on commit 44d6cf9

Please sign in to comment.