1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-20 15:18:08 +02:00

Added sendWithStream method in MessageHandler.

Adds functionality to accept Queueing Strategy in
sendWithStream method. Using Queueing Strategy we
can control the data that is enqueued into the sink,
and hence regulated the flow of chunks from worker
to main thread.

Adds capability in pull and cancel methods.
Adds ready and desiredSize property in streamSink.

Adds unit test for ReadableStream and sendWithStream.
This commit is contained in:
Mukul Mishra 2017-04-14 02:39:25 +05:30
parent edd7d89fe5
commit bbd9968f76
6 changed files with 653 additions and 40 deletions

View file

@ -19,6 +19,7 @@
"type1_parser_spec.js",
"ui_utils_spec.js",
"unicode_spec.js",
"util_spec.js"
"util_spec.js",
"util_stream_spec.js"
]
}

View file

@ -45,15 +45,15 @@ function initializePDFJS(callback) {
'pdfjs/display/global', 'pdfjs-test/unit/annotation_spec',
'pdfjs-test/unit/api_spec', 'pdfjs-test/unit/bidi_spec',
'pdfjs-test/unit/cff_parser_spec', 'pdfjs-test/unit/cmap_spec',
'pdfjs-test/unit/crypto_spec', 'pdfjs-test/unit/document_spec',
'pdfjs-test/unit/dom_utils_spec', 'pdfjs-test/unit/evaluator_spec',
'pdfjs-test/unit/fonts_spec', 'pdfjs-test/unit/function_spec',
'pdfjs-test/unit/metadata_spec', 'pdfjs-test/unit/murmurhash3_spec',
'pdfjs-test/unit/network_spec', 'pdfjs-test/unit/parser_spec',
'pdfjs-test/unit/primitives_spec', 'pdfjs-test/unit/stream_spec',
'pdfjs-test/unit/type1_parser_spec', 'pdfjs-test/unit/ui_utils_spec',
'pdfjs-test/unit/unicode_spec', 'pdfjs-test/unit/util_spec',
'pdfjs-test/unit/custom_spec'
'pdfjs-test/unit/crypto_spec', 'pdfjs-test/unit/custom_spec',
'pdfjs-test/unit/document_spec', 'pdfjs-test/unit/dom_utils_spec',
'pdfjs-test/unit/evaluator_spec', 'pdfjs-test/unit/fonts_spec',
'pdfjs-test/unit/function_spec', 'pdfjs-test/unit/metadata_spec',
'pdfjs-test/unit/murmurhash3_spec', 'pdfjs-test/unit/network_spec',
'pdfjs-test/unit/parser_spec', 'pdfjs-test/unit/primitives_spec',
'pdfjs-test/unit/stream_spec', 'pdfjs-test/unit/type1_parser_spec',
'pdfjs-test/unit/ui_utils_spec', 'pdfjs-test/unit/unicode_spec',
'pdfjs-test/unit/util_spec', 'pdfjs-test/unit/util_stream_spec'
].map(function (moduleName) {
return SystemJS.import(moduleName);
})).then(function (modules) {

View file

@ -20,46 +20,46 @@ import {
describe('util', function() {
describe('stringToPDFString', function() {
it('handles ISO Latin 1 strings', function() {
var str = '\x8Dstring\x8E';
let str = '\x8Dstring\x8E';
expect(stringToPDFString(str)).toEqual('\u201Cstring\u201D');
});
it('handles UTF-16BE strings', function() {
var str = '\xFE\xFF\x00\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67';
let str = '\xFE\xFF\x00\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67';
expect(stringToPDFString(str)).toEqual('string');
});
it('handles empty strings', function() {
// ISO Latin 1
var str1 = '';
let str1 = '';
expect(stringToPDFString(str1)).toEqual('');
// UTF-16BE
var str2 = '\xFE\xFF';
let str2 = '\xFE\xFF';
expect(stringToPDFString(str2)).toEqual('');
});
});
describe('removeNullCharacters', function() {
it('should not modify string without null characters', function() {
var str = 'string without null chars';
let str = 'string without null chars';
expect(removeNullCharacters(str)).toEqual('string without null chars');
});
it('should modify string with null characters', function() {
var str = 'string\x00With\x00Null\x00Chars';
let str = 'string\x00With\x00Null\x00Chars';
expect(removeNullCharacters(str)).toEqual('stringWithNullChars');
});
});
describe('ReadableStream', function() {
it('should return an Object', function () {
var readable = new ReadableStream();
let readable = new ReadableStream();
expect(typeof readable).toEqual('object');
});
it('should have property getReader', function () {
var readable = new ReadableStream();
let readable = new ReadableStream();
expect(typeof readable.getReader).toEqual('function');
});
});

View file

@ -0,0 +1,378 @@
/* Copyright 2017 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createPromiseCapability, MessageHandler } from '../../src/shared/util';
describe('util_stream', function () {
// Temporary fake port for sending messages between main and worker.
class FakePort {
constructor() {
this._listeners = [];
this._deferred = Promise.resolve(undefined);
}
postMessage(obj) {
let event = { data: obj, };
this._deferred.then(() => {
this._listeners.forEach(function (listener) {
listener.call(this, event);
}, this);
});
}
addEventListener(name, listener) {
this._listeners.push(listener);
}
removeEventListener(name, listener) {
let i = this._listeners.indexOf(listener);
this._listeners.splice(i, 1);
}
terminate() {
this._listeners = [];
}
}
// Sleep function to wait for sometime, similar to setTimeout but faster.
function sleep(ticks) {
return Promise.resolve().then(() => {
return (ticks && sleep(ticks - 1));
});
}
describe('sendWithStream', function () {
it('should return a ReadableStream', function () {
let port = new FakePort();
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler');
// Check if readable is an instance of ReadableStream.
expect(typeof readable).toEqual('object');
expect(typeof readable.getReader).toEqual('function');
});
it('should read using a reader', function (done) {
let log = '';
let port = new FakePort();
let messageHandler1 = new MessageHandler('main', 'worker', port);
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
sink.ready.then(() => {
sink.enqueue('hi');
return sink.ready;
}).then(() => {
sink.close();
});
return sleep(5);
});
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 1,
size() {
return 1;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('');
return reader.read();
}).then((result) => {
expect(log).toEqual('p');
expect(result.value).toEqual('hi');
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should not read any data when cancelled', function (done) {
let log = '';
let port = new FakePort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
log += '3';
sink.close();
}, () => {
log += '4';
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2');
return reader.cancel();
}).then(() => {
expect(log).toEqual('01p2c');
done();
});
});
it('should not read when errored', function(done) {
let log = '';
let port = new FakePort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
sink.ready.then(() => {
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += 'error';
sink.error('error');
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return reader.read();
}).then(() => {
}, (reason) => {
expect(reason).toEqual('error');
done();
});
});
it('should read data with blocking promise', function (done) {
let log = '';
let port = new FakePort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
sink.close();
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
// Sleep for 10ms, so that read() is not unblocking the ready promise.
// Chain all read() to stream in sequence.
sleep(10).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should read data with blocking promise and buffer whole data' +
' into stream', function (done) {
let log = '';
let port = new FakePort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
sink.close();
});
return sleep(10);
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 8,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('012');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('012p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('012p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should ignore any pull after close is called', function (done) {
let log = '';
let port = new FakePort();
let capability = createPromiseCapability();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
});
return capability.promise.then(() => {
sink.close();
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 10,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('01');
capability.resolve();
return capability.promise.then(() => {
return reader.read();
});
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
});
});