Merge pull request #3904 from yurydelendik/mem-redux
Reduces amount of memory allocated during worker operations
This commit is contained in:
commit
1f7bfc8cc7
@ -301,7 +301,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
|
|
||||||
PDFImage.buildImage(function(imageObj) {
|
PDFImage.buildImage(function(imageObj) {
|
||||||
var imgData = imageObj.getImageData();
|
var imgData = imageObj.getImageData();
|
||||||
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData]);
|
self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData],
|
||||||
|
null, [imgData.data.buffer]);
|
||||||
}, self.handler, self.xref, resources, image, inline);
|
}, self.handler, self.xref, resources, image, inline);
|
||||||
|
|
||||||
operatorList.addOp(OPS.paintImageXObject, args);
|
operatorList.addOp(OPS.paintImageXObject, args);
|
||||||
@ -1457,14 +1458,30 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return PartialEvaluator;
|
return PartialEvaluator;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var OperatorList = (function OperatorListClosure() {
|
var OperatorList = (function OperatorListClosure() {
|
||||||
var CHUNK_SIZE = 100;
|
var CHUNK_SIZE = 100;
|
||||||
|
|
||||||
function OperatorList(messageHandler, pageIndex) {
|
function getTransfers(queue) {
|
||||||
|
var transfers = [];
|
||||||
|
var fnArray = queue.fnArray, argsArray = queue.argsArray;
|
||||||
|
for (var i = 0, ii = queue.length; i < ii; i++) {
|
||||||
|
switch (fnArray[i]) {
|
||||||
|
case OPS.paintInlineImageXObject:
|
||||||
|
case OPS.paintInlineImageXObjectGroup:
|
||||||
|
case OPS.paintImageMaskXObject:
|
||||||
|
var arg = argsArray[i][0]; // first param in imgData
|
||||||
|
transfers.push(arg.data.buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transfers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function OperatorList(messageHandler, pageIndex) {
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
// When there isn't a message handler the fn array needs to be able to grow
|
// When there isn't a message handler the fn array needs to be able to grow
|
||||||
// since we can't flush the operators.
|
// since we can't flush the operators.
|
||||||
@ -1529,6 +1546,7 @@ var OperatorList = (function OperatorListClosure() {
|
|||||||
|
|
||||||
flush: function(lastChunk) {
|
flush: function(lastChunk) {
|
||||||
PartialEvaluator.optimizeQueue(this);
|
PartialEvaluator.optimizeQueue(this);
|
||||||
|
var transfers = getTransfers(this);
|
||||||
this.messageHandler.send('RenderPageChunk', {
|
this.messageHandler.send('RenderPageChunk', {
|
||||||
operatorList: {
|
operatorList: {
|
||||||
fnArray: this.fnArray,
|
fnArray: this.fnArray,
|
||||||
@ -1537,7 +1555,7 @@ var OperatorList = (function OperatorListClosure() {
|
|||||||
length: this.length
|
length: this.length
|
||||||
},
|
},
|
||||||
pageIndex: this.pageIndex
|
pageIndex: this.pageIndex
|
||||||
});
|
}, null, transfers);
|
||||||
this.dependencies = [];
|
this.dependencies = [];
|
||||||
this.fnIndex = 0;
|
this.fnIndex = 0;
|
||||||
this.argsArray = [];
|
this.argsArray = [];
|
||||||
@ -1546,6 +1564,7 @@ var OperatorList = (function OperatorListClosure() {
|
|||||||
|
|
||||||
return OperatorList;
|
return OperatorList;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var TextState = (function TextStateClosure() {
|
var TextState = (function TextStateClosure() {
|
||||||
function TextState() {
|
function TextState() {
|
||||||
this.fontSize = 0;
|
this.fontSize = 0;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* globals bytesToString, ColorSpace, Dict, EOF, error, info, Jbig2Image,
|
/* globals bytesToString, ColorSpace, Dict, EOF, error, info, Jbig2Image,
|
||||||
JpegImage, JpxImage, Lexer, Util */
|
JpegImage, JpxImage, Lexer, Util, PDFJS */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -832,7 +832,7 @@ var JpegStream = (function JpegStreamClosure() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
JpegStream.prototype.getIR = function JpegStream_getIR() {
|
JpegStream.prototype.getIR = function JpegStream_getIR() {
|
||||||
return bytesToString(this.bytes);
|
return PDFJS.createObjectURL(this.bytes, 'image/jpeg');
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Checks if the image can be decoded and displayed by the browser without any
|
* Checks if the image can be decoded and displayed by the browser without any
|
||||||
|
@ -173,6 +173,9 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
handler.send('test', false);
|
handler.send('test', false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// making sure postMessage transfers are working
|
||||||
|
var supportTransfers = data[0] === 255;
|
||||||
|
handler.postMessageTransfers = supportTransfers;
|
||||||
// check if the response property is supported by xhr
|
// check if the response property is supported by xhr
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
var responseExists = 'response' in xhr;
|
var responseExists = 'response' in xhr;
|
||||||
@ -186,7 +189,10 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
|
|||||||
handler.send('test', false);
|
handler.send('test', false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler.send('test', true);
|
handler.send('test', {
|
||||||
|
supportTypedArray: true,
|
||||||
|
supportTransfers: supportTransfers
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.on('GetDocRequest', function wphSetupDoc(data) {
|
handler.on('GetDocRequest', function wphSetupDoc(data) {
|
||||||
|
@ -85,6 +85,12 @@ PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ?
|
|||||||
*/
|
*/
|
||||||
PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
|
PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables transfer usage in postMessage for ArrayBuffers.
|
||||||
|
* @var {boolean}
|
||||||
|
*/
|
||||||
|
PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ?
|
||||||
|
true : PDFJS.postMessageTransfers;
|
||||||
/**
|
/**
|
||||||
* This is the main entry point for loading a PDF and interacting with it.
|
* This is the main entry point for loading a PDF and interacting with it.
|
||||||
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
|
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
|
||||||
@ -544,9 +550,13 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
var messageHandler = new MessageHandler('main', worker);
|
var messageHandler = new MessageHandler('main', worker);
|
||||||
this.messageHandler = messageHandler;
|
this.messageHandler = messageHandler;
|
||||||
|
|
||||||
messageHandler.on('test', function transportTest(supportTypedArray) {
|
messageHandler.on('test', function transportTest(data) {
|
||||||
|
var supportTypedArray = data && data.supportTypedArray;
|
||||||
if (supportTypedArray) {
|
if (supportTypedArray) {
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
|
if (!data.supportTransfers) {
|
||||||
|
PDFJS.postMessageTransfers = false;
|
||||||
|
}
|
||||||
this.setupMessageHandler(messageHandler);
|
this.setupMessageHandler(messageHandler);
|
||||||
workerInitializedPromise.resolve();
|
workerInitializedPromise.resolve();
|
||||||
} else {
|
} else {
|
||||||
@ -558,10 +568,16 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var testObj = new Uint8Array(1);
|
var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]);
|
||||||
// Some versions of Opera throw a DATA_CLONE_ERR on
|
// Some versions of Opera throw a DATA_CLONE_ERR on serializing the
|
||||||
// serializing the typed array.
|
// typed array. Also, checking if we can use transfers.
|
||||||
messageHandler.send('test', testObj);
|
try {
|
||||||
|
messageHandler.send('test', testObj, null, [testObj.buffer]);
|
||||||
|
} catch (ex) {
|
||||||
|
info('Cannot use postMessage transfers');
|
||||||
|
testObj[0] = 0;
|
||||||
|
messageHandler.send('test', testObj);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
info('The worker has been disabled.');
|
info('The worker has been disabled.');
|
||||||
@ -799,7 +815,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
messageHandler.on('JpegDecode', function(data, promise) {
|
messageHandler.on('JpegDecode', function(data, promise) {
|
||||||
var imageData = data[0];
|
var imageUrl = data[0];
|
||||||
var components = data[1];
|
var components = data[1];
|
||||||
if (components != 3 && components != 1)
|
if (components != 3 && components != 1)
|
||||||
error('Only 3 component or 1 component can be returned');
|
error('Only 3 component or 1 component can be returned');
|
||||||
@ -829,8 +845,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||||||
}
|
}
|
||||||
promise.resolve({ data: buf, width: width, height: height});
|
promise.resolve({ data: buf, width: width, height: height});
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
img.src = imageUrl;
|
||||||
img.src = src;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref */
|
/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -1100,10 +1100,38 @@ PDFJS.createBlob = function createBlob(data, contentType) {
|
|||||||
return bb.getBlob(contentType);
|
return bb.getBlob(contentType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PDFJS.createObjectURL = (function createObjectURLClosure() {
|
||||||
|
if (typeof URL !== 'undefined' && URL.createObjectURL) {
|
||||||
|
return function createObjectURL(data, contentType) {
|
||||||
|
var blob = PDFJS.createBlob(data, contentType);
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blob/createObjectURL is not available, falling back to data schema.
|
||||||
|
var digits =
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||||
|
|
||||||
|
return function createObjectURL(data, contentType) {
|
||||||
|
var buffer = 'data:' + contentType + ';base64,';
|
||||||
|
for (var i = 0, ii = data.length; i < ii; i += 3) {
|
||||||
|
var b1 = data[i] & 0xFF;
|
||||||
|
var b2 = data[i + 1] & 0xFF;
|
||||||
|
var b3 = data[i + 2] & 0xFF;
|
||||||
|
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
||||||
|
var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
|
||||||
|
var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
|
||||||
|
buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
function MessageHandler(name, comObj) {
|
function MessageHandler(name, comObj) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.comObj = comObj;
|
this.comObj = comObj;
|
||||||
this.callbackIndex = 1;
|
this.callbackIndex = 1;
|
||||||
|
this.postMessageTransfers = true;
|
||||||
var callbacks = this.callbacks = {};
|
var callbacks = this.callbacks = {};
|
||||||
var ah = this.actionHandler = {};
|
var ah = this.actionHandler = {};
|
||||||
|
|
||||||
@ -1170,8 +1198,9 @@ MessageHandler.prototype = {
|
|||||||
* @param {String} actionName Action to call.
|
* @param {String} actionName Action to call.
|
||||||
* @param {JSON} data JSON data to send.
|
* @param {JSON} data JSON data to send.
|
||||||
* @param {function} [callback] Optional callback that will handle a reply.
|
* @param {function} [callback] Optional callback that will handle a reply.
|
||||||
|
* @param {Array} [transfers] Optional list of transfers/ArrayBuffers
|
||||||
*/
|
*/
|
||||||
send: function messageHandlerSend(actionName, data, callback) {
|
send: function messageHandlerSend(actionName, data, callback, transfers) {
|
||||||
var message = {
|
var message = {
|
||||||
action: actionName,
|
action: actionName,
|
||||||
data: data
|
data: data
|
||||||
@ -1181,14 +1210,18 @@ MessageHandler.prototype = {
|
|||||||
this.callbacks[callbackId] = callback;
|
this.callbacks[callbackId] = callback;
|
||||||
message.callbackId = callbackId;
|
message.callbackId = callbackId;
|
||||||
}
|
}
|
||||||
this.comObj.postMessage(message);
|
if (transfers && this.postMessageTransfers) {
|
||||||
|
this.comObj.postMessage(message, transfers);
|
||||||
|
} else {
|
||||||
|
this.comObj.postMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadJpegStream(id, imageData, objs) {
|
function loadJpegStream(id, imageUrl, objs) {
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.onload = (function loadJpegStream_onloadClosure() {
|
img.onload = (function loadJpegStream_onloadClosure() {
|
||||||
objs.resolve(id, img);
|
objs.resolve(id, img);
|
||||||
});
|
});
|
||||||
img.src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
img.src = imageUrl;
|
||||||
}
|
}
|
||||||
|
@ -540,6 +540,39 @@ var tests = [
|
|||||||
impact: 'Important',
|
impact: 'Important',
|
||||||
area: 'Core'
|
area: 'Core'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Worker-transfers',
|
||||||
|
name: 'Worker can use transfers for postMessage',
|
||||||
|
run: function () {
|
||||||
|
if (typeof Worker == 'undefined')
|
||||||
|
return { output: 'Skipped', emulated: '' };
|
||||||
|
|
||||||
|
try {
|
||||||
|
var worker = new Worker('worker-stub.js');
|
||||||
|
|
||||||
|
var promise = new Promise();
|
||||||
|
var timeout = setTimeout(function () {
|
||||||
|
promise.resolve({ output: 'Failed', emulated: '?' });
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
worker.addEventListener('message', function (e) {
|
||||||
|
var data = e.data;
|
||||||
|
if (data.action == 'test-transfers' && data.result)
|
||||||
|
promise.resolve({ output: 'Success', emulated: '' });
|
||||||
|
else
|
||||||
|
promise.resolve({ output: 'Failed', emulated: 'Yes' });
|
||||||
|
}, false);
|
||||||
|
var testObj = new Uint8Array([255]);
|
||||||
|
worker.postMessage({action: 'test-transfers',
|
||||||
|
data: testObj}, [testObj.buffer]);
|
||||||
|
return promise;
|
||||||
|
} catch (e) {
|
||||||
|
return { output: 'Failed', emulated: 'Yes' };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
impact: 'Normal',
|
||||||
|
area: 'Core'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Worker-xhr-response',
|
id: 'Worker-xhr-response',
|
||||||
name: 'XMLHttpRequest supports the reponse property in web workers',
|
name: 'XMLHttpRequest supports the reponse property in web workers',
|
||||||
|
@ -21,6 +21,9 @@ onmessage = function (e) {
|
|||||||
case 'test':
|
case 'test':
|
||||||
postMessage({action: 'test', result: data.data instanceof Uint8Array});
|
postMessage({action: 'test', result: data.data instanceof Uint8Array});
|
||||||
break;
|
break;
|
||||||
|
case 'test-transfers':
|
||||||
|
postMessage({action: 'test-transfers', result: data.data[0] === 255});
|
||||||
|
break;
|
||||||
case 'xhr':
|
case 'xhr':
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
var responseExists = 'response' in xhr;
|
var responseExists = 'response' in xhr;
|
||||||
|
Loading…
Reference in New Issue
Block a user